Save & Retrieve Tableviewcell Checkmark Using Nsuserdefaults in Swift

Save & Retrieve TableViewCell checkmark using NSUserDefaults in Swift


  • First of all create a class Item as data source with a name and selected property.

    class Item {
    let name : String
    var selected = false

    init(name: String) {
    self.name = name
    }
    }
  • Declare the data source array

    var myItems = [Item]()
  • Create the items this way

    let item = Item(name:"Foo") // your former string value, `selected` is false by default.
    myItems.append(item)
  • In applicationDidFinishLaunching register an empty string array as default value for key selectedCells

    let defaults = NSUserDefaults.standardUserDefaults()
    let defaultValues = ["selectedCells" : [String]()]
    defaults.registerDefaults(defaultValues)
  • To read all selected cells from user defaults get the string array and set the property selected of all corresponding items to true. Then reload the table view. The forced unwrapping is safe because the key/value is pre-registered and always non-optional. Important: Make sure that readDefaults() is always called after registering the default values.

    func readDefaults()
    {
    let defaults = NSUserDefaults.standardUserDefaults()
    let selectedItems = defaults.stringArrayForKey("selectedCells")!
    for item in myItems {
    item.selected = selectedItems.contains(item.name)
    }
    tableView.reloadData()
    }
  • In cellForRowAtIndexPath set both properties accordingly

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("MyCell", forIndexPath: indexPath)
    let item = myItems[indexPath.row]
    cell.textLabel!.text = item.name
    cell.accessoryType = item.selected ? .Checkmark : .None
    cell.selectionStyle = .None
    cell.tintColor = UIColor.greenColor()

    return cell
    }
  • To save the data filter all items whose selected property is true, map it to the names and save the array.

    func saveDefaults() {
    let selectedCells = myItems.filter { $0.selected }.map { $0.name }
    let defaults = NSUserDefaults.standardUserDefaults()
    defaults.setObject(selectedCells, forKey:"selectedCells")
    }
  • Now you should change the model in didSelectRowAtIndexPath and reload the row. This is much more efficient (and recommended) than manipulating the cell

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let item = myItems[indexPath.row]
    item.selected = true
    tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
    }

How to save tableView cell's checkmark after reload view use NSUserDefaults?

It can be easy to store and retrieve checked cell(s) information with NSUserDefaults.

NSUserDefaults can store the data interms of Key / Value pair. Here in your case value could be boolean and key could combination of NSString & indexpath.row from UITableView.

You can make a functions like,

- (NSString *)getKeyForIndex:(int)index
{
return [NSString stringWithFormat:@"KEY%d",index];
}

- (BOOL) getCheckedForIndex:(int)index
{
if([[[NSUserDefaults standardUserDefaults] valueForKey:[self getKeyForIndex:index]] boolValue]==YES)
{
return YES;
}
else
{
return NO;
}
}

- (void) checkedCellAtIndex:(int)index
{
BOOL boolChecked = [self getCheckedForIndex:index];

[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:!boolChecked] forKey:[self getKeyForIndex:index]];
[[NSUserDefaults standardUserDefaults] synchronize];
}


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


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Here, you can check for previously checked cells like

static NSString *subviewCells = @"Cells";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:subviewCells];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:subviewCells];
}

cell.textLabel.text = [_array objectAtIndex:indexPath.row];

if([self getCheckedForIndex:indexPath.row]==YES)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}

return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];

//Use checkedCellAtIndex for check or uncheck cell
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

[self checkedCellAtIndex:indexPath.row];

if([self getCheckedForIndex:indexPath.row]==YES)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
}

P.S. Please, note that, this method will only useful and suggested if order of data from your NSArray will always in same order. Other then NSUserDefaults you've options like SQLite Database or plist file or any other option! Thanks :)

How to save checkmark state in a UITableView for Core Data App in Swift

You need to track the completion status in your data model (The Core Data objects) not the cells. Cells are re-used as you have found, also cells aren't persisted, so the checkmark state isn't saved.

I would use something like this:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

let taskObject=self.listItems[indexPath.row]
var taskStatus = taskObject.valueForKey("completed") as? Bool ?? false

taskObject.setValue(!taskStatus,forKey:"completed") // Toggle completed status

do {
try managedContext.save()
} catch let error as NSError {
print("Cannot save object: \(error), \(error.localizedDescription)")
}

tableView.deselectRowAtIndexPath(indexPath,animated:false)

tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
}

Then, in your cellForRowAtIndexPath you can render the row appropriately:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCellWithIdentifier("listCell") as! UITableViewCell
let task = listItems[indexPath.row]
let taskStatus = task.valueForKey("completed") as! Bool
cell.textLabel?.text = task.valueForKey("task") as? String

var accessoryType=UITableViewCellAccessoryType.None
var tintColor = UIColor.clearColor()

if (taskStatus) {
accessoryType=UITableViewCellAccessoryType.Checkmark
tintColor = UIColor.greenColor()
}

cell.accessoryType=accessoryType
cell.tintColor = tintColor

return cell
}

Save UITableViewCell Checkmark Selection in Swift

Yep, you answered your own question here. You need to store lastSelectedIndexPath some how (ie to whatever you're using for persistence). A simple way (read: not the best way) to do this if you don't have any sort of database set up already is to store it using NSUserDefaults.

How to store, retrieve check or unchecked cells

Solution can be tricky :

Maintain one array same to tableview datasource array.

Say for example u have NSMutableArray *mutArrNames which tableview datasource. Now NSMutableArray *mutArrNamesCheckListwhich shows which one is checked or not;

Intially nothing is selected so add 0 to uncheck one:

for(int i =0; i<[mutArrNames count];i++)
{
[mutArrNamesCheckList addObject:@"0"];
}

Now use like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

if(section == 4) {
//check from mutArrNamesCheckList's list if cell is check one or not
if([[mutArrNamesCheckList objectAtIndex:indexPath.row] intValue] == 1) //checked one
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else //unchecked one
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
}
return cell;
}

Here it can be used according to your requirement like allow mutiple selection or single selection.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//get cell object
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

if(section == 4)
{
if([[mutArrNamesCheckList objectAtIndex:indexPath.row] intValue] == 1) //checked one
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
//replace 0 with 1 as check one
[mutArrNamesCheckList replaceObjectAtIndex:indexPath.row withObject:@"0"];
}
else //unchecked one
{
cell.accessoryType = UITableViewCellAccessoryNone;
//replace 1 with 0 as uncheck one
[mutArrNamesCheckList replaceObjectAtIndex:indexPath.row withObject:@"1"];
}
}
}

For selection and unselection of all cell add this in button's action method. Use this :

//remove all as new objects will be added
[mutArrNamesCheckList removeAllObjects];
if(!btnCheckAll.selected)//selected property used - default is no so all not selected
{
//make all selected
for(int i =0; i<[mutArrNames count];i++)
{
[mutArrNamesCheckList addObject:@"1"];
}
//change buttons image here
}
else
{
//make all unselected
for(int i =0; i<[mutArrNames count];i++)
{
[mutArrNamesCheckList addObject:@"0"];
}
//change buttons image here
}

//tableView reload
[yourtableView reloadData];

btnCheckAll.selected = !btnCheckAll.selected;

Add mutArrNamesCheckList in AppDelegate to be visible in whole Application.

So remove iteration we done on top of above answer in our viewcontroller as we didn't wanted to use gobally

EDIT : added toggling in didSelectRowAtIndexPath + clicked select all button, all cell is checked else unchecked

Error when trying to save to NSUserdefaults

I don't know why you are saving all the NSIndexPath's for the cells selected previously when according your previous question and this one, you need to save only the index of the cell selected and the value of the total you had when you go to another controller. You can do it in the following way:

var frequency = [
Item(name:"Every week",selected: false, amount: 30),
Item(name:"Every 2 weeks",selected: false, amount: 30),
Item(name:"Every 4 weeks",selected: false, amount: 30),
Item(name:"Once",selected: false, amount: 40),
Item(name:"End of tenancy cleaning", selected: false, amount: 44)
]

var indexPathForCellSelected: NSIndexPath?
var total = 0

let indexPathKey = "indexPathForCellSelectedNinethViewControllerKey"
let totalKey = "totalNinethViewControllerKey"

override func viewDidLoad() {
super.viewDidLoad()

loadData()
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = frequency[indexPath.row].name
if let _ = indexPathForCellSelected where indexPath == indexPathForCellSelected {
cell.accessoryType = .Checkmark
}
return cell
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

if !frequency[indexPath.row].selected {

// this avoid set initial value for the first time
if let index = indexPathForCellSelected {
// clear the previous cell
frequency[index.row].selected = false
tableView.cellForRowAtIndexPath(index)?.accessoryType = .None
self.total -= frequency[index.row].amount
}

// mark the new one
frequency[indexPath.row].selected = true
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .Checkmark

indexPathForCellSelected = indexPath
self.total += frequency[indexPath.row].amount

saveData()
}
}

private func saveData() {

// save the index for the cell selected in NSUserDefaults
NSUserDefaults.standardUserDefaults().setInteger(indexPathForCellSelected!.row, forKey: indexPathKey)

// save total in NSUserDefaults
NSUserDefaults.standardUserDefaults().setInteger(total, forKey: totalKey)
}

private func loadData() {

// get the values from NSUserDefaults if exist
if let indexValue = NSUserDefaults.standardUserDefaults().valueForKey(indexPathKey), totalValue = NSUserDefaults.standardUserDefaults().valueForKey(totalKey) {
indexPathForCellSelected = NSIndexPath(forRow: indexValue as! Int, inSection: 0)
total = totalValue as! Int
}
}

I hope this help you.



Related Topics



Leave a reply



Submit