I am experimenting with the splitViewController, introduced for iPads and am stuck at a point. I have a button on my detail view of the splitViewController, clicking on which a modal view opens. Now I want to change the positoning of UI controls on the detail view when the modal view gets dissmissed.
A pretty obvious way of doing this would be to catch the view transition in the ViewWillAppear method of the detailView. But it's not being called in this case. I remember facing the same problem in tabBarController where [tabBarController viewWillAppear:animated] was needed to be set before viewWillAppear of views in each tab item got called. I tried doing this with the splitViewController as well, but this doesn't seem to to work.
Any Ideas??
If the positioning is required due to an action that occurred in the modal view, you should use an explicit delegate callback. That will allow you to clearly specify the control flow and resulting behaviour of your app.
You should then define a protocol that has specific methods that carry pertinent information about the action taken. When the action occurs in the modal, perform the protocol method on the delegate, and it can react to that event (for you it seems to be a re-layout of button positioning).
To get an idea of the methods that are abstract enough to handle generic modal behaviour, look at UIAlertViewDelegate protocol. Here the delegate will get an alertViewCancel: message when the user decides to take no action, or alertView:didDismissWithButtonIndex: when they selected one of the options presented to them.
That is a good start for how to define the protocol.
If you need a many view controllers to react to the action taken in the modal, say a Sign In modal, then a better mechanism is notifications.
Related
I want to make some switches on a view controller dependant on whether a user has allowed / not to register for notifications. The very first time this view loads and
appDelegate.registerForNotifications(UIApplication.sharedApplication())
is called in ViewDidLoad the AlertController is shown and the viewController completes its layout as expected, hence missing any dependency on if the user allows / not the notifications.
I know I could present the register alert as soon as the app launches, but I want to wait to ask for permission in this view controller as it is only relevant in this view and not others before it.
Is there any way of capturing the UIAlertAction before viewWillAppear is called?
Is there any way of capturing the UIAlertAction before viewWillAppear is called?
No, nor should there be. You do not do things before viewDidLoad, viewWillAppear, and so forth. Your job in these methods is to respond as needed (for example, by doing initializations) and then get out of the way, just as fast as possible. You must not delay the appearance of the view controller in any way.
Instead, rethink your architecture.
The view controller's view is going to appear. So just let it! If you are concerned that, say, a button should not be enabled if the user disallows registration, then start out with the button disabled and only enable it after you know (or learn in the alert action's handler) that it is now allowed.
Or, if this view controller depends entirely on the user allowing registration, move registration to an earlier phase of your app, and don't even allow the user to come here without it!
New to iOS, kindly bare if the question is very basic? When I press the tab button multiple times, it is not invoking viewWillAppear function? If I am wrong, then which function gets invoked, each time a tab button is pressed being on the same tabview?
You are correct, viewWillAppear is a little special, it is usually called automatically but in some cases including when you are adding a view controllers view manually (view addSubview:), and also when adding this as a view controller to a UITabViewController it doesn't get messaged.
This however is only for the root view, as you navigate (maybe with a navigation controller) back and forth, that root view's viewWillAppear will get triggered as some point.
In short, if you need to implement something in viewWillAppear in these cases, you should message it yourself when you know it's going to be presented. You can handle this case in your view controller, check out the following article about the matter:
http://www.touchthatfruit.com/viewwillappear-and-viewdidappear-not-being-ca
Good luck.
I have a page-curl modal working. But I need to be able to track when the user hits the page curl to close the page so I can see some vars. Is there an event I can listen for or some delegate where I will get a call to tell me of this event?
Many Thanks
-Code
You'll see a lot of advice around here advocating the use of performSelector:withObject:afterDelay: with a small delay to take action after the dismissal of modal views, but I think there may be a better approach.
Generate the notification yourself. If you control the view that's being presented modally, use the viewDidDisappear: method in its controller to generate an "I was dismissed" notification which you then subscribe to in your main view controller (or somewhere else). viewDidDisappear: is called after the page uncurl animation completes upon dismissal of the modal view.
In the worst case, where the view may be presented modally or otherwise, you can keep state on the presenting view controller to ensure that the notification actually corresponds to a modal view dismissal.
For completeness' sake, viewWillDisappear: is generated when the page decurl transition starts.
I'm a newbie doing Objective-C, coming from Flex/Actionscript development.
I have an iPhone app with an UIApplicationDelegate conforming delegate called MyAppDelegate - it has a UIWindow.
Instead of adding buttons, labels and whatnot directly to the window, I guess I'm supposed to make a child class of UIViewController for every screen I wanna make. When that screen should be displayed, I add the respective controller's view to the window as a subview. When another screen should be displayed, I should pop off any other view from the window and add the new view. Hope I got everything correct so far...
My intent is to make each view controller only know about its own things, so let's say I wanna call view A from view B, in ActionScript I'd add a button in A firing off an event which would be caught in view A's owning object (could be the application), which could take proper action (remove view A, instantiate view B and display it).
How do I do this in Objective-C?
A UIControl, such as UIButton, can have any number of event listeners registered with:
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
The target would be the view controller you want to receive the method, and the action is the method you want called. For a button, events is usually just UIControlEventTouchUpInside. If the target is nil, the event will pass up the responder chain until a responder implements the action. If you pass #selector(buttonClicked:) then the target should have this method:
-(IBAction) buttonClicked:(id)sender;
The sender will be the button that was clicked. IBAction is equivalent to a void return type. You can bind the action in Interface Builder if you prefer that to doing it programmatically.
When another screen should be
displayed, I should pop off any other
view from the window and add the new
view.
This is basically correct, but usually you use a meta view controller like UINavigationController to manage view controllers. Even if you do not use the UI that a meta controller might present, it is convenient to have view switching managed for you.
If you're coming from Actionscript you may be interested in looking at the PureMVC framwork for objective C. Using PureMVC you'll create a combination of Mediators, Commands, and Models for application interaction.
With PureMVC you register notification with the facade, and you define listeners in your mediators to respond to these. This is pretty close to the event model you're used to in Actionscript. (At my last job we added some categories to the UIResponder to remove some of the cruft in doing this). If you're creating a considerably sized application, then I would recommend you give it a look; it certainly helped us keep everything manageable.
If you don't want to pull in a third party library then you should define your view manipulation code in your MyAppDelegate and use the [UIApplication sharedApplication] class method to access the globally shared instance.
So the the problem is that when someone touches the back button on the UINavigationControler, I would like to run some code to update the datasource.
The problem is that i cant seem to find the right delegate to do it. only these are available on the nav controller delegate, and i want the 'didfinishshowing' type method.
– navigationController:willShowViewController:animated: optional method
– navigationController:didShowViewController:animated: optional method
The next best place i thought was the nav bar but when i try that.
Terminating app due to uncaught
exception
'NSInternalInconsistencyException',
reason: 'Cannot manually set the
delegate on a UINavigationBar managed
by a controller
This makes sense retrospectively, as you don't want some hacker messing around with the internals of the nav controller and stopping it from working.
This must is a common problem, and i have missed something simple.
Just so we're clear: view A is the starting point. User taps something and you slide right to view B. User taps the back button and you're going from B back to A and you want to do something as a result of the 'back' action.
There are three ways to do it (and on neither do you have to go near the navigationController -- these apply to the underlying viewControllers themselves):
As dmercredi suggests override viewWillAppear on view controller A so when you're heading back to it, it refreshes itself. Problem is that viewWillAppear is also called when A is called the very first time. So you'll have to set some sort of flag to distinguish between the first viewWillAppear and any subsequent ones when returning from B.
Override viewWillDisappear on view controller B and do your refreshing there. This will only get called when B is about to go away. If there's something on B that goes one level deeper or brings up a modal dialog on top, viewWillDisappear is going to get called so again you'll have to distinguish between the coming and the going.
Decouple the various views and use the delegate pattern. View controller A sets itself as a delegate of B and when B updates something it invokes the delegate method, so A is notified of the change and can update whatever it needs to. You can invoke the delegate method any time the user makes a change inside B or override viewWillDisappear and just do it one time on the way out.
Add your refresh code to the viewWillAppear:(BOOL)animated method on the view controller that is about to be displayed. In your case, that is the view controller that's already on the navigation stack.