I have 8 tabs and am using customizable tabBarItems. So the user can reorder the tabs. Now my question is how do I get the order of the tabs and save that to NSUserDefaults so it remains the same whenever the user exits the app and comes back.
Here is the code I've got so far:
- (void)tabBar:(UITabBar *)tabBar didEndCustomizingItems:(NSArray *)items changed:(BOOL)changed {
NSMutableArray *savedOrder = [NSMutableArray arrayWithCapacity:8];
NSArray *tabOrderToSave = tabBarController.viewControllers;
for (UIViewController *aViewController in tabOrderToSave) {
[savedOrder addObject:aViewController.title];
}
[[NSUserDefaults standardUserDefaults] setObject:savedOrder forKey:#"savedTabOrder"];
}
No errors in that code, it just doesn't work.
What am I doing wrong?
By the way: My app is a tabBarApplication.
EDIT:
Here is what I've done.
Created a tabBar Application
Populated Views with data
Added 8 tabs with different views loaded from controllers
Then added the customizable reordering of tabs
Added the code above to save the tab order
Have do I retrieve that saved data when I relaunch the app?
EDIT 2:
I've been searching around and found a lot of info on this but very little on how.
Here is the code I have now:
- (void)applicationWillTerminate:(UIApplication *)application {
/*
Called when the application is about to terminate.
See also applicationDidEnterBackground:.
*/
NSMutableArray *vcArray = [NSMutableArray arrayWithCapacity:6];
NSArray *savedViews = tabBarController.viewControllers;
for (UIViewController *theVC in savedViews){
[vcArray addObject:theVC.title];
}
[[NSUserDefaults standardUserDefaults] setObject:vcArray forKey:#"tabLayout"];
}
- (void)tabBar:(UITabBar *)tabBar didEndCustomizingItems:(NSArray *)items changed:(BOOL)changed {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *tabLayout = [defaults arrayForKey:#"tabLayout"];
NSMutableArray *orderedLayout = [NSMutableArray arrayWithCapacity:6];
NSArray *defaultOrder = tabBarController.viewControllers;
for (int i =0; i < 6; i++){
for (UIViewController *theVC in defaultOrder) {
if ([theVC.title isEqualToString:[tabLayout objectAtIndex:i]]) {
[orderedLayout addObject:theVC];
}
}
}
tabBarController.viewControllers = orderedLayout;
}
Why doesn't this work, and by that I mean after I customize the tabbar and hit stop in Xcode, when I go to run it again it doesn't show my saved order. What in the world am I doing wrong? Is this the correct way to do this?
You're missing the synchronize.
Try this:
- (void)tabBar:(UITabBar *)tabBar didEndCustomizingItems:(NSArray *)items changed:(BOOL)changed {
NSMutableArray *savedOrder = [NSMutableArray arrayWithCapacity:8];
NSArray *tabOrderToSave = tabBarController.viewControllers;
for (UIViewController *aViewController in tabOrderToSave) {
[savedOrder addObject:aViewController.title];
}
[[NSUserDefaults standardUserDefaults] setObject:savedOrder forKey:#"savedTabOrder"];
[[NSUserDefaults standarduserDefaults] synchronize];
}
As #Nekto said - What doesn't work? How do you detect this?
also saving this info won't save your tabbar order... you have to fetch this order from nsuserdefault and generate the tab in that order next time ..
Here's the code I've been using
-(void) saveTabOrder
{
NSMutableArray *savedOrder = [NSMutableArray arrayWithCapacity:6];
NSArray *tabOrderToSave = tabBarController.viewControllers;
for (UIViewController *aViewController in tabOrderToSave)
{
[savedOrder addObject:aViewController.title];
}
[[NSUserDefaults standardUserDefaults] setObject:savedOrder forKey:#"savedTabOrder"];
//[[NSUserDefaults standardUserDefaults] synchronize];
}
// NOTE -- because tabs may change between versions of the app,
// should NOT try to restore tabs if the tab count or names
// differ from the saved version.
- (void)setTabOrderIfSaved {
//return;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *savedOrder = [defaults arrayForKey:#"savedTabOrder"];
NSMutableArray *orderedTabs = [NSMutableArray arrayWithCapacity:6];
if ([savedOrder count] > 0 )
{
for (int i = 0; i < [savedOrder count]; i++) // loop through saved tabs
{
BOOL tabFound = NO;
for (UIViewController *aController in tabBarController.viewControllers) // loop through actual tabs
{
if ([aController.title isEqualToString:[savedOrder objectAtIndex:i]])
{
[orderedTabs addObject:aController];
tabFound = YES;
}
}
if (!tabFound)
{
// so the old tab order doesn't include this tab. Lets bail and use the default ordering
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"savedTabOrder"];
return;
}
}
tabBarController.viewControllers = orderedTabs;
}
}
Related
I am stuck with little problem. I have basic calculating app.
Viewcontroller.m
#import "ViewController.h"
#interface ViewController () <UITextFieldDelegate, UIAlertViewDelegate>
#end
#implementation ViewController
-(void)textFieldDidEndEditing:(UITextField *)textField
{
self.currentSettings = _currentSettings;
[self calculateThePrice];
}
-(void)calculateThePrice
{
float wynik = self.currentSettings.kwh * self.currentSettings.price;
self.priceLabel.text = [NSString stringWithFormat:#"%.02f %#", wynik , self.currentSettings.currency];
}
SettingsVC.m
#import "SettingsVC.h"
#interface SettingsVC () <UITextFieldDelegate>
#end
#implementation SettingsVC
#pragma mark - UserDefaults Implementation
-(void)viewWillAppear:(BOOL)animated
{
[self createCurrencyArray];
NSUserDefaults *priceDef = [NSUserDefaults standardUserDefaults];
NSString *priceDefText = [priceDef stringForKey:#"priceCall"];
_priceTextField.text = priceDefText;
NSUserDefaults *currencyDef = [NSUserDefaults standardUserDefaults];
[_currencyPicker selectRow:[currencyDef integerForKey:#"currencyCall"]
inComponent:0 animated:NO];
[priceDef synchronize];
[currencyDef synchronize];
}
-(void)viewWillDisappear:(BOOL)animated
{
NSString *textOfPriceTexField = _priceTextField.text;
[[NSUserDefaults standardUserDefaults] setObject:textOfPriceTexField forKey:#"priceCall"];
}
Now, the problem is when I want program to automatically-calculate, it won't. To have any result, I have to switch to Second View, choose a value from picker and then when I will go back, I have my result.
But...
- When I change value on 1st screen, result won't change. When I change value on 2nd scree, result won't change. But when I change value on PickerView - TADAH - result updates!
When I go to second view, and go back to first, then go again to second and go back to first, my result changes to "0.00 (NULL)"...
Any ideas where I did wrong? I think it is about NSUserDefaults, I tried many options, nothing worked, nor changed anything.
You need to synchronize NSUserDefaults when you set new values. You are using synchronize when you retrieve values.
Here you don't need 2 pointers to defaults and don't need synchronize:
NSUserDefaults *priceDef = [NSUserDefaults standardUserDefaults];
NSString *priceDefText = [priceDef stringForKey:#"priceCall"];
_priceTextField.text = priceDefText;
[_currencyPicker selectRow:[priceDef integerForKey:#"currencyCall"] inComponent:0 animated:NO];
Here you need synchronizes:
-(void)viewWillDisappear:(BOOL)animated
{
NSString *textOfPriceTexField = _priceTextField.text;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:textOfPriceTexField forKey:#"priceCall"];
[defaults synchronize];
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSInteger selectedRow = [_currencyPicker selectedRowInComponent:0];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:selectedRow forKey:#"currencyCall"];
[defaults synchronize];
self.currentSettings.currency = [self.currencyArray objectAtIndex:row];
self.currentSettings.price = [self.priceTextField.text floatValue];
//[self userDidFinishSetting];
}
Try this code
-(void)viewWillDisappear:(BOOL)animated
{
NSString *textOfPriceTexField = _priceTextField.text;
[[NSUserDefaults standardUserDefaults] setObject:textOfPriceTexField forKey:#"priceCall"];
}
-(void)viewWillAppear:(BOOL)animated
{
[self createCurrencyArray];
NSUserDefaults *priceDef = [NSUserDefaults standardUserDefaults];
NSString *priceDefText = [priceDef objectForKey:#"priceCall"];
}
instead of stringForKey you can call objectForKey same as for integerForKey also you can call objectForKey. Actually you are setting the object not string or integer.
I am trying to save a NSMutableArray when the app shuts down, and then load it on startup to a tableview.
Here's my code:
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:_savedText forKey:#"savedText"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super initWithCoder:decoder])) {
_savedText = [decoder decodeObjectForKey:#"savedText"];
}
return self;
}
I use this code to save the MutableArray:
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:_savedText];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"savedText"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
And this to load it:
- (void)viewDidLoad {
NSData *savedData = [[NSUserDefaults standardUserDefaults] objectForKey:#"savedText"];
NSMutableArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:savedData];
array = [_savedText copy];
}
There are no errors, but the table view is empty on startup. Have I forgotten something?
You should read the data from the defaults and set to _savedText object. You are reading the data into var named array and then assigning array = [_savedText copy]. This will probably be nil. It should be the reverse.
- (void)viewDidLoad {
NSData *savedData = [[NSUserDefaults standardUserDefaults] objectForKey:#"savedText"];
NSMutableArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:savedData];
_savedText = [array copy];
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I'm building an app for tap counting (for example: people going into the room).
The main goal is to be able to have multiple counters (for example: counter for main room, counter for white room, counter for green room etc.).
The app consists of:
A list of counters - CounterListTableViewController, with function
is to create new items (counters) in the list
A counter page - RowCounterViewController
I am stuck at the moment at: Saving count number of counters with reference to item in the TableViewController.
Possible solutions:
Save all count numbers into an array - I don’t know how yet (a complete beginner).
Save all count numbers in NSUserDefaults with its index, as suggested by KudoCC in answers bellow.
Thank you!
Last edit:
#import "RowCounterViewController.h"
#interface RowCounterViewController ()
#end
#implementation RowCounterViewController
-(IBAction)plus {
counter=counter + 1;
count.text = [NSString stringWithFormat:#"%i",counter];
}
-(IBAction)minus {
counter=counter - 1;
count.text = [NSString stringWithFormat:#"%i",counter];
}
-(IBAction)reset {
counter=0;
count.text = [NSString stringWithFormat:#"%i",counter];
}
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(saveCount)
name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(saveCount)
name:UIApplicationWillTerminateNotification object:nil];
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] objectForKey:#"saveCount"] ;
NSUInteger count = [dict objectForKey:#(itemIndex)] ;
//NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//NSString *countString = [userDefaults objectForKey:#"saveCount"];
//NSLog(#"Your Count: %#",count);
//checking if data in user defaults is not empty
//if(countString.length>0)
//{
// count.text = [NSString stringWithFormat:#"%i",counter];
// counter = [countString intValue];
//}
//else
//{
// //for first time
// counter = 0;
// count.text = #"0";
//}
//counter=0;
//count.text = #"0";
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(void)saveCount {
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] objectForKey:#"saveCount"] ;
NSMutableDictionary *mDict = [dict mutableCopy] ;
[mDict setObject:#(count) forKey:#(itemIndex)] ;
[[NSUserDefaults standardUserDefaults] setObject:mDict forKey:#"saveCount"] ;
[[NSUserDefaults standardUserDefaults] synchronize] ;
}
//- (void)dealloc {
// [self saveCount] ;
//}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before
navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
You can call [self saveCount] in dealloc method or viewWillDisappear:.
- (void)dealloc {
[self saveCount] ;
}
or:
- (void)viewWillDisappear:(BOOL)animated {
[self saveCount] ;
}
Update:
It seems that you want to keep the count for every item in your list. In order to achieve this, you must save the all the count in NSUserDefaults with its index, I recommend you to save NSDictionary which use index as its key and count as its value. So when you push into the RowCounterViewController, you should tell the index number of the item, so you can get its count in NSUserDefaults. When you leave the view controller, save its count to the index.
The code below is used to fetch the count for itemIndex, you should tell RowCounterViewController the item index before push or present it.
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] objectForKey:#"saveCount"] ;
NSUInteger count = [[dict objectForKey:#(itemIndex)] unsignedIntegerValue] ;
The code below is used to save the count for itemIndex.
-(void)saveCount {
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] objectForKey:#"saveCount"] ;
NSMutableDictionary *mDict = [dict mutableCopy] ;
[mDict setObject:#(count) forKey:#(itemIndex)] ;
[[NSUserDefaults standardUserDefaults] setObject:mDict forKey:#"saveCount"] ;
[[NSUserDefaults standardUserDefaults] synchronize] ;
}
and next bit of code in the list view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showDetail"]) {
// you can fetch the selected index path in this way.
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RowCounterViewController *vc = (RowCounterViewController*)[segue destinationViewController];
// the currentIndex is the itemIndex I mentioned above.
vc.currentIndex = indexPath.row;
}
}
if you have more than one counter, you need an array to store all counter , and use UITableViewDelegate and UITableViewDatasource to present counters for rows in the tableview.
save the array to a file with this method
[array writeToFile:arrayFileName atomically:YES];
or you can use anything you can come up with to store it in
dealloc or viewWillDisappear
if you want to communicate between your tableview and RowCounterViewController, say RowCounterViewController passing the counter number back to tableview and present it in the related item, you can use delegate or notification.
that's something you need to learn in the future.
you can find some demo about it.
good luck.
I have a tableview that show saved data, but when I try to go from selected cell, it doesn't show the data that's been saved. It try this
I used NSUserDefaults to save data in other view (another .m file)
- (IBAction)saveCourseDetail:(id)sender
{
NSMutableDictionary *courseDictionary=[NSMutableDictionary new];
[courseDictionary setObject:courseName.text forKey:#"courseName"];
[courseDictionary setObject:courseDescription.text forKey:#"courseDescription"];
[courseDictionary setObject:classRoom.text forKey:#"classRoom"];
[courseDictionary setObject:teacherName.text forKey:#"teacherName"];
[courseDictionary setObject:buildingName.text forKey:#"buildingName"];
[globArray addObject:courseDictionary];
NSUserDefaults *savedData=[NSUserDefaults standardUserDefaults];
[savedData setObject:globArray forKey:#"array"];
// [savedData setObject:courseDictionary forKey:#"courseDictionary"];
[savedData synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CourseAddedNotification" object:nil];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
CourseDetailViewController *courseDetails=[[CourseDetailViewController alloc]init] ;
courseDetails.savedDataDic = [globArray objectAtIndex:indexPath.row];
[self.navigationController pushViewController: courseDetails animated:YES];
}
My problem is that I cant go from a selected cell to the view that the data's been saved.
I personally recommend you to use core data if there is large amount of data to be stored. Using user defaults is not good to store large chunks of data. Anyways, if you want to store an array of dictionary, you have to encode it and to read the array you have to decode it like this
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arr = ... ; // set value
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[defaults setObject:data forKey:#"theKey"];
Load:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:#"theKey"];
NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];
The element in the array implements
#interface DictionaryItems : NSObject<NSCoding> {
NSString *value;
}
Then in the implementation of CommentItem, provides two methods:
-(void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:value forKey:#"Value"];
}
-(id)initWithCoder:(NSCoder *)decoder
{
self.value = [decoder decodeObjectForKey:#"Value"];
return self;
}
Hope it helps :)
So I have a double picker where the user sets each wheel to what they want, and then press a button which will display their choice. Easy clean and simple, but I want to store that data for later so that it does not disappear after they close the app and re-open it again. Now, I know how to do it with a datePicker, but not a doublePicker. So my question is how do I tweak my code from saving and retrieving a datePickers info into a doublePicker?
Here is my code for the datePicker:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// Pulling the date out of my picker
NSDate *selectedDate = [self.datePicker date];
[defaults setObject:selectedDate forKey:#"DatePickerViewController.selectedDate"];
And then retrieving it again:
- (void)viewDidLoad
{
[super viewDidLoad];
// Get the date. I'm going to use a little shorthand instead of creating
// a variable for the instance of `NSUserDefaults`.
NSDate *storedDate = [[NSUserDefaults standardUserDefaults] objectForKey:#"DatePickerViewController.selectedDate"];
// Setting the date on the date picker. I'm passing `NO` to `animated:`
// because I'm performing this before the view is on screen, but after
// it has been loaded.
[self.datePicker setDate:storedDate animated:NO];
}
Any help would be appreciated, thx :)
In your UIPickerViewDelegate:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:row forKey:[NSString stringWithFormat:#"DoublePickerViewController.%i", component]];
}
To get the data out:
for (int i = 0; i < doublePicker.numberOfComponents; i++) {
NSInteger *storedInteger = [[NSUserDefaults standardUserDefaults] integerForKey:[NSString stringWithFormat:#"DoublePickerViewController.%i", i];
[doublePicker selectRow:storedInteger inComponent:i animated:NO];
}
For storing multiple values:
In your UIPickerViewDelegate:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *oldArray = [defaults arrayForKey:[NSString stringWithFormat:#"DoublePickerViewController.%i", component]];
//Make a mutable version so we can change it
NSMutableArray *newArray = [oldArray mutableCopy];
if (!newArray) {
newArray = [NSMutableArray array];
}
//Add The Latest Row To The end of the array. We wrap it in an NSNumber so it can be in an array
[newArray addObject:[NSNumber numberWithInteger:row]];
//If you want to have a limit on the amount of values that can be stored then use this
int maxValues = 5;
if (newArray.count >= maxValues) {
//Remove the oldest object
[newArray removeObjectAtIndex:0];
}
[defaults setObject:newArray forKey:[NSString stringWithFormat:#"DoublePickerViewController.%i", component]];
}
To get the data out:
for (int i = 0; i < doublePicker.numberOfComponents; i++) {
NSArray *storedIntegers = [[NSUserDefaults standardUserDefaults] arrayForKey: [NSString stringWithFormat:#"DoublePickerViewController.%i", i];
//I don't know what values you want, but to get an int out of the array:
//int integer = [(NSNumber *)[storedIntegers objectAtIndex:0] intValue];
//And to set the row for a component:
//[doublePicker selectRow:storedInteger inComponent:i animated:NO];
}