Passing data to parent viewcontroller from tableviewcontroller - iphone

so currently I have a tableviewcontroller that gets presented modally above the loginviewcontroller if a certain case occurs, then once the user presses on a cell, the title should be passed to the parent view controller.
So picture (A) being the parent - the mainview, (B) being the login, and (C) being the tableviewcontroller...
C sits on top of B which sits on top of A, NOT THROUGH A NAVIGATION STACK BUT THROUGH A MODAL STACK. So C doesn't have any reference to A other than this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self Login];
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
When a user presses on a cell, the view then goes straight to (A), so from (C) to (A) skipping (B). This is what I wanted... But the issue is how do I pass data to (A) without having to instantiate (A) again? I mean instantiate again by, it's already instantiated in the beginning of the app launch and (B) is presented over (A) if the user isn't already logged in. So (A) is ALWAYS loaded, but how do I pass data to it?
If there is more information that should be provided, please inform me and i'll edit ASAP.
EDIT
I looked at a few more questions that related to this one, would I use NSNotification? Or delegation? I attempted to use both but none really worked... I refered to my previously answered question on delegation to implement the delegation solution but I have to instantiate (C) in (A). But I already instantiate (C) in (B). Heres the delgation solution I was gonna use but didn't: Delegation. Here is the Link for the NSNotification solution i attempted to use:
Put this in your parent controller in viewDidLoad
- (void) viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self makeCallbacks];
// get register to fetch notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(yourNotificationHandler:)
name:#"MODELVIEW DISMISS" object:nil];
}
//Now create yourNotificationHandler: like this in parent class
-(void)yourNotificationHandler:(NSNotification *)notice{
str = [notice object];
}
Put following to your child class where
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"%#", cell.textLabel.text);
// cell.textLabel.text logs this -> EDF ENRS
[[NSNotificationCenter defaultCenter] postNotificationName:#"MODELVIEW DISMISS" object:cell.textLabel.text];
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
When I log 'str' (null) is printed
Now the issue is when I log str outside of the notification later in viewWillAppear, it keeps returning null.... It only DOESNT return null when I log str within the notification method... I'll try to figure this out later but the primary issue is i guess solved using NSNotificationCenter.

The problem behind the NSNotificationCenter not working, is with it being declared in viewDidAppear.
Make sure, its appearing and written somewhere where the changes are made , everytime the screen is displayed, like viewWillAppear.
And then, make the necessary changes to the parentView, in this viewWillAppear method.
Also, be sure to remove the notifications, inside the methods that are called, when those notifications are posted.

Your notification code should work, unless you're removing the observer at some point in time.
Delegation will also work, you just need to pass the delegate down through the instances (A pass to B, B pass on to C).
Blocks are another option, similar to delegation but with a block type instead of a protocol. Again, passed down through the instances.
Blocks are probably your best option. Notifications should be used where multiple items are interested in the event and those items aren't necessarily associated with the trigger of the event. That said, notifications are the 'least code option'.

Related

Reload data for UIView\UIViewController?

When I am moving the buttons on the screen from a function, [self makeButtons], nothing happends unless I push a viewcontroller, then pop back. Is there a reload data function for ViewController, as it is for UITableViews? I am using NavigationController, and I am adding subviews to a UISrollView and moving other buttons on the screen. The method is called after fetching data with ASIFORMHTTPRequest.
EDIT: I am sorry I didn't specify more.
I have a method that is sending a [request startAsynchronous] (ASIFORMHTTPrequest).
I have a NSMutableArray containing all my buttons. When the request is done, a method called doneGettingRequest, which looks like this.
- (void) doneGettingRequest(ASIFORMHTTPRequest *) request {
[self removeButtons];
}
which is calling this method;
- (void) removeButtons {
NSLog(#"Removing buttons!");
for (UIButton *button in gameButtons) {
[button removeFromSuperview];
}
Everything works when I go to another view, then back again. The problem is it won't refresh if THAT view is being shown when the method is called (which will happend almost always). The gameButton is a NSMutableArray containing buttons that are currently being showed. When I am done removing them, I want to add some other buttons. The problem is, the buttons is not removed when the removeButtons is called. The message "Removing buttons!" however, is shown, so the method is being called.
Any suggestions?
Thanks
You can put your logic in your viewWillAppear.
This method is called before the receiver’s view is about to be added to a view hierarchy and before any animations are configured for showing the view.
You can override this method to perform custom tasks associated with displaying the view.
If you override this method, you must call super at some point in your implementation.
Have you tried
[view setNeedsDisplay];

Reload table in model class

I have one view class. In this class I have table view. In my model class I make Asynchrous ASIHTTPRequest. And I want when the operation is successful to reload data in table view. Which is good way to do this. I consider add one UITableView property to model class and use reload it. Is this good approach?
Make your model post notification to NSNotificationCenter and your table view register for this notification and reloadData when it receives one.
Inside your model, when changes occur:
[[NSNotificationCenter defaultCenter] postNotificationName:#"uniqueNotificationName"
object:self];
Inside your table view controller, register for notification, for example in viewDidAppear:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(methodToCall:)
name:#"uniqueNotificationName"
object:nil];
And implement methodToCall:
- (void)methodToCall:(NSNotification *)notification {
[self.tableView reloadData];
}
When you are done with the table view, for example in viewWillDisappear: you need to unregister for notifications:
[[NSNotificationCenter defaultCenter] removeObserver:self];
I think your model should inform te controller that is has updated (or other way around using KVO). The controller should then message the view to reload.
When you want to reload table view you can use one of this approaches:
If you want to reload all displayed cells then you can just call [tableView reloadData];. Notice, that only displayed cells will be reloaded. So if you have, for example, 10000 cells and only 10 of them are displayed, then only for 10 cells will be recalled method cellForRowAtIndexPath:.
If you want to reload particular cells then you should call following method of UITableView : - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation and provide in first operand list of cells to reload.
If you want to reload whole section (or number of sections) of UITableView then you should call - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation and provide in first operand list of sections to reload.
Hope, it will help you to choose appropriate variant.
use synchrous and gcd to load data.
when receive data call [self reload] on main queue

Why isn't viewWillDisappear or viewDidAppear being called?

I have a UINavigationController with a UITableView as my main menu. User clicks on a cell and a new view is pushed on the stack. In one case I push another UITableView that needs a toolbar. So on that 2nd tableView's init I setup the self.toolbarItems property with the correct items. But then I need to call [self.navigationController setToolbarHidden:NO animated:YES]; So it makes sense to call this in the viewDidAppear or viewWillAppear method. But I put it in those methods and find out (Also via NSLog) that they never get called. The same goes for hiding it in viewWillDisappear or viewDidDisappear. Why don't these methods get called? Where should I be doing this hiding/showing of the toolbar then?
I have noticed behavior where if a parent controller (like UINavigationController or UITabBarController) never get's viewWill/DidAppear called on it, it won't call it on the child controllers either. So make sure that in the code where you create the parent controller, you call viewWillAppear, show it, then call viewDidAppear. Then it should make those calls on it's child controllers as is appropriate.
Double check the parent controller is having those methods called, and call them yourself if they are not.
Yes Its true
you can do this by first write this code in
- (void)viewDidLoad {
self.navigationController.delegate = self;
}
And then write the code which you want to write in viewWillAppear
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
if ([viewController isKindOfClass:[self class]]) {
//write your code here
}
}
Although you solved your problem, in case someone comes along in the future another problem could have been that you forgot the animated: argument to either method - that is to say, the format of the method needs to look like:
- (void) viewWillAppear:(BOOL)animated
i noticed the same issue in iOS7. When i'm using both tab bar (2 buttons A, B) and navigation controller.
A has two views. One with tableview and second displays data according to the selection from the table view.
B has is the only view.
Button which is refer to another separate view D, placed in both tab bar views (A & B) and in both views of A.
Problem arises when i click the button from tab item B, viewWillAppear and viewDidLoad not called.
So i solved this issue by presentModalViewController:animated: and to come back i used dismissModalViewControllerAnimated:, just when i go to view D from tab item B.

iPhone updating view

I am trying to solve a problem considering update of an view.
First I switch to another view by using:
- (void)viewSettings {
settingsViewController = [[SettingsViewController alloc] initWithNibName:nil bundle:nil];
[[self viewController] presentModalViewController:settingsViewController animated:YES];}
Which is a delegate Method called by
ivaskAppDelegate *mainDelegate = (ivaskAppDelegate *)[[UIApplication sharedApplication] delegate];
[mainDelegate viewSettings];
I switch back by calling another dellegate method
- (void)settingsDone {
[[self viewController] dismissModalViewControllerAnimated:YES];}
When I return to my view I now want to update a label, can you explain how to do it?
I use NIB-files which have a controller class and a view class connected in the identity inspector.
/N
Although I heavily suggest delegation in this case there are two other options, that come to my mind: Notification and KVO.
Notification
Whenever settings are changed the settings view controller could post a Notification, to let other parts of the app know about this change. Posting a notification is easy as:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"SettingsChangedNotification" object:theNewSettings];
Every object that somehow want to know about a settings change can subscribe to that notification via:
//Subscribe (in viewDidLoad or init)
[[NSNotificationCenter defaultCenter]
addObserver:self selector:#selector(settingsChanged:)
name:#"SettingsChangedNotification" object:nil];
// Called when a "SettingsChangedNotification" is posted
- (void)settingsChanged:(NSNotification*)settingsChangedNotification {
id newSettings = [settingsChangedNotification object];
}
//Unsubscribe (in viewDidUnload or dealloc)
[[NSNotificationCenter defaultCenter]
removeObserver:self name:#"SettingsChangedNotification" object:nil];
See Notification Programming Topics
If you are trying to manages UserDefaults with your settingsViewController there's an even better way. Just set the values on the sharedUserDefaults and the app will post a NSUserDefaultsDidChangeNotification notification all on it's own. All objects that depend on user settings could subscribe to that notification, and after it's posted reread the userDefaults.
See
NSUserDefaults Class Reference
User Defaults Programming Topics
Key-Value Observing (KVO)
Your rootViewController could observe changes of an object, which it needs to synchronize with, by Key-Value Observing.
One object registers itself as observer for keyPaths on other objects by sending them a addObserver:forKeyPath:options:context: message. The object is informed about changes via the observeValueForKeyPath:ofObject:change:context: callback method. KVO could sometimes be difficult the get right, because you have to ensure, that you register and unregister the same number of times, before an object gets deallocated.
See Key-Value Observing Programming Guide
Conclusion
Please refrain from using "global" variables on your app-delegate. There's enough possibilities to do better. The sooner you dive into them, the better code you will write.
You will need to set up a delegate that is implemented in your main view controller (where you need to have the label updated), and that is called from your settings view controller. I have a blog post that describes how to do this:
http://www.dosomethinghere.com/2009/07/18/setting-up-a-delegate-in-the-iphone-sdk/
You would make your view controller conform to a protocol that your create. In this case it may be named SettingsDelegate. That protocol contains one message - (void)didFinishSettingsWithSomeNewValue:(id)newValue;
The Settings Controller has a delegate instance variable and a property of type id<SettingsDelegate>
Before you present the settings viewController you assign the parent view controller to the delegate property. In the settings view controller you send a didFinishSettingsWithSomeNewValue: message to your delegate (the parent view controller) along with some new value. The parent view controller implements that method and inside the implementation can dissmiss the modal controller and update any views on itself.

iPhone UITableView trigger a parent method?

Okay, so I'm working on an iPhone app with a preferences-esque section where there's a base TableView, which has one section, and several rows of customized cells to hold a name and a value label. Clicking on a row brings up another View, which allows the user to pick from a list (another TableView), and select an item.
All of these TableViews are done programatically. The base TableView has a property that holds a Controller instance for each of the "pick from the list" Views. Each of the "pick from the list" views has a property called chosenValue that has the currently-selected option. I've gotten the UI to handle the didSelectRowAtIndexPath to update the chosenValue property and then "pop" the view (going back to the main TableView). But, even though the main TableView's cellForRowAtIndexPath method references the chosenValue property of the subview that's held in a property, the view doesn't update when an item is selected. In short, how can the sub-view trigger a reloadData on the parent object, after it "pops" and unloads?
You could realize this using the notifications or delegation, for example.
Using notifications your first view controller has to register for a notification like this:
…
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(methodToBeCalled:)
name:#"myNotification"
object:nil];
…
- (void)methodToBeCalled:(NSNotification *)notification {
[self.tableView reloadData];
// do something else
}
Then you can raise a notification in your second view controller like this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object:nil];
Using delegation you could implement a property for a delegate on the second view, set that to self before pushing the second view controller and call a method on the delegate when needed.
You should define a delegate for that.
In the child TableView define a protocol:
#protocol ChildViewControllerDelegate
- (void) somethingUpdated;
#end
Also define a property for a delegate implementing this protocol:
id <ChildViewControllerDelegate> delegate;
Then in Parent all you need to do is to define a method somethingUpdated, assign itself (parent) to a delegate property in Child and call this method in Child view when you need to update it. Implementation of somethingUpdated in Parent can be different based on what you're trying to accomplish.
flohei thanks so much for posting this!! I got it working with NSNotificationCenter! I have a child view calling a method in a parent view now! Awesome. #sha I tried your protocol way also but could not figure it out. Perhaps a more illaborate discription would help a noob like me. I have been reading that your protocol way is the better cause I guess less code? So i'll try to keep learning it. Thanks to you both!