NSUserDefault and Switches - objective-c

I use NSUserDefaults to save a switch on/off and so far it is good. It remembers the switch position in next session.
Now to the thing which I do not understand.
I use the same switch (with the same name)in another view, let´s say a flip view which is pushed in from the first view. If I change the switch in the first view it is automatically changed in the flip view.
But the other way round, if I change it in the flip view it is not changed in the first view when I go back. Only if I restart the application the first view is also changed.
How can I solve this to be changed at the same time? Or kind of refresh the first view without need to restart.

Your first view is not refreshed, as it is not initialized again. If you using ViewControllers you could update your switch in viewWillAppear (if isViewLoaded).

You should observe NSUserDefaults changes in views that are interested in the changes.
You can use the following code to observe the changes:
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:#selector(defaultsChanged:)
name:NSUserDefaultsDidChangeNotification
object:nil];
And implement the defaultsChanged method:
- (void)defaultsChanged:(NSNotification *)notification
{
NSUserDefaults *defaults = (NSUserDefaults *)[notification object];
id value = [defaults objectForKey:#"keyOfDefaultThatChanged"];
self.something = [(NSNumber *)value intValue]; // For example.
}
Don't forget the remove the observer when your view closes (perhaps in dealloc):
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self];

Related

Setting UIScrollView's Position from another ViewController

I am trying to set the position of a UIScrollView by using contentOffset as such:
- (void) navigateToTableViewPosition:(CGPoint)contentOffset {
NSLog(#"Position set method gets called...");
NSLog(#"%#", NSStringFromCGPoint(contentOffset));
[mainScrollView setContentOffset:contentOffset animated:YES];
}
I call this method from another view controller before I dismiss it, and everything checks out. I pass the argument correctly, and the method gets called (checked it with NSLog), but the scroll view does not move...
What is funny is that when I call this method from the view controller, in which it is located, it works fine. Only when I call it from another view controller, it stops working.
Just for future reference, here is the calling method:
MainViewController *mainView = [[MainViewController alloc] init];
[mainView navigateToTableViewPosition:contentOffset];
Content offset is a CGPoint I set beforehand. It doesn't matter here; besides, it gets passed correctly anyways.
Try this, You have to send notification from other viewcontroller when you want to change ..
[[NSNotificationCenter defaultCenter] postNotificationName:#"changepostion" object:NSStringFromCGPoint(CGPointMake(contentOffset.x, contentOffset.y))];
in mainviewcontroller
-(void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(navigateToTableViewPosition:) name:#"changepostion" object:nil];
}
- (void) navigateToTableViewPosition:(NSNotification *)notification
{
contentOffset =CGPointFromString([notification object]);
NSLog(#"Position set method gets called...");
NSLog(#"%#", NSStringFromCGPoint(contentOffset));
[mainScrollView setContentOffset:contentOffset animated:YES];
}
You can't set the properties of a view which is not visible. If you are using iOS5+ you can implement the offset setting in the completion in the view dismiss completion block.
Use delegate for backward messaging in view controllers.
Refer Basic Delegate Example link for more reference.
Your are making new instance of viewcontroller which will call method but will have no effect.

NSUbiquitousKeyValueStoreDidChangeExternallyNotification is not called sometimes

I wrote code for iCloud key-value Store
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyValueStoreChanged:)
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
object:nil];
LOG(#"sync");
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
When I delete app and reinstall it, observer method is called usually, but sometimes not called.
Why? Just the network problem?
I had a similar issue where the NSUbiquitousKeyValueStoreDidChangeExternallyNotification wasn't triggering on the first launch after install, no matter how long I waited. Setting an initial key in the NSUBiquitousKeyValueStore seemed to solve this.
Immediately after adding the observer to the default store, I call:
[[NSUbiquitousKeyValueStore defaultStore] setString:#"testValue" forKey:#"testKey"];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
I use different keys (i.e. not testKey) for the actual data I want to sync.

manage Tableview properties from setting view controller

I have UITabBarViewController which has 2 views.
The first view has a UITableView which has 1 section and 5 rows.
The second view has a UITableView as well which has a settings options like UISwitches.
My question is how can I show and hide or remove a cell from first view by using UISwitches on the settings view? Thanks in advance.
edit
this video explain what i am trying to do (check the app view)
Press Here
you can accomplish this by using NSNotificationCenter
in your firstView you can write a code like:
-(void)viewDidLoad{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(modifyCell:) name:#"modifyCell" object:nil];
}
//make sure this is declared in your .h
-(void)modifyCell:(NSNotification*)notif
{
if (notif) {
//cellindex to modify
NSString *cellIndex = [[notif userInfo] objectForKey:#"index"];
[yourDataSource removeObjectAtIndex:[cellIndex intValue]]
[yourTableView reloadData];
}
}
in your secondView:
-(void)switchChanged
{
NSNotificationCenter *ncSubject = [NSNotificationCenter defaultCenter];
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:#"indexNum",#"index", nil];
[ncSubject postNotificationName:#"modifyCell" object:nil userInfo:dict];
[ncSubject removeObserver:self];
}
You should reload your tableview after each UISwitch change. Such as:
- you set a delegate from your UISwitch to your UITabBarViewController (or the class which controls the events)
- you should store your tableview's cells' number in a variable
- this variable will change after each UISwitch change
- after the variable change, you should reload the tableview
In the viewWillAppear method of the table view controller I would check whether the setting has been changed or not. If it has changed then I would redraw the cell by calling its the reloadData method.
Sometimes it is recommended to call reloadData through performSelectorOnMainThread:
[ self performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO]
And your data loading methods (numberOfSectionsInTableView, numberOfRowsInSection, cellForRowAtIndexPath, etc.) will have to consider the settings value accordingly.

iOS Reload All tabs in a UITabViewController programmatically

My app has an UITabViewController with 3 tabs. The first two tabs will read some data from disk and display it (done in viewDidLoad of the first two tabs).
The third tab has some kind of config information. If the user changes the config information in the third tab, i want the first two tabs to be refreshed, i.e., viewDidLoad should be re-called.
I cannot use viewWillAppear in the first two tabs, as the read from disk part is kind of intensive and I wouldn't want to do it everytime the tab is clicked. Also, I need to do some auxiliary tasks (in addition to updating the first two tabs) when the third tab data is edited, so I want to reload the tabs via viewDidLoad, while doing those auxiliary tasks.
Use NSNotifications to do this.
Since the third tab is your config settings you will probability want to be storing these in NSUserDefaults so use the NSUserDefaultsDidChangeNotification to watch for this in your viewDidLoad method and move your reloadData code into its own method.
- (void)viewDidLoad
{
[super viewDidLoad];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:#selector(userDefaultsChanged:)
name:NSUserDefaultsDidChangeNotification
object:nil];
[self reloadData];
}
Now this will trigger a call to the method userDefaultsChanged: whenever your defaults are changed, add the method as follows.
- (void)userDefaultsChanged:(NSNotification *)notification
{
[self reloadData];
}
- (void)viewDidUnload
{
[super viewDidUnLoad];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Edit: Alternative method to watch for specific default values
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:#"SomeDefaultKey"
options:NSKeyValueObservingOptionNew
context:NULL];
- (void)observeValueForKeyPath:(NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context
{
if([keyPath isEqual:#"SomeDefaultKey"])
{
// Do Something
}
if([keyPath isEqual:#"SomeOtherKey"])
{
// Do Something else
}
}
I would use -(void)viewWillAppear:(BOOL)animated. To get around the read from disk being 'kind of intensive' you could set a flag when the config changes in the 3rd tab and then only read from disk in the other tabs if that flag is set
You can use the -(void)viewWillAppear:(BOOL)animated method to trigger the refresh on the other two view controllers.
If you don't want to reload the data every time the user clicks the Tab you may use NSNotifications to trigger a refresh. See a detailed explanation at: http://www.numbergrinder.com/2008/12/patterns-in-objective-c-observer-pattern/

Cannot change views in splitviewcontroller

I am trying to change the views in a splitview controller based upon clicking a button in a modalview (a person is selecting an option). I am using notifications to accomplish this:
When the button is clicked in the modal view, it issues a notice, then closes (dismisses) itself:
[[NSNotificationCenter defaultCenter] postNotificationName:#"launchProject" object:nil];
The DetailViewController inside the split view controller is listening for this notification, and switches out the views in the SVC
-(void)launchProject:(NSNotification *)notification {
Project* secondDetail2 = [[Project alloc] initWithNibName:nil bundle:nil];
ProjectRootController* secondRoot2 = [[ProjectRootController alloc] initWithNibName:nil bundle:nil ];
self.splitViewController.viewControllers =[NSArray arrayWithObjects: secondRoot2, secondDetail2 , nil];
}
I don't understand why the views aren't switching out. Any advice on this will be welcome.
You haven't shown all the code, so I'm guessing the problem is a misunderstanding of how notifications work. It can be initially confusing, but it's very straightforward. So far, you have:
[[NSNotificationCenter defaultCenter] postNotificationName:#"launchProject" object:nil]
which is fine.
But you also need to have
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(launchProject:) // selector should be your function name, launchProject
name:#"launchProject" // notification name - must be same as what is given to postNotificatioName.
object: nil];
somewhere, like in an init function.
In other words, postNotificationName:#"launchProject" does NOT call your function launchProject. It puts a notification with the name "launchProject" into the NSNotificationCenter defaultCenter. If you're not looking for that particular notification, then nothing will happen.
Hope that helps..