I have an array of dictionaries in NSUserDefaults and those values are used to display data in a table view in another tab on the tab bar. It works ok if I add a new value to my NSUserDefaults array and close the app then go back into it and go the table view tab. The problem I'm having is when I add a new object to the user defaults array then go to the table view tab without leaving the app. When I do this, the table view does not show the just added values.
Here is where I add the dictionaries to NSUserDefaults:
- (void)addFavorite {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *favoritesLoaded = [defaults objectForKey:#"favorites"];
NSDictionary *fav = [NSDictionary dictionaryWithObjectsAndKeys:
self.postTitle, #"title",
self.postUrlString, #"url",
nil];
NSMutableArray *favorites = [NSMutableArray array];
if (favoritesLoaded) {
favorites = [[NSMutableArray alloc] initWithArray:favoritesLoaded];
} else {
favorites = [[NSMutableArray alloc] init];
}
[favorites insertObject:fav atIndex:0];
[defaults setObject:favorites forKey:#"favorites"];
[defaults synchronize];
}
In my table view:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
[self.tableView setDataSource:self];
[self.tableView setDelegate:self];
self.tableView.backgroundColor = [UIColor blackColor];
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"favorites"]) {
self.redditPosts = [[NSUserDefaults standardUserDefaults] objectForKey:#"favorites"];
}
else {
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
NSDictionary *post = [self.redditPosts objectAtIndex:[indexPath row]];
if (cell == nil) {
cell = [[CustomCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CustomCell"];
}
if (cell) {
cell.textLabel.text = [post objectForKey:#"title"];
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor whiteColor];
}
return cell;
}
just after inserting your new content you should reload the tableView:
[self.tableView reloadData];
if this can take a while you can use the following so it won't freeze-up your app:
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
Related
I've created an UITableView with cells and i am trying to add a person's phone and name information into a cell via some functions. The problem is when i try to add a person's info, it is being added into my data array but, i cannot see the new info on UITableView cell. I tried to use reloadData function to reload UITableView, it was no use.
I have arrays for people's numbers and names. Also, i have a button which adds a person via ABPeoplePickerNavigationController.
addedContacts.h :
#import <UIKit/UIKit.h>
#import AddressBook;
#import AddressBookUI;
#interface addedContacts : UIViewController <UITextFieldDelegate, ABPeoplePickerNavigationControllerDelegate, UITableViewDelegate,
UITableViewDataSource>{
NSMutableArray* people;
NSMutableArray* numbers;
IBOutlet UITableView *tableview;
}
- (IBAction)addPerson:(id)sender;
#end
I use commitEditingStyle function to delete a person's information from the tableview. In addition, I keep people's information in defaults of application.
addedContacts.m :
#import "addedContacts.h"
#implementation addedContacts
-(void)viewDidLoad{
[super viewDidLoad];
}
- (IBAction)addPerson:(id)sender {
ABPeoplePickerNavigationController* picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentViewController:picker animated:YES completion:nil];
}
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
[self dismissViewControllerAnimated:YES completion:nil];
}
-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
numbers = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:#"numbers"]];
people = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:#"people"]];
if(numbers.count == 0 && numbers.count == 0){
numbers = [[NSMutableArray alloc] init];
people = [[NSMutableArray alloc] init];
}
NSString *contactName = CFBridgingRelease(ABRecordCopyCompositeName(person));
NSString* nameField = [NSString stringWithFormat:#"%#", contactName ? contactName : #"No Name"];
ABMultiValueRef phoneRecord = ABRecordCopyValue(person, kABPersonPhoneProperty);
CFStringRef phoneNumber = ABMultiValueCopyValueAtIndex(phoneRecord, 0);
NSString* phoneField = (__bridge_transfer NSString *)phoneNumber;
CFRelease(phoneRecord);
[people addObject:nameField];
[numbers addObject:phoneField];
[defaults setObject:people forKey:#"people"];;
[defaults setObject:numbers forKey:#"numbers"];
[self dismissViewControllerAnimated:YES completion:nil];
return NO;
}
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// Remove the row from data model
[people removeObjectAtIndex:indexPath.row];
[numbers removeObjectAtIndex:indexPath.row];
// Request table view to reload
[tableView reloadData];
}
-(NSInteger) numberOfSectionsInTableView: (UITableView*) tableView{
return 1;
}
-(NSInteger) tableView:(UITableView*) tableView numberOfRowsInSection:(NSInteger)section{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
numbers = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:#"numbers"]];
return [numbers count];
}
-(UITableViewCell*) tableView: (UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
numbers = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:#"numbers"]];
people = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:#"people"]];
NSLog(#"%#",numbers);
NSLog(#"%#",people);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
cell.accessoryType = UITableViewCellAccessoryDetailButton;
}
NSLog(#"%ld", (long)indexPath.row);
NSLog(#"%ld", (long)people.count);
NSString* Tname = [people objectAtIndex:indexPath.row];
NSString* Tnumber = [numbers objectAtIndex:indexPath.row];
cell.textLabel.text = Tname;
cell.detailTextLabel.text = Tnumber;
return cell;
}
#end
Any suggestions?
1) this is something strange:
if(numbers.count == 0 && numbers.count == 0){
numbers = [[NSMutableArray alloc] init];
people = [[NSMutableArray alloc] init];
}
i think you mean
if(numbers.count == 0 && people.count == 0)
2) i think you mast insert reload of table after you add your new object into people and numbers arrays
[people addObject:nameField];
[numbers addObject:phoneField];
[tableView reloadData];
My hunch is that, there this a bug in the tableView:numberOfRowsInSection code. You need to return count of people or numbers. Not get the value from the user defaults. Since they don't have the newly added person and number.
-(NSInteger) tableView:(UITableView*) tableView numberOfRowsInSection:(NSInteger)section{
return [numbers count];
}
I am a newbie on iOS Development and I am sorting out how am I going to have a table view with a checkmark on the settings bundle like this:
Is there any way to do it? Or is this just only available for specific iOS approved apps?
Looking forward for an answer. Thanks.
You can achieve this through Multi Value Element. When the user taps a preference containing a multi-value element, the Settings application displays a new page with the possible values to choose from. Google it for the tutorials if needed (Multivalue option for the settings bundle).
Here are the details : PSMultiValueSpecifier
If I am guessing right and you want to know how to display settings outside your app and in the iOS Settings, then check out this tutorial. It should get you started.
Taken out of the link below:
I have been searching around and couldn't find a boilerplate solution so created my own code for doing this. It supports the setting types Title, Group, Text Field, Multi Value and Toggle Switch.
It does NOT SUPPORT Slider.
This solution does support portrait AND landscape mode and can also handle changing over device orientations.
First off all I'm assuming that you are using the following code to read out your default values from the Settings.bundle.
- (void) registerDefaultsFromSettingsBundle
{
NSLog(#"Registering default values from Settings.bundle");
NSUserDefaults * defs = [NSUserDefaults standardUserDefaults];
[defs synchronize];
NSString *settingsBundle = [[NSBundle mainBundle] pathForResource: #"Settings" ofType: #"bundle"];
if(!settingsBundle)
{
NSLog(#"Could not find Settings.bundle");
return;
}
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent: #"Root.plist"]];
NSArray *preferences = [settings objectForKey: #"PreferenceSpecifiers"];
NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
for (NSDictionary *prefSpecification in preferences)
{
NSString *key = [prefSpecification objectForKey:#"Key"];
if (key)
{
// check if value readable in userDefaults
id currentObject = [defs objectForKey: key];
if (currentObject == nil)
{
// not readable: set value from Settings.bundle
id objectToSet = [prefSpecification objectForKey: #"DefaultValue"];
[defaultsToRegister setObject: objectToSet forKey: key];
NSLog(#"Setting object %# for key %#", objectToSet, key);
}
else
{
// already readable: don't touch
NSLog(#"Key %# is readable (value: %#), nothing written to defaults.", key, currentObject);
}
}
}
[defs registerDefaults: defaultsToRegister];
[defs synchronize];
}
Okay now you'll need 2 classes. SettingsTableViewController and MultiValueTableViewController.
SettingsTableViewController.h
//
// SettingsTableViewController.h
// Cochlear App
//
// Created by Gilles Lesire on 16/07/14.
// Free to use
//
#import <UIKit/UIKit.h>
#import "MultiValueTableViewController.h"
#interface SettingsTableViewController : UITableViewController <MultiValueDelegate> {
NSMutableArray *labelViews;
NSMutableArray *textViews;
NSMutableArray *settingsKeys;
NSMutableArray *settingsTableSections;
NSMutableArray *settingsTableData;
}
#end
SettingsTableViewController.m
//
// SettingsTableViewController.m
// Cochlear App
//
// Created by Gilles Lesire on 16/07/14.
// Free to use
////
#import "SettingsTableViewController.h"
#define labelCGRectX 25
#define labelCGRectY 25
#define labelCGRectWidth 140
#define labelCGRectHeight 21
#define typeGroup #"PSGroupSpecifier"
#define typeTitle #"PSTitleValueSpecifier"
#define typeToggleSwitch #"PSToggleSwitchSpecifier"
#define typeMultiValue #"PSMultiValueSpecifier"
#define typeTextField #"PSTextFieldSpecifier"
#interface SettingsTableViewController ()
#end
#implementation SettingsTableViewController
- (id)initWithStyle: (UITableViewStyle)style
{
self = [super initWithStyle: style];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Track rotation changes
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(deviceOrientationDidChange) name: UIDeviceOrientationDidChangeNotification object: nil];
// Avoid tab bar to overlap tableview
self.edgesForExtendedLayout = UIRectEdgeAll;
self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f);
// Custom initialization
labelViews = [NSMutableArray arrayWithObjects: nil];
textViews = [NSMutableArray arrayWithObjects: nil];
settingsTableSections = [NSMutableArray arrayWithObjects: nil];
settingsTableData = [NSMutableArray arrayWithObjects: nil];
settingsKeys = [NSMutableArray arrayWithObjects: nil];
NSLog(#"Created arrays");
NSString *settingsBundle = [[NSBundle mainBundle] pathForResource: #"Settings" ofType: #"bundle"];
if(!settingsBundle) {
NSLog(#"Could not find Settings.bundle");
} else {
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent: #"Root.plist"]];
NSArray *preferences = [settings objectForKey: #"PreferenceSpecifiers"];
NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity: [preferences count]];
for (NSDictionary *prefSpecification in preferences) {
NSLog(#"%#", prefSpecification);
NSString *title = [prefSpecification objectForKey: #"Title"];
NSString *type = [prefSpecification objectForKey: #"Type"];
if([type isEqualToString: typeGroup]) {
// Create new section
[settingsTableSections addObject: title];
NSMutableArray *newSection = [NSMutableArray arrayWithObjects: nil];
[settingsTableData addObject: newSection];
} else {
// Add specification to last section
[[settingsTableData objectAtIndex: ([settingsTableData count] - 1)] addObject:prefSpecification];
}
}
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView
{
// Return the number of sections.
return [settingsTableSections count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[settingsTableData objectAtIndex: section] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [settingsTableSections objectAtIndex: section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Get the dictionary item
NSDictionary *prefSpecification = [[settingsTableData objectAtIndex: indexPath.section] objectAtIndex: indexPath.row];
NSString *title = [prefSpecification objectForKey: #"Title"];
NSString *key = [prefSpecification objectForKey: #"Key"];
NSString *type = [prefSpecification objectForKey: #"Type"];
// Define cell
UITableViewCell *cell;
// Keep tag of keys
[settingsKeys addObject: key];
int tag = [settingsKeys count] - 1;
if([type isEqualToString: typeTitle]) {
// Create cell
cell = [tableView dequeueReusableCellWithIdentifier: #"Cell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// Set title
cell.textLabel.text = title;
// Add label
UILabel *labelView = [[UILabel alloc] initWithFrame: CGRectMake(labelCGRectX, labelCGRectY, labelCGRectWidth, labelCGRectHeight)];
labelView.text = [[NSUserDefaults standardUserDefaults] objectForKey: key];
labelView.textAlignment = NSTextAlignmentRight;
labelView.textColor = [UIColor grayColor];
cell.accessoryView = labelView;
}
if([type isEqualToString: typeToggleSwitch]) {
// Create cell
cell = [tableView dequeueReusableCellWithIdentifier: #"Cell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// Set title
cell.textLabel.text = title;
// Add switch
UISwitch *switchView = [[UISwitch alloc] initWithFrame: CGRectZero];
cell.accessoryView = switchView;
switchView.tag = tag;
[switchView setOn: [[[NSUserDefaults standardUserDefaults] objectForKey: key] boolValue] animated: NO];
// Connect action to switch
[switchView addTarget: self action: #selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
}
if([type isEqualToString: typeTextField]) {
// Create cell
cell = [tableView dequeueReusableCellWithIdentifier: #"Cell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
int frameSize = self.view.frame.size.width;
UITextField *textField = [[UITextField alloc] initWithFrame: CGRectMake(15, 10, frameSize,labelCGRectHeight)];
textField.tag = tag;
textField.text = [[NSUserDefaults standardUserDefaults] objectForKey: key];
[textField addTarget: self
action: #selector(textFieldChanged:)
forControlEvents: UIControlEventEditingChanged];
[cell.contentView addSubview: textField];
// Tract text field
[textViews addObject: textField];
}
if([type isEqualToString: typeMultiValue]) {
// Create cell
cell = [tableView dequeueReusableCellWithIdentifier: #"MultiValueCell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// Get value
int value = [[[NSUserDefaults standardUserDefaults] objectForKey: key] intValue];
NSArray *values = [prefSpecification objectForKey: #"Values"];
NSArray *titles = [prefSpecification objectForKey: #"Titles"];
NSString *multiValue = #"Unknown";
int index = [values indexOfObject: [NSString stringWithFormat: #"%d", value]];
if(index >= 0 && index < [values count]) {
multiValue = [titles objectAtIndex: index];
}
// Set title
cell.textLabel.text = title;
int frameSize = self.view.frame.size.width;
// Add label
UILabel *labelView = [[UILabel alloc] initWithFrame: CGRectMake((frameSize - labelCGRectWidth - 30), 12, labelCGRectWidth, labelCGRectHeight)];
labelView.textAlignment = NSTextAlignmentRight;
labelView.text = multiValue;
labelView.textColor = [UIColor grayColor];
[cell addSubview: labelView];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Track label
[labelViews addObject: labelView];
}
return cell;
}
- (void) switchChanged: (id) sender {
UISwitch* switchControl = sender;
NSString *key = [settingsKeys objectAtIndex: switchControl.tag];
NSNumber *numberValue = [NSNumber numberWithBool: switchControl.on];
[[NSUserDefaults standardUserDefaults] setObject: numberValue forKey: key];
}
- (void) textFieldChanged: (id) sender {
UITextField* textControl = sender;
NSString *key = [settingsKeys objectAtIndex: textControl.tag];
NSString *stringValue = textControl.text;
[[NSUserDefaults standardUserDefaults] setObject: stringValue forKey: key];
}
- (void) selectedMultiValue {
[self reloadTable];
}
- (void) deviceOrientationDidChange {
[self reloadTable];
}
- (void)prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender
{
if ([[segue identifier] isEqualToString: #"changeMultiValue"])
{
MultiValueTableViewController *multiValueViewController =
[segue destinationViewController];
NSIndexPath *indexPath = [self.tableView
indexPathForSelectedRow];
// Get the dictionary item
NSDictionary *prefSpecification = [[settingsTableData objectAtIndex: indexPath.section] objectAtIndex: indexPath.row];
multiValueViewController.prefSpecification = prefSpecification;
multiValueViewController.delegate = self;
}
}
- (void) reloadTable {
for (UILabel *labelView in labelViews) {
[labelView removeFromSuperview];
}
for (UITextField *textView in textViews) {
[textView removeFromSuperview];
}
// Remove references to objects for garbage collection
labelViews = [NSMutableArray arrayWithObjects: nil];
textViews = [NSMutableArray arrayWithObjects: nil];
[self.tableView reloadData];
}
- (void) dealloc {
// Remove observers
[[NSNotificationCenter defaultCenter] removeObserver: self];
}
#end
MultiValueTableViewController.h
//
// MultiValueTableViewController.h
// Cochlear App
//
// Created by Gilles Lesire on 16/07/14.
// Free to use
//
#import <UIKit/UIKit.h>
#import "SettingsController.h"
#protocol MultiValueDelegate
- (void) selectedMultiValue;
#end
#interface MultiValueTableViewController : UITableViewController {
NSDictionary *prefSpecification;
}
#property (nonatomic) id<MultiValueDelegate> delegate;
#property (strong, nonatomic) NSDictionary *prefSpecification;
#end
MultiValueTableViewController.m
//
// MultiValueTableViewController.m
// Cochlear App
//
// Created by Gilles Lesire on 16/07/14.
// Free to use
//
#import "MultiValueTableViewController.h"
#interface MultiValueTableViewController ()
#end
#implementation MultiValueTableViewController
#synthesize prefSpecification;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *title = [prefSpecification objectForKey: #"Title"];
self.navigationItem.title = title;
// Avoid tab bar to overlap tableview
self.edgesForExtendedLayout = UIRectEdgeAll;
self.tableView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, CGRectGetHeight(self.tabBarController.tabBar.frame), 0.0f);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSArray *values = [prefSpecification objectForKey: #"Values"];
// Return the number of rows in the section.
return [values count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: #"Cell" forIndexPath:indexPath];
NSString *key = [prefSpecification objectForKey: #"Key"];
NSArray *titles = [prefSpecification objectForKey: #"Titles"];
NSArray *values = [prefSpecification objectForKey: #"Values"];
NSString *title = [titles objectAtIndex: indexPath.row];
// Create cell
cell.selectionStyle = UITableViewCellSelectionStyleGray;
// Set title
cell.textLabel.text = title;
// If this is the selected value
if([[values objectAtIndex: indexPath.row] intValue] == [[[NSUserDefaults standardUserDefaults] objectForKey: key] intValue]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *key = [prefSpecification objectForKey: #"Key"];
NSArray *values = [prefSpecification objectForKey: #"Values"];
NSNumber *value = [values objectAtIndex: indexPath.row];
[[NSUserDefaults standardUserDefaults] setObject: value forKey: key];
[self.delegate selectedMultiValue];
[self.tableView reloadData];
}
#end
Storyboard Now go the storyboard and create a TableViewController. Select the TableViewController and Choose "Editor" -> "Embed in" -> "Navigation controller".
Set the class of the TableViewController as SettingsTableViewController. Set the identifier of the cell as "Cell", add a second TableViewCell to the TableView and set it's identifier as "MultiValueCell". Add a second TableViewController, and CTRL+CLICK and drag from the MultiValueCell to the second TableViewController. Set the class of the second TableViewController as MultiValueTableViewController. Set the identifier of the cell in the second TableViewController as "Cell" too. That's it!
#import "SettingViewController.h"
NSInteger selectedIndex;
//Use this, it works for me
- (void)viewDidLoad
{
selectedIndex = 0;
[super viewDidLoad];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50.0f;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_arrayForSaveSetting count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if (indexPath.row==selectedIndex) {
cell.accessoryType =UITableViewCellAccessoryCheckmark;
}
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
selectedIndex=indexPath.row;
[_tblSaveSetting reloadData];
}
I cannot seem to wrap my head around population of different sections in a single uitableview. Is there any way I can do it with my current setup? I am willing to change the way my arrays are set up if I need to. I have a lot of useless code in here that I am going to remove later, but basically all of it works. The issue is that all sections are being loaded with all of the data stored in the NSMutableDictionary.
- (void)viewDidLoad
{
bannerIsVisible = NO;
bannerView.hidden = YES;
[super viewDidLoad];
//NSDate Info
self.secondsPerDay = 86400;
self.todayDate = [[NSUserDefaults standardUserDefaults]objectForKey:#"todayDate"];
self.dateFormat = [[NSDateFormatter alloc] init];
[self.dateFormat setDateFormat:#"MMMM dd, yyyy"];
self.todayString = [self.dateFormat stringFromDate:self.todayDate];
//change to string and set to the string properties
todaysDate.text = self.todayString;
//initialize the arrays and the state
self.mainArray = [[NSMutableArray alloc] init];
self.subjectArray = [[NSMutableArray alloc]init];
self.mainDictionary = [[NSMutableDictionary alloc]init];
/*The UITapGestureRecognizer will make it so the program can dismiss
the keyboard at will. */
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(hideKeyboard)];
[self.view addGestureRecognizer:tapGesture];
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
// app already launched
[self.subjectArray addObjectsFromArray:[[NSUserDefaults standardUserDefaults]arrayForKey:[NSString stringWithFormat:#"%#sections",self.todayString]]];
[[NSUserDefaults standardUserDefaults]synchronize];
}
else
{
self.counter++;
// This is the first launch ever
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
AG_Storage *store = [[AG_Storage alloc] init];
store.itemName = #"Swipe to Delete";
NSString *storeString = store.itemName;
self.counter++;
NSLog(#"counter%d",self.counter);
AG_Storage *store2 = [[AG_Storage alloc] init];
store2.itemName = #"+ Button to Add";
NSString *store2String = store2.itemName;
self.counter++;
NSString *stringStuff = #"Tap this area to add notes for the day!";
[[NSUserDefaults standardUserDefaults]setObject:stringStuff forKey:[NSString stringWithFormat:#"%#textView",self.todayString]];
[[NSUserDefaults standardUserDefaults]synchronize];
[self.subjectArray addObject:#"Test"];
[[NSUserDefaults standardUserDefaults]setObject:self.subjectArray forKey:[NSString stringWithFormat:#"%#sections",self.todayString]];
[[NSUserDefaults standardUserDefaults]synchronize];
//allocates tempDic and gives it tasks and keys
NSMutableDictionary *tempDic = [[NSMutableDictionary alloc] initWithObjectsAndKeys: storeString, [NSString stringWithFormat:#"%d",0], store2String, [NSString stringWithFormat:#"%d",1], nil];
//Sets tempDic in mainDictionary with the key Test
[self.mainDictionary setObject:tempDic forKey:[NSString stringWithFormat:#"%#",[self.subjectArray objectAtIndex:0]]];
//Saves mainDictionary
[[NSUserDefaults standardUserDefaults] setObject:self.mainDictionary forKey:[NSString stringWithFormat:#"mainDictionary%#",self.todayString ]];
[[NSUserDefaults standardUserDefaults]synchronize];
}
[self loadInitialData];
}
- (void)loadInitialData
{
// Do any additional setup after loading the view, typically from a nib.
//call my custom class and store today's date.
AG_Storage *theDateToday = [[AG_Storage alloc]init];
theDateToday.todaysDate = self.todayDate;
NSLog(#"tried loadinitialdata");
NSMutableDictionary *mutDic = [[NSUserDefaults standardUserDefaults] objectForKey:[NSString stringWithFormat:#"mainDictionary%#",self.todayString ]];
NSLog(#"%#",mutDic);
//Populate mainArray
for (int x= 0; x < self.subjectArray.count; x++) {
for (int y = 0; y != -99; y++) {
NSDictionary *tempDir = [mutDic valueForKey:[NSString stringWithFormat:#"%#",[self.subjectArray objectAtIndex:x]]];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"tempDir %#",tempDir);
NSString *tempString = [tempDir valueForKey:[NSString stringWithFormat:#"%d",y]];
NSLog(#"tempString %#",tempString);
if ([tempString length] != 0) {
//add the data to the mainArray and update counter
[self.mainArray addObject:tempString];
NSLog(#"added to array%#", self.mainArray);
}
else
y = -100;
}
NSLog(#"SHOULD EXIT LOOP RIGHT NOW");
}
NSLog(#"LOOP ENDED PROPERLY");
/*Populate textviews which hold the user's notes. Populates
based on the state of the program.*/
todayTextView.text = [[NSUserDefaults standardUserDefaults]stringForKey:[NSString stringWithFormat:#"%#textView",self.todayString]];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.subjectArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.mainArray.count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [self.subjectArray objectAtIndex:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableViewer cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//populates the table based on which view is selected.
UITableViewCell *cell = [tableViewer dequeueReusableCellWithIdentifier:#"todayCell"];
NSString *toDoItem = [self.mainArray objectAtIndex:indexPath.row];
cell.textLabel.text = toDoItem;
cell.textLabel.adjustsFontSizeToFitWidth = YES;
cell.textLabel.minimumScaleFactor = 0.5;
return cell;
}
In order to reuse sections and try to preserve your code setup, I would recommend using a [NSMutableArray] of [NSMutableArray]s. Each NSMutableArray would represent a section of your table, where the index corresponds to the section
Then in tableView:numberOfRowsInSection: you grab the array from the index using the NSInteger param.
Inside (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath you access section via indexPath.section and use that on your NSMutableArray of sections to pull out your data. From there, you then have access to data only for that section to create your rows.
Example:
self.sectionArrays = [NSMutableArray new];
....
(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.sectionArrays count];
}
(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[self.sectionArrays objectAtIndex:section] count];
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *currentSectionArray = [self.sectionArrays objectAtIndex:indexPath.section];
...
NSString *toDoItem = [currentSectionArray objectAtIndex:indexPath.row];
...
configure cell
}
I am trying to save checked and remove unchecked tablecells into NSUserdefaults but it seems to ignore it as the NSLog in the for loop never gets called not sure why, is there something wrong with this code? (Everything shows up right and the cells get checked and un-checked correctly)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//setting up nsUser
userDefaults = [NSUserDefaults standardUserDefaults];
//current row text
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSString *cellName = cell.textLabel.text;
// Check if current row is selected
Boolean isNowChecked = NO;
if([tableView cellForRowAtIndexPath:indexPath].accessoryType == UITableViewCellAccessoryCheckmark)
{
isNowChecked = YES;
}
if(isNowChecked)
{
NSLog(#"UNCHECKING");
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
//takes away highlight from selection
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//retrieve from nsdefaults
NSMutableArray *arrayOfCategories = [userDefaults objectForKey:#"categories"];
NSMutableArray *categoriesSelected;
//remove from nsuserdefaults
//add to nsuserdefaults
for (NSString* o in arrayOfCategories)
{
NSLog(#"%#",o);
if([o isEqualToString:cellName]) {
} else {
[categoriesSelected addObject:o];
}
}
//set for nsdefaults
[userDefaults setObject:categoriesSelected forKey:#"categories"];
[userDefaults synchronize];
}
else
{
NSLog(#"CHECKING");
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
//takes away highlight from selection
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSMutableArray *categoriesSelected;
//add to array
[categoriesSelected addObject:cellName];
//retrieve from nsdefaults
NSMutableArray *arrayOfCategories = [userDefaults objectForKey:#"categories"];
//add to nsuserdefaults
for (NSString* o in arrayOfCategories)
{
NSLog(#"%#",o);
[categoriesSelected addObject:o];
}
//set for nsdefaults
[userDefaults setObject:categoriesSelected forKey:#"categories"];
[userDefaults synchronize];
}
}
Change this line, otherwise you have an uninitialized Array:
NSMutableArray *categoriesSelected;
to:
NSMutableArray *categoriesSelected = [[NSMutableArray alloc] init];
Update: After looking at your code there are actually quite a few things that should be improved.
You should load the list of checked categories once in viewDidLoad and keep the list in an instance variable.
It's not a good idea to check to see if a cell is currently checked or not by looking at the cell's accessory type. Your data model should tell you which rows are checked.
You have a lot of duplicate code to toggle the checked state of a cell.
Use a set, not an array, to keep track of the checked cells.
Try the following changes:
Add an NSMutableSet instance variable to your class:
NSMutableSet *_checkedCategories;
In viewDidLoad, load the set:
NSArray *checkedCategories = [[NSUserDefault standardUserDefaults] objectForKey:#"categories"];
if (checkedCategories) {
_checkedCategories = [[NSMutableSet alloc] initWithArray:checkedCategories];
} else {
_checkedCategories = [[NSMutableSet alloc] init];
}
Now update your didSelectRow method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellName = ...; // get the cell name from your data model, not the cell
BOOL isChecked = [_checkedCategories containsObject:cellName];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (isChecked) {
[_checkedCategories removeObject:cellName]; // no longer checked
cell.accessoryType = UITableViewCellAccessoryNone;
} else {
[_checkedCategories addObject:cellName]; // now checked
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[[NSUserDefaults standardUserDefaults] setObject:[_checkedCategories allObjects] forKey:#"categories"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I am trying to save any of the items as user selects from a uitableview to nsuserdefaults. At that moment only the most recent selection is saved. I would like to have the user to be able to select any of the rows they want and then saved to nsuserdefaults and then use that info anywhere in the app.
thanks for any help
here's my code:
- (void)viewDidLoad
{
[super viewDidLoad];
// categories array
listOfCategories = [[NSMutableArray alloc] init];
[listOfCategories addObject:#"Food & Drinks"];
[listOfCategories addObject:#"Beauty & Wellness"];
[listOfCategories addObject:#"Sports & Fun Activities"];
[listOfCategories addObject:#"Labor & Services"];
[listOfCategories addObject:#"Clothes & Accessories"];
[listOfCategories addObject:#"Education & Training"];
[listOfCategories addObject:#"Products"];
// add label
UIView *viewForHeader = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,40)];
UILabel *categoryLabel = [[UILabel alloc] initWithFrame:CGRectMake(10,0,80,30)];
categoryLabel.text = #"Select all:";
[categoryLabel setFont:[UIFont fontWithName: #"Asap-Bold" size: 14.0f]];
categoryLabel.textColor = [UIColor lightGrayColor];
// add switch
onoff = [[UISwitch alloc] initWithFrame: CGRectMake(100.0f, 0.0f, 100.0f, 0.0f)];
[onoff addTarget: self action: #selector(flipSwitch:) forControlEvents:UIControlEventValueChanged];
[viewForHeader addSubview:onoff];
[viewForHeader addSubview:categoryLabel];
self.tableView.tableHeaderView = viewForHeader;
}
// uiswitch button
- (IBAction) flipSwitch: (id) sender {
onoff = (UISwitch *) sender;
NSLog(#"%#", onoff.on ? #"On" : #"Off");
if (onoff.on) {
for (NSInteger s = 0; s < self.tableView.numberOfSections; s++) {
for (NSInteger r = 0; r < [self.tableView numberOfRowsInSection:s]; r++) {
[self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:r inSection:s]
animated:NO
scrollPosition:UITableViewScrollPositionNone];
}
}
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [listOfCategories count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
} // Configure the cell...
NSString *cellValue = [listOfCategories objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
[cell.textLabel setFont:[UIFont fontWithName: #"Asap-Bold" size: 14.0f]];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int index = indexPath.row; id obj = [listOfCategories objectAtIndex:index];
//This toggles the checkmark
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
UIImage *image = [UIImage imageNamed:#"icon-tick.png"];
UIButton *downloadButton = [UIButton buttonWithType:UIButtonTypeCustom];
[downloadButton setImage:image forState:UIControlStateNormal];
[downloadButton setFrame:CGRectMake(0, 0, 19, 19)];
[downloadButton setBackgroundColor:[UIColor clearColor]];
[tableView cellForRowAtIndexPath:indexPath].accessoryView = downloadButton;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
//This sets the array
} else
{
cell.accessoryType = UITableViewCellAccessoryNone;
UIButton *downloadButton = [UIButton buttonWithType:UIButtonTypeCustom];
[downloadButton setTitle:#"" forState:UIControlStateNormal];
[downloadButton setFrame:CGRectMake(0, 0, 0, 0)];
[tableView cellForRowAtIndexPath:indexPath].accessoryView = downloadButton;
}
// Save text of the selected cell:
UITableViewCell *cellSelected = [tableView cellForRowAtIndexPath:indexPath];
if ([cellSelected.textLabel.text isEqualToString:#"Food & Drinks"]) {
NSString *valueToSave = cellSelected.textLabel.text;
[[NSUserDefaults standardUserDefaults]
setObject:valueToSave forKey:#"preferenceName"];
}
NSString *valueToSave = cellSelected.textLabel.text;
[[NSUserDefaults standardUserDefaults]
setObject:valueToSave forKey:#"preferenceName"];
NSString *savedValue = [[NSUserDefaults standardUserDefaults]
stringForKey:#"preferenceName"];
NSLog(#"savedValue %#", savedValue);
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
// Customize archiver here
[archiver encodeObject:obj forKey:#"keyForYourArrayOfNSIndexPathObjects"];
[archiver finishEncoding];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"keyForYourArrayOfNSIndexPathObjects"];
NSKeyedUnarchiver *unarchiver;
unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:
[[NSUserDefaults standardUserDefaults] objectForKey:#"keyForYourArrayOfNSIndexPathObjects"]];
// Customize unarchiver here
categoryItemSelected = [unarchiver decodeObjectForKey:#"keyForYourArrayOfNSIndexPathObjects"];
[unarchiver finishDecoding];
NSLog(#"list of categories selected %#", categoryItemSelected);
}
#end
The issue is because you overridden the previously saved data.
So first of all you need to load saved data:
NSKeyedUnarchiver *unarchiver;
unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:
[[NSUserDefaults standardUserDefaults] objectForKey:#"keyForYourArrayOfNSIndexPathObjects"]];
// Customize unarchiver here
categoryItemSelected = [unarchiver decodeObjectForKey:#"keyForYourArrayOfNSIndexPathObjects"];
[unarchiver finishDecoding];
if (categoryItemSelected == nil) {
//If it isn't been created - then create new array
}
Then add new object:
[categoryItemSelected addObject: obj];
And then save it back to UserDefautls