P46: Saving an NSCoding object graph
- Make a class that conforms to the NSCoding protocol:
#import <Foundation/Foundation.h>
@interface NSCoderObject : NSObject <NSCoding> {
NSString *string;
}
-(id)initWithString: (NSString*) str;
@end
It’s .m file:
#import “NSCoderObject.h”
static NSString *STRING_KEY=@”string”;
@implementation NSCoderObject
-(id)initWithString: (NSString*) str
{
self = [super init];
string = str;
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
string = [[aDecoder decodeObjectForKey:STRING_KEY] retain];
return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:string forKey:STRING_KEY];
}
-(NSString*)getString {
return string;
}
@end
- Then you can put this in an array, etc, and use the NSKeyArchiver to store it. And NSKeyUnarchiver to fetch it:
[NSKeyedArchiver archiveRootObject:yourArrayOfThoseObjects toFile:[self filePath:@”myplist.plist”]];
It returns a boolean if it succeeded or not. (See the filePath method from the last post)
P45: Saving a plist in the documents folder
- Because you can’t save a plist in the Bundle, you must save it in the documents directory. This gets a location in such.
-(NSString*) filePath: (NSString*) file {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString * path = [documentsDirectory stringByAppendingPathComponent:file];
return path;
}
- Then you can save a file in that location.
[anObject writeToFile:[self filePath:@”filey.plist” atomically:true];
The ‘anObject’ must conform to the NSCoding protocol. NSArray, NSDictionary etc do.
- You can see if a file exists in your documents folder via:
-(BOOL) doesExist:(NSString *) str {
return [[NSFileManager defaultManager] fileExistsAtPath:str];
}
P44: Date to string
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateFormat:YOUR_DATE_FORMAT];
self.mCheckoutDateLabel.text = [formatter stringFromDate:yourDate];
The date format can be found here: http://unicode.org/reports/tr35/tr35-10.html#Date_Format_Patterns
P43: Getting the current location
First make a CLLocationManager, set it as yourself as the delegate, set the accuracy and the distance filter (when to ignore the cache as get a new location):
CLLocationManager *clm = [[CLLocationManager alloc] init];
clm.delegate = self;
clm.desiredAccuracy = kCLLocationAccuracyKilometer;
clm.distanceFilter=500;
[clm startUpdatingLocation];
Now you need delegate methods to capture the result via <CLLocationManagerDelegate>
-(void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
//newLocation.coordinate.latitude;
//newLocation.coordinate.longitude;
}
-(void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error
{
//error
}
P42: Custom UITableViewCell
Create ‘UIViewController’ in Xcode, but make it superclass UITableViewCell.
In the XIB, make sure the base view is pointing to the .m file above. Link the File Owner’s backgroundView to the main view in the XIB. Now link the elements in the view to the file owner should you need to.
Make its init method this:
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
[[[NSBundle mainBundle] loadNibNamed:@”YOURNIBHERE” owner:nil options:nil] objectAtIndex:0];
return self;
}
Notice we’re initing it like normal, but loading and returning the XIB.
Your cellForRowAtIndexPath: can now be:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
YOURTABLECELL *cell = (YOURTABLECELL *)[tableView dequeueReusableCellWithIdentifier:@”YOURTABLECELLSTRING”];
if (cell == nil) {
cell = [[YOURTABLECELL alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@”YOURTABLECELLSTRING”];
}
cell.ONEOFITSLABELS.text = @”sometextetc”;
return cell;
}
P41: Adding a plist to your project
Add a new property list to your project. It’s a dictionary by default, but you can change it to an array, or just have a dictionary with one key which is the array.
Then to load that:
NSString *path = [[NSBundle mainBundle] pathForResource:@”myplist” ofType:@”plist”];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
P40: Deleting or adding cells using the inbuilt UI
Once you’re in editing mode (see P39), and you’ve set the editing style to delete or add, then this method is called when you click on either add or delete item
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.array removeObjectAtIndex:indexPath.row];
self.numOfRows—;
[self.table reloadData];
}
In this case I’m just deleting the row. If you easily add something here if the editingStyle is add.
Once in editing mode, self.isEditing is true.
You can also perform clicks in this mode if you set the table view to allow clicks in properties.
P39: Reordering cells in a table view
Make sure your tableview’s delegate is of UITableViewController, else you’ll need to implement setEditing: yourself.
For each cell you want a reorder control, give it a cell.showsReorderControl=YES; when you’re creating the cell in cellForRowAtIndexPath:.
Implement
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
//change the data in your array, or whatever, here
}
Either issue [self setEditing:YES animated:YES]; at some point, or do self.navigationItem.rightBarButtonItem = self.editButtonItem; if you’re using a navigation controller to give yourself an editing button.
Choose the style of your editing cell. Delete, add or none.
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}
Issue this if you want no indent when editing.
-(BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath {
return NO;
}
This will allow you to disallow a move to the first row in your table.
-(NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
if(proposedDestinationIndexPath.row==0) {
return sourceIndexPath;
} else {
return proposedDestinationIndexPath;
}
}
P38: Autocomplete in a TextField
First create a table in your view. Make it hidden. Put it right at the bottom, but within the main ‘View’ if you want.
Then make the delegate and datasource methods for that table in your controller, set the delegate and datasource of your table appropriately.
You’ll need the tableView:numberOfRowsInSection: and tableView:cellForRowAtIndexPath: as a minmum of table delegate/datasources to implement.
On your UITextView, link its editing event to a method in your controller file. From here we’ll pull up our table and fill its data with the autocomplete stuff:
CGRect rect = self.mAutocompleteTable.frame;
CGRect newRect = CGRectMake(rect.origin.x, YOURTEXTFIELD.origin.y+ YOURTEXTFIELD.size.height+5, rect.size.width, rect.size.height);
self.mAutocompleteTable.frame = newRect;
self.mAutocompleteTable.hidden=NO;
I’m first getting the frame of the autocomplete table. Then Then making a new rect with those values, but the y value of the UITextField box. Then I give that new rect to the auto complete table and show it.
Then you must fill the table with whatever new data you’ve grabbed from where ever. Then reload the table’s data source.
And voila. An autocomplete table. You’ll now need to implement the tableView method to listen on a click and put that value in your edit box.
P37: Inserting / deleting a table section
If you increment the number of sections to be passed in
numberOfSectionsInTableView: and then create a NSIndexSet with position you want the new section added in, you can add a new section.
self.numOfSections+=1;
NSIndexSet * set = [NSIndexSet indexSetWithIndex:3];
[self.table insertSections:set withRowAnimation:UITableViewRowAnimationTop];
Deleting is similar, except the NSIndexSet is the section you want to delete, now where to insert it as before.
self.numOfSections+=-1;
NSIndexSet * set = [NSIndexSet indexSetWithIndex:2];
[self.table deleteSections:set withRowAnimation:UITableViewRowAnimationTop];