manage Tableview properties from setting view controller - objective-c

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.

Related

get root view's methods to be called on root views object when called from child views

RootView has a label, and a method which updates this label. There is also a number of views located inside container views. Any of these should be able to update this label. They do so by calling this method. I am able to call it from these subview-controllers, but it does not update the label. Calling it from the root view does update the label. I've read about delegates, but I haven't been successful in adding it. Is that the way I should do it, by delegates?
this is from the subView. Please correct me If i am wrong, but I suppose it doesn't work because it creates a new rootView, and not using the old one.
Storyboard image here: http://i58.tinypic.com/2e17j1e.jpg
[super viewDidLoad];
self.rootView = [[RootViewController alloc]init];
[self.rootView displaymessage:#"Hello sent from child"];
What you are doing is wrong. Calling self.rootView = [[RootViewController alloc]init]; actually creates a new instance of RootViewController. Fortunaltely, UIVIewController holds a property called presentingViewController which represents the viewController which presented the child.
self.rootView = self.presentingViewController;
[self.rootView displaymessage:#"Hello sent from child"];
Or simply,
[(RootViewController*)self.presentingViewController displaymessage:#"Hello sent from child"];
Here's the link to Apple's documentation regarding the presentingViewController property.
EDIT:
Looking at the complexity, of your storyboard, I would suggest you use NSNotificationCenter to change the label. Here's how:
In RootViewController's viewDidLoad, paste this code:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(displaymessageFromNotification:) name:#"rootDisplayMessage" object:nil];
Then add the following method to RootViewController's .m file:
-(void)displaymessageFromNotification:(NSNotification*)notification
{
NSDictionary *data = [notification userInfo];
NSString *message = [data objectForKey:#"message"];
[self displaymessage:message];
}
Then, from anywhere in the code where you want to display a message in the RootViewController, use these lines:
NSDictionary *dicMessage = [NSDictionary dictionaryWithObject:#"Your message here." forKey:#"message"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"rootDisplayMessage" object:nil userInfo:dicMessage];
try some code like this:
//rootView add child controllers place
ChildControllers *childVc = [[ChildControllers alloc] init];
childVc.rootView = theRootViewAddChildVc;
//child controller.h
#property (nonatomic, weak) RootViewController * rootView;
//child controller.m update label place:
[self.rootView updateLabel];

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.

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/

Refresh UIView from Subview

in my universal app, I have a UIControl View inside of a UIScrollView.
On pressing a setup Button, I add another View as subview, like this:
SetupController *setupview = [[SetupController alloc] initWithNibName:#"SetupView-iPad" bundle:nil];
[mainview addSubview:setupview.view];
The subview is displayed like expected.
In this SubView I have some Buttons, which allows the user to switch between settings.
The performed actions are saved in a local Database.
The problem is: On ButtonClick in the SubView, I have to refresh the mainview, to apply the changes. I've tested many ways to make this happen:
In the Subviews class:
[self.parentViewController.view setNeedsDisplay];
No result.
Then I tried to refresh the mainview by notification:
I added this to my subviews classfile, in the function that changes my settings.
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:#"setEmoticon_NOTIFICATION" object:self];
}
Then I added the observer to my mainview in the ViewDidLoad method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshView) name:#"setEmoticon_NOTIFICATION" object:nil];
and created the function for this observer:
-(void)refreshView{
NSLog(#"Notification!");
[self.view performSelectorOnMainThread:#selector(setNeedsDisplay) withObject:nil waitUntilDone:YES];
}
In Log I get the "Notification!" on changing the settings. But whatever I try, no refresh.
I tried setNeedsDisplay, resignFirstResponder, [self viewDidLoad:] , but still nothing works.
Any ideas how to refresh my mainview?
What exactly do you need to do when refreshing? Do you need to redraw elements on screen, etc?
Do you have a custom refresh method you've implemented in the main view? I would setup a custom protocol in your sub view and before adding the sub view to the main view, I would add the sub view's delegate as the main view
SetupController *setupview = [[SetupController alloc] initWithNibName:#"SetupView-iPad" bundle:nil];
setupView.delegate = mainview;
[mainview addSubview:setupview.view];
Then handle the refresh in the main view call back.
Hell yeah, seems like I found a solution to refresh my superview from subview.
You only have to add the following code snippet to the desired action/method, that should perform the refresh, in your subview controller.
for (UIView* next = [self.view superview]; next; next = next.superview)
{
UIResponder* nextResponder = [next nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]])
{
[(UIViewController*)nextResponder viewWillAppear:YES];
}
}
In this case I reload the superview, using it's viewWillAppear method.
You can replace it with any method that fits your needs (viewDidLoad, viewWillDissapear etc.).
It may not be the best way to do this, but it works great for me.
Thanks for your answers. ;)

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..