Question

Background: I want to create an app that lists some books that someone might be collecting which they can use as a checklist. I want to get a test working properly before I launch into it completely and I've managed to get the following working so far.

Using a plist, UITableView and a UITableCell I can populate a table. The cell consists of a thumbnail, the book title and its second-hand price. There's also a 'checkbox' button which the user can toggle if they've already got the book or not. What I want to do is save the state of the checkbox toggle on exit. Maybe store a value into the .plist (which I've copied over to the Documents directory so it's editable if necessary)? Or is there another way? I could do it using NSUserDefaults, but how could I get that to relate to each cell (book)?

Screenshot of the test app in action (excuse the rudimentary graphics for now):

enter image description here

And here's the cell layout and associated .h file:

enter image description here

Here's the code for the main ViewController .m file:

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Find out the path of books_star.plist

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory =  [paths objectAtIndex:0];

    // Load the file content and read the data into arrays

    NSString *myListPath = [documentsDirectory stringByAppendingPathComponent:@"books_star.plist"];
    tableData = [[NSArray alloc]initWithContentsOfFile:myListPath];
    NSLog(@"%@",tableData);
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [tableData count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *books_starTableIdentifier = @"booksCell";

    booksCell *cell = (booksCell *)[tableView dequeueReusableCellWithIdentifier:books_starTableIdentifier];
    if (cell == nil)

    {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"booksCell" owner:self options:nil];
        cell = [nib objectAtIndex:0];
    }

    cell.titleLabel.text = [[tableData objectAtIndex:indexPath.row]objectForKey:@"title"];
    cell.thumbnailImageView.image = [UIImage imageNamed: [[tableData objectAtIndex:indexPath.row]objectForKey:@"thumbnail"]];
    cell.priceLabel.text = [[tableData objectAtIndex:indexPath.row]objectForKey:@"price"];

    return cell;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 96;
}

@end

And here's the code for the Cell .m file:

#import "booksCell.h"

@implementation booksCell

@synthesize titleLabel = _titleLabel;
@synthesize priceLabel = _priceLabel;
@synthesize thumbnailImageView = _thumbnailImageView;
@synthesize checkBoxButton;


- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {}
    return self;}


- (void) awakeFromNib {

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    checked = [defaults boolForKey:@"boxIsChecked"];
    [self checkTheBox];
}

- (IBAction)checkButton:(id)sender {

        if (!checked) {
            [checkBoxButton setImage:[UIImage imageNamed:@"check_on.png"] forState:UIControlStateNormal];
            checked = YES;

            NSMutableDictionary *plist = [NSMutableDictionary dictionaryWithContentsOfFile:@"books_star.plist"];
            [plist setObject:[NSNumber numberWithBool:YES] forKey:@"check"];
            [plist writeToFile:@"books_star.plist" atomically:YES];
        }

        else if (checked) {
            [checkBoxButton setImage:[UIImage imageNamed:@"check_off.png"] forState:UIControlStateNormal];
            checked = NO;

        }
        }

- (void) checkTheBox {
        if (!checked) {
            [checkBoxButton setImage:[UIImage imageNamed:@"check_off.png"] forState:UIControlStateNormal];
        }

        else if (checked) {
            [checkBoxButton setImage:[UIImage imageNamed:@"checkBox_on.png"] forState:UIControlStateNormal];}
        }


    - (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}


@end

And finally here's the .plist file:

enter image description here

Was it helpful?

Solution

You should change the way how you keep your data in plist.

It should be like that:

enter image description here

For table view you select objectAtIndex of current [indexPath row]. You make you UITableViewCell configurable from those "book NSDictionary" - you can add to those objects "checked" property if necessary and easily modify it.

It is much better approach if you want to delete / edit / create objects in your system. With that approach you don't have a problem of missing indexes for selected objects etc.

Once you have NSDictionary in your UITableViewCell you can add that property like that:

[dict setValue:[NSNumber numberWithBool:YES] forKey:@"checked"];

To get value:

[[dict valueForKey:@"checked"] boolValue];

OTHER TIPS

You may want to look at using Core Data going forwards, but your plist will work. I would recommend a number of changes to the plist though:

  1. I'd make the root an array (this is a list of books)
  2. I'd make each item a dictionary (with title, price and thumbnail)

Now, all of your data for each book is in the same container. But, more importantly, you have the option to add to that container - so you have somewhere to store the selected status.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top