Coding custom SplitViewController - when should I call viewWillAppear, viewDidAppear, etc...? - iphone

I'm writing my own SplitViewController from scratch (i.e. by subclassing UIViewController and not UISplitViewController).
It has two sub-viewControllers (one for the left panel and one for the detail right panel), to which I need to send the appropriate messages (viewWillAppear, viewDidAppear, viewWillDisapppear and viewDidDisappear).
I am already forwarding those messages when my custom SplitViewController receives them and it works fine. However I am struggling to figure out when to send them when any of the two sub-viewcontrollers is replaced by a new one, which also needs to receive those messages. I am adding the view of the new UIViewController properly but the messages are not called adequately.
My initial approach was to call them in the setter of the sub-viewControllers, calling viewWillDisappear to UIViewController about to be released and viewWillAppear to the new UIViewController set, but this one is executed before viewDidLoad and therefore I presume is wrong.
I have also seen that UIView has a method didAddSubview: that might be useful to know when to call viewDidAppear on the correspondent UIViewController.
Any help would be much appreciated!

If you want to mirror UISplitViewController, it seems best to just have dummy UIViewControllers that print out whenever each method is called.
As for your current problem of the ordering of viewWillDisappear, viewWillAppear and viewDidLoad, just do:
-(void)setSomeViewController(UIViewController newVC)
{
[oldVC viewWillDisappear];
[newVC view]; // Causes newVC to load the view,
// and will automatically call -viewDidLoad
[newVC viewWillAppear];
[oldVC.view removeFromSuperview];
[self.view addSubview:newVC.view];
//retain and release as appropriate
// other stuff you'll need to mirror, etc. etc.
}

Related

View transition without using navigation controller?

I want to implement view transition without using navigation controller like pushviewController mechanism. So, I created the view hierarchy like below.
window is superview and contains three subviews.
[window] - aViewController - bViewController - cViewController
If I want to go back to the aVewiController from cViewController, I simply allocate aViewController again like this: [[aViewController alloc] init].
Then, after 4 circulations, I got didReceiveMemoryWarning and "Program exited with status value:0" messages. Its obviously memory issue but no memory leak. Allocating viewcontroller over and over is a problem. I have no idea how to transit view in this case.
If you want your hierarchy of UIViewControllers to behave as they do in UINavigationController case you should
A) show your new ViewController with this call
//This code should be implemented in viewControllerA
[self presentModalViewController:viewControllerB animated:YES]
B) go back one step by calling
//This code should be implemented in viewControllerA
[self dismissModalViewControllerAnimated:YES]
Notes: The way you would tell viewControllerA that you want to close viewControllerB is by sending a NSNotification. The good thing about this is that if you want to go from viewControllerC to viewControllerA you simply send a notification to viewControlerA to dissmisModalViewController and it will recursively dismiss viewControllerC and viewControllerB for you.
Hope this helps
You should allocate them only once and transition between the views itself by hiding/unhiding them:
viewController.view.hidden = YES/NO;
You will need to make sure you are releasing your object after you add them to their superview. and Make sure that you are removing them from their superview when they are sent off screen....
If you want to reuse the object then you can keep them in a list and try to dequeue them like the table view does. This way you wont have to remake them each time.
If you add them to the subview and never remove them. then you will have multiple views that are no longer used that are still being referenced by their super view. therefore they will still be in memory.

UIViewController: viewWillAppear is called, viewDidAppear not

In a UIViewController subclass, I have the following methods:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// do something
myTextField.text = #"Default";
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// do something
[myTextField selectAll:self];
[myTextField becomeFirstResponder];
}
- (void)viewDidLoad {
[super viewDidLoad];
// do something
myTextField.delegate = self;
}
The NIB has been created using Interface Builder. The corresponding view controller object is pushed by the navigation controller through pushViewController.
The inteded behavior is to show a default text entry in a text field, to select the entire text and to set the text field as first responder. [Edit: I've noticed that selecting all and making first responder makes no sense as the selection would dissapear; still, I wonder why the methods behave as described next.]
However, while methods viewDidLoad and viewWillAppear are called, the method viewDidAppear is not called. Can anybody tell me why? Most questions I found on the web and here deal with both viewWillAppear/viewDidAppear are not working; I also understood that in subviews or programmatically created views these methods are not evoked; but this does not apply in case and also I wonder why one of these "lifecycle" methods is evoked and the other not.
Any idea? Thanks!
I had this issue happen to me: viewWillAppear was being called but viewDidAppear was not!
I finally figured out that this was because I had a tabBarController where I overloaded it's own viewDidAppear and forgot the [super viewDidAppear:animated];
It threw off every VC in every tab! adding that line back in fixed it for my other VC's.
Hope this helps someone!
There is one case when viewWillAppear will be called but viewDidAppear will not.
Suppose you have two viewControllers and you push from the first to the second controller. Then, using the swipe, you want to go back to the first one, both controllers are displayed at the same time and at that moment viewWillAppear will be called from the first controller.
This way, you do not finish the swipe and stay on the second controller and viewDidAppear will not be called from the first controller.
I had the same problem.
I had copy/pasted viewDidAppear to create viewWillAppear but had forgotten to change the super.viewDidAppear() call. This then seemed to stop viewDidAppear from being called.
It sounds like somewhere in your code you have missed or messed-up a call to the superclass.
The call to viewDidAppear: should always follow viewWillAppear: unless you are doing something custom, which you say you don't. I don't know why it doesn't work but here are a few ideas:
Could it be that you are doing something strange in one of the delegate methods for UITextFieldDelegate? It's unlikely that it would affect viewDidAppear: being called but it could be a culprit.
Have you loaded a lot of stuff into memory before pushing the view? I'm not sure what would happen if you got a memory warning between viewWillAppear: and viewDidAppear:.
Have you tried to do a Clean? Sometimes that can help.
In cases like these when it should work I usually create a new class and the introduce the functionality one at a the time to see if I can get it work that way. I tried your code in a new Navigation Based project where I added a new UIViewController with an outlet to the text field. Then I pasted the code from the question and it did work as expected.
This can be because you added a child view controller to your parent VC in viewDidLoad or viewWillAppear. The child's appearance prevents the call to viewDidAppear.
This is a crazy thing to do, and I only know because this was a bug in my code. I meant to add the child VC to this VC, not the parent VC.

Managing calls to objects that get deallocated when the view is backed out of

I have a view controller managed in a UINavigationController. My view controller puts up a "loading" screen, and uses ASIHTTP to asynchronously load table data. Then it renders its table, and in each cell there's an image that it uses ASIHTTP to asynchronously load. When it lands each image, it uses [self.tableView reloadRowsAtIndexPaths] to reload that row, inside which the image is fed to the UIImageView in each row.
Works great. But if I back out of the view before it's done loading, it crashes.
Where it crashes is in my -tableView:numberOfRowsInSection method, and NSZombies tells me it dies because it's asking for the -count of an NSArray called self.offers that has been deallocated.
That method looks like this:
-(NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
return [self.offers count];
}
Wrapping that return in if (self.offers) made no difference.
My -dealloc releases and sets-to-nil every one of these properties, including both self.offers and self.tableView itself. I even tried setting up a BOOL disappearing property, hitting it with YES in -viewWillDisappear, and hanging conditional behavior off that, but it doesn't work because viewWillDisappear doesn't seem to get called! Far as I can tell we're not getting ANY method called when the navigation bar pops us off.
What do I do about this?
EDIT:
Thanks to #cduhn (who's bucking for a check!), I did a bunch more looking at this. The problem has been, my -dealloc just isn't getting called when I pop this viewcontroller (nor my -viewWillDisappear nor -viewDidUnload or anything else I could use to unhook the delegation structure that's at the root of this problem).
Then I realized: THIS viewController isn't the one on the NavController stack! What's at the top of the stack right here is a shell view, just a segmented controller and a big empty UIView. I toggle the contents of that UIView between two other UIViewController subclasses depending on the state of my segmented controller. So when my view with the table on it's PARENT view gets popped from the nav stack, this CHILD I'm working on doesn't seem to get any notice about it. Which is odd, because I'm definitely releaseing it.
I can call its -dealloc from my shell controller. I could call its -viewWillDisappear too, for that matter. Is that how I should be handling this? Probably I should put something into my shell controller's viewWillDisappear like:
[[self.mainView subviews] makeObjectsPerformSelector:#selector(viewWillDisappear)];
...so that message propagates down to my child views.
Am I on the right track here, you think??
(Oh man... and that also explains why actions from inside this child table view can't get to self.navigationController! I've been puzzled about that for weeks!)
Bugs like this, where a method gets called on an object after it's been deallocated, often happen when a method gets called on a delegate after that delegate has been deallocated. The recommended practice to avoid bugs like these is to set any delegate (or delegate-like) properties of an object to nil before you release that object in the delegate's dealloc method. I know that's a confusing sentence, so I'll explain it in the context of your bug.
You have an asynchronous image download that finishes after you've backed out of your table view controller. When this happens, you're calling reloadRowsAtIndexPaths:withRowAnimation:, which results in a call to tableView:numberOfRowsInSection: on the table view's dataSource. This call is failing because that dataSource no longer exists.
The problem is that the table view object still has your controller set as its dataSource and delegate properties, even after your controller has been deallocated. The solution is to simply set these properties to nil in your controller's dealloc, like this:
- (void)dealloc {
self.tableView.dataSource = nil;
self.tableView.delegate = nil;
self.tableView = nil; // Releases as a side-effect due to #property (retain)
[super dealloc];
}
Now when your table view tries to call tableView:numberOfRowsInSection: on its dataSource, it will send the message to the nil object, which swallows all messages silently in Objective C.
You should also do the same thing with your ASIHTTPRequest's delegate, by the way.
Any time you have an asynchronous operation that calls delegate methods upon completion, it's particularly important that you set that delegate to nil. When using UITableViewController under normal circumstances you can typically get away without setting the dataSource and delegate to nil, but in this case it was necessary because you're calling methods on the tableView after its controller has gone away.
As far as I can tell, the user cannot actually "back out" of a view while the UITableView is loading it's data. The methods are not run on a thread and block the main one, also blocking UI interaction. I cannot replicate your results. Even, scrolling the table view quickly and then pressing the back button.
I suggest that the stack popping is not the problem here.

how does one make a subview firstResponder

i am trying to get a subview to become firstResponder. my understanding is that this is done in its viewDidAppear method, like so:
- (void)viewDidAppear {
[self becomeFirstResponder];
}
while overriding canBecomeFirstResponder to return YES:
- (BOOL)canBecomeFirstResponder {
return YES;
}
however, when i insert the subview in its parent view's viewDidLoad method:
- (void)viewDidLoad {
subViewController = [[SubViewController alloc] init];
[self.view insertSubview: subViewController.view atIndex: 0];
[subViewController viewDidAppear: NO];
[super viewDidLoad];
}
(i call viewDidAppear manually, because it does not get triggered automatically), the subview does not become firstResponder.
why does the subview not become firstResponder? and how can i make it firstResponder?
thanks,
mbotta
btw, this is a rewrite of my original question:
i am trying to build an iphone app where a rootviewcontroller object manages two subviews, one of which should react to a user shaking his iphone.
after some digging, i concluded the subview must be made firstResponder in its view controller's viewDidAppear method. moreover, the canBecomeFirstResponder method should be modified to return YES.
so here's what happens: i instantiate the rootviewcontroller in the app delegate. in its viewDidLoad method, i tell it to addSubView firstViewController. all of this works just beautifully.
however, firstViewController does not react to any shaking. i sprinkled some NSLogs around and found that while we DO tell firstViewController in canBecomeFirstResponder to return YES, and while we DO tell it to [self becomeFirstResponder] in viewDidAppear, in actual fact, it is not the firstResponder.
so, my questions are:
1) does a subview actually need to be firstResponder in order to react to shaking?
a) if not, how does one make a subview react without being firstResponder?
2) how does one make a subview firstResponder?
what makes this interesting is that if i perform the same sequence (canBecomeFirstResponder, [self firstResponder], motionBegan:) in a different project with only one view controller, it all works flawlessly. clearly, i must be hitting a design flaw of my own making.
thanks,
mbotta
Not 100% sure, but this could be your problem. If we could see the offending methods it might be easier.
From the Event Handling Best Practices (emphasis added by me):
If you handle events in a subclass of
UIView, UIViewController, or (in rare
cases) UIResponder,
You should implement all of the event-handling methods (even if it is
a null implementation).
Do not call the superclass implementation of the methods.
If you call the superclass methods the events are probably getting passed along to the nextResponder.
EDIT
The Event Handling Best Practices link above is dead. I couldn't find that pull quote anywhere, but Event Handling Guide for UIKit Apps seems to be the most relevant.

iPhone viewWillAppear not firing

I've read numerous posts about people having problems with viewWillAppear when you do not create your view hierarchy just right. My problem is I can't figure out what that means.
If I create a RootViewController and call addSubView on that controller, I would expect the added view(s) to be wired up for viewWillAppear events.
Does anyone have an example of a complex programmatic view hierarchy that successfully receives viewWillAppear events at every level?
Apple's Docs state:
Warning: If the view belonging to a view controller is added to a view hierarchy directly, the view controller will not receive this message. If you insert or add a view to the view hierarchy, and it has a view controller, you should send the associated view controller this message directly. Failing to send the view controller this message will prevent any associated animation from being displayed.
The problem is that they don't describe how to do this. What does "directly" mean? How do you "indirectly" add a view?
I am fairly new to Cocoa and iPhone so it would be nice if there were useful examples from Apple besides the basic Hello World crap.
If you use a navigation controller and set its delegate, then the view{Will,Did}{Appear,Disappear} methods are not invoked.
You need to use the navigation controller delegate methods instead:
navigationController:willShowViewController:animated:
navigationController:didShowViewController:animated:
I've run into this same problem. Just send a viewWillAppear message to your view controller before you add it as a subview. (There is one BOOL parameter which tells the view controller if it's being animated to appear or not.)
[myViewController viewWillAppear:NO];
Look at RootViewController.m in the Metronome example.
(I actually found Apple's example projects great. There's a LOT more than HelloWorld ;)
I finally found a solution for this THAT WORKS!
UINavigationControllerDelegate
I think the gist of it is to set your nav control's delegate to the viewcontroller it is in, and implement UINavigationControllerDelegate and it's two methods. Brilliant! I'm so excited i finally found a solution!
Thanks iOS 13.
ViewWillDisappear, ViewDidDisappear, ViewWillAppear and
ViewDidAppear won't get called on a presenting view controller on
iOS 13 which uses a new modal presentation that doesn't cover the
whole screen.
Credits are going to Arek Holko. He really saved my day.
I just had the same issue. In my application I have 2 navigation controllers and pushing the same view controller in each of them worked in one case and not in the other. I mean that when pushing the exact same view controller in the first UINavigationController, viewWillAppear was called but not when pushed in the second navigation controller.
Then I came across this post UINavigationController should call viewWillAppear/viewWillDisappear methods
And realized that my second navigation controller did redefine viewWillAppear. Screening the code showed that I was not calling
[super viewWillAppear:animated];
I added it and it worked !
The documentation says:
If you override this method, you must call super at some point in your implementation.
I've been using a navigation controller. When I want to either descend to another level of data or show my custom view I use the following:
[self.navigationController pushViewController:<view> animated:<BOOL>];
When I do this, I do get the viewWillAppear function to fire. I suppose this qualifies as "indirect" because I'm not calling the actual addSubView method myself. I don't know if this is 100% applicable to your application since I can't tell if you're using a navigation controller, but maybe it will provide a clue.
Firstly, the tab bar should be at the root level, ie, added to the window, as stated in the Apple documentation. This is key for correct behavior.
Secondly, you can use UITabBarDelegate / UINavigationBarDelegate to forward the notifications on manually, but I found that to get the whole hierarchy of view calls to work correctly, all I had to do was manually call
[tabBarController viewWillAppear:NO];
[tabBarController viewDidAppear:NO];
and
[navBarController viewWillAppear:NO];
[navBarController viewDidAppear:NO];
.. just ONCE before setting up the view controllers on the respective controller (right after allocation). From then on, it correctly called these methods on its child view controllers.
My hierarchy is like this:
window
UITabBarController (subclass of)
UIViewController (subclass of) // <-- manually calls [navController viewWill/DidAppear
UINavigationController (subclass of)
UIViewController (subclass of) // <-- still receives viewWill/Did..etc all the way down from a tab switch at the top of the chain without needing to use ANY delegate methods
Just calling the mentioned methods on the tab/nav controller the first time ensured that ALL the events were forwarded correctly. It stopped me needing to call them manually from the UINavigationBarDelegate / UITabBarControllerDelegate methods.
Sidenote:
Curiously, when it didn't work, the private method
- (void)transitionFromViewController:(UIViewController*)aFromViewController toViewController:(UIViewController*)aToViewController
.. which you can see from the callstack on a working implementation, usually calls the viewWill/Did.. methods but didn't until I performed the above (even though it was called).
I think it is VERY important that the UITabBarController is at window level though and the documents seem to back this up.
Hope that was clear(ish), happy to answer further questions.
As no answer is accepted and people (like I did) land here I give my variation. Though I am not sure that was the original problem. When the navigation controller is added as a subview to a another view you must call the viewWillAppear/Dissappear etc. methods yourself like this:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[subNavCntlr viewWillAppear:animated];
}
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[subNavCntlr viewWillDisappear:animated];
}
Just to make the example complete. This code appears in my ViewController where I created and added the the navigation controller into a view that I placed on the view.
- (void)viewDidLoad {
// This is the root View Controller
rootTable *rootTableController = [[rootTable alloc]
initWithStyle:UITableViewStyleGrouped];
subNavCntlr = [[UINavigationController alloc]
initWithRootViewController:rootTableController];
[rootTableController release];
subNavCntlr.view.frame = subNavContainer.bounds;
[subNavContainer addSubview:subNavCntlr.view];
[super viewDidLoad];
}
the .h looks like this
#interface navTestViewController : UIViewController <UINavigationControllerDelegate> {
IBOutlet UIView *subNavContainer;
UINavigationController *subNavCntlr;
}
#end
In the nib file I have the view and below this view I have a label a image and the container (another view) where i put the controller in. Here is how it looks. I had to scramble some things as this was work for a client.
Views are added "directly" by calling [view addSubview:subview].
Views are added "indirectly" by methods such as tab bars or nav bars that swap subviews.
Any time you call [view addSubview:subviewController.view], you should then call [subviewController viewWillAppear:NO] (or YES as your case may be).
I had this problem when I implemented my own custom root-view management system for a subscreen in a game. Manually adding the call to viewWillAppear cured my problem.
Correct way to do this is using UIViewController containment api.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIViewController *viewController = ...;
[self addChildViewController:viewController];
[self.view addSubview:viewController.view];
[viewController didMoveToParentViewController:self];
}
I use this code for push and pop view controllers:
push:
[self.navigationController pushViewController:detaiViewController animated:YES];
[detailNewsViewController viewWillAppear:YES];
pop:
[[self.navigationController popViewControllerAnimated:YES] viewWillAppear:YES];
.. and it works fine for me.
A very common mistake is as follows.
You have one view, UIView* a, and another one, UIView* b.
You add b to a as a subview.
If you try to call viewWillAppear in b, it will never be fired, because it is a subview of a
iOS 13 bit my app in the butt here. If you've noticed behavior change as of iOS 13 just set the following before you push it:
yourVC.modalPresentationStyle = UIModalPresentationFullScreen;
You may also need to set it in your .storyboard in the Attributes inspector (set Presentation to Full Screen).
This will make your app behave as it did in prior versions of iOS.
I'm not 100% sure on this, but I think that adding a view to the view hierarchy directly means calling -addSubview: on the view controller's view (e.g., [viewController.view addSubview:anotherViewController.view]) instead of pushing a new view controller onto the navigation controller's stack.
I think that adding a subview doesn't necessarily mean that the view will appear, so there is not an automatic call to the class's method that it will
I think what they mean "directly" is by hooking things up just the same way as the xcode "Navigation Application" template does, which sets the UINavigationController as the sole subview of the application's UIWindow.
Using that template is the only way I've been able to get the Will/Did/Appear/Disappear methods called on the object ViewControllers upon push/pops of those controllers in the UINavigationController. None of the other solutions in the answers here worked for me, including implementing them in the RootController and passing them through to the (child) NavigationController. Those functions (will/did/appear/disappear) were only called in my RootController upon showing/hiding the top-level VCs, my "login" and navigationVCs, not the sub-VCs in the navigation controller, so I had no opportunity to "pass them through" to the Nav VC.
I ended up using the UINavigationController's delegate functionality to look for the particular transitions that required follow-up functionality in my app, and that works, but it requires a bit more work in order to get both the disappear and appear functionality "simulated".
Also it's a matter of principle to get it to work after banging my head against this problem for hours today. Any working code snippets using a custom RootController and a child navigation VC would be much appreciated.
In case this helps anyone. I had a similar problem where my ViewWillAppear is not firing on a UITableViewController. After a lot of playing around, I realized that the problem was that the UINavigationController that is controlling my UITableView is not on the root view. Once I fix that, it is now working like a champ.
I just had this problem myself and it took me 3 full hours (2 of which googling) to fix it.
What turned out to help was to simply delete the app from the device/simulator, clean and then run again.
Hope that helps
[self.navigationController setDelegate:self];
Set the delegate to the root view controller.
In my case problem was with custom transition animation.
When set modalPresentationStyle = .custom viewWillAppear not called
in custom transition animation class need call methods:
beginAppearanceTransition and endAppearanceTransition
For Swift. First create the protocol to call what you wanted to call in viewWillAppear
protocol MyViewWillAppearProtocol{func myViewWillAppear()}
Second, create the class
class ForceUpdateOnViewAppear: NSObject, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool){
if let updatedCntllr: MyViewWillAppearProtocol = viewController as? MyViewWillAppearProtocol{
updatedCntllr.myViewWillAppear()
}
}
}
Third, make the instance of ForceUpdateOnViewAppear to be the member of the appropriate class that have the access to the Navigation Controller and exists as long as Navigation controller exists. It may be for example the root view controller of the navigation controller or the class that creates or present it. Then assign the instance of ForceUpdateOnViewAppear to the Navigation Controller delegate property as early as possible.
In my case that was just a weird bug on the ios 12.1 emulator. Disappeared after launching on real device.
I have created a class that solves this problem.
Just set it as a delegate of your navigation controller, and implement simple one or two methods in your view controller - that will get called when the view is about to be shown or has been shown via NavigationController
Here's the GIST showing the code
ViewWillAppear is an override method of UIViewController class so adding a subView will not call viewWillAppear, but when you present, push , pop, show , setFront Or popToRootViewController from a viewController then viewWillAppear for presented viewController will get called.
My issue was that viewWillAppear was not called when unwinding from a segue. The answer was to put a call to viewWillAppear(true) in the unwind segue in the View Controller that you segueing back to
#IBAction func unwind(for unwindSegue: UIStoryboardSegue, ViewController subsequentVC: Any) {
viewWillAppear(true)
}
I'm not sure this is the same problem that I solved.
In some occasions, method doesn't executed with normal way such as "[self methodOne]".
Try
- (void)viewWillAppear:(BOOL)animated
{
[self performSelector:#selector(methodOne)
withObject:nil afterDelay:0];
}
You should only have 1 UIViewController active at any time. Any subviews you want to manipulate should be exactly that - subVIEWS - i.e. UIView.
I use a simlple technique for managing my view hierarchy and have yet to run into a problem since I started doing things this way. There are 2 key points:
a single UIViewController should be used to manage "a screen's worth"
of your app
use UINavigationController for changing views
What do I mean by "a screen's worth"? It's a bit vague on purpose, but generally it's a feature or section of your app. If you've got a few screens with the same background image but different overlays/popups etc., that should be 1 view controller and several child views. You should never find yourself working with 2 view controllers. Note you can still instantiate a UIView in one view controller and add it as a subview of another view controller if you want certain areas of the screen to be shown in multiple view controllers.
As for UINavigationController - this is your best friend! Turn off the navigation bar and specify NO for animated, and you have an excellent way of switching screens on demand. You can push and pop view controllers if they're in a hierarchy, or you can prepare an array of view controllers (including an array containing a single VC) and set it to be the view stack using setViewControllers. This gives you total freedom to change VC's, while gaining all the advantages of working within Apple's expected model and getting all events etc. fired properly.
Here's what I do every time when I start an app:
start from a window-based app
add a UINavigationController as the window's rootViewController
add whatever I want my first UIViewController to be as the rootViewController of the nav
controller
(note starting from window-based is just a personal preference - I like to construct things myself so I know exactly how they are built. It should work fine with view-based template)
All events fire correctly and basically life is good. You can then spend all your time writing the important bits of your app and not messing about trying to manually hack view hierarchies into shape.