Save & Retrieve TableViewCell checkmark using NSUserDefaults in Swift
First of all create a class
Item
as data source with aname
andselected
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 keyselectedCells
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 thatreadDefaults()
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 accordinglyfunc 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 cellfunc 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 *mutArrNamesCheckList
which 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
How to Loop Through an Array from the Second Element in Elegant Way Using Swift
Many Ways of Defining a Swift Dictionary
Perform Segue from Another Class with Helper Function
How to Convert Anyclass to a Specific Class and Init It Dynamically in Swift
How to Change the Current Day's Hours and Minutes in Swift
Scenedidload Being Called Twice
Call Parent Constructor When Init a Subclass in Swift
How to Show a Window Without Stealing Focus on MACos
Xcode 6, Swift - Read Standard Input (Console) to String
Label Disappear When Changing Font Size to 25 in Swift
Callkit - How to Bring the Cxcallcontroller to the Front
Swift Test Give Error "Undefined Symbols for Architecture X86_64"
How to Disable User Interaction on Swiftui View
Swift: Floating Plus Button Over Tableview Using the Storyboard
Skaction Completion Handlers; Usage in Swift
Difference Between Type Method and Type Instance Method, etc