black screen - superview is nil in viewWillAppear - ios

I have an application created from the tabbed application template. (ARC, iOS 4)
There are several tabs and there is a button on the 2. tabs viewcontroller.view(ViewCont2).
This button loads another viewcontroller's(ModalViewCont) view by presentModalViewController method.
There is a close button on ModalViewCont which calls dismissModalViewControllerAnimated.
In viewDidDisappear of ViewCont2, i am setting self.view = nil and other outlets to nil to unload the view so it will be fresh loaded next time it appears on screen. I am doing this because it inherits from a base class(BaseViewCont) which initializes some general properties of the view controller and adds some buttons, labels etc. in viewDidLoad method. So, ViewControllers that inherit from this base class may configure those properties differently as they wish in their viewDidLoad method.
Problem
Now, when ModalViewCont on screen, pressing the Home button to put application in background and after getting the application back, closing the ModalViewCont does not bring back the ViewCont2's view but a black screen with the tabbar at the bottom. The same thing happens without putting the application background/foreground; if other tabs tapped before tapping the 2. tab.(EDIT : This happens only if self.view set to nil in viewWillDisappear instead of viewDidDisappear.)
I determined that ViewCont2 loads a new view (checked it's reference) but view's superview is nil so the new view is not displayed but a black screen.
Things that did not work
Using [self.view removeFromSuperview]; before setting self.view=nil,
In viewWillAppear adding view to the parent; [self.parentViewController.view addSubview:self.view]; This one did not work smoothly, view placed slightly up of the screen. This is because there are several other superviews in the hierarchy.
Solutions i considered;
1- If superview is nil in viewDidLoad, it becomes available in viewWillAppear (assumption). So, viewWillAppear method of ViewCont2 could be used to get the superview loaded correctly by the following;
_
if (self.view.superview == nil)
{
self.tabBarController.selectedViewController = nil;
self.tabBarController.selectedViewController = self;
}
2- viewWillAppear method of base class could be used instead for initialization so there is no need to unload the view. So, performance could be optimized, it will not be unloaded each time view disappears. Also, it would be better to perform initialization only once by checking a flag, instead of performing it every time it appears.
Questions
1- Why does not the superview restored? What should i do for it? (This is the main problem i want to understand and solve instead of trying alternatives...)
2- Am i doing something wrong by assigning nil to view for unloading it? If so, how should i unload the view properly in such case like this(tabbed application)?
3- Is anything wrong with the 1. solution? Does it seem like a kludge? Is that assumption about superview and viewWillAppear correct?
EDIT : It seems that when viewDidLoad is called earlier than it should(i.e when view nilled in viewWillDisappear instead of viewDidDisappear), superview is not set.

It seems weird, but your suggestion (1) is indeed a correct workaround for this problem:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.view.superview) { // check if view has been added to view hierarchy
self.tabBarController.selectedViewController = nil;
self.tabBarController.selectedViewController = self;
}
}
Your second suggestion is good for performance (because view loading is an expensive operation) - but it will not solve the problem. You can also end up with a black screen without setting the view to nil in the following situation (test this in the iOS simulator):
open the modal view
simulate a memory warning -> this will unload the views in the tabbarcontroller
press home button and open the app again
close modal view -> black screen
Generally you can assume that in viewDidLoad the view property is set and in viewWillAppear + viewDidAppear the view has been added to the view hierarchy; so the superview should be there at that time (Here the superview is a private view of the tabbarcontroller of class UIViewControllerWrapperView). However in our case, although the view is reloaded (at the time of app resume), it is not added to the view hierarchy resulting in a black screen. This seems to be a bug in UITabBarController.
The workaround forces the appearance selectors to be performed again. So viewWillAppear will be called again, this time with a superview in place. Also viewDidAppear will be called twice!
Setting self.view to nil is okay, but should not be necessary in most cases. Let the system decide when to unload the view (iOS can unload views when memory gets low). The view controller code should be designed in a way so that the UI can be reconfigured at any time without reloading the view.

You do not have full control over when views are loaded and unloaded, and you are not supposed to load/unload views manually yourself.
Instead, you should think of view loading/unloading as something that's entirely up to your UIViewControllers, with you being responsible only for:
Implementing the actual loading, by associating your UIViewController subclass with a nib file or by implementing loadView manually.
Optionally implementing the viewDidLoad, viewWillUnload and viewDidUnload callbacks, which are called by the view controller when it decides to load/unload its view.
The fact that you have no full control of when the above callbacks will be called, has implications about what should go into them.
In your case, if I understand correctly, whenever your ViewCont2's view disappears, you want to reset it so that when it reappears it will be in some "clean" state. I would implement this state reset in some method, and call it both from viewDidLoad and from viewDidDisappear. Alternatively, you can have the "clean" logic in viewWillAppear.
Or maybe you want to clean ViewCont2's view only when the present button is tapped? In that case, clean the view both in viewDidLoad, and when the button is tapped.

What I offer is that when the modal view controller is active, and you dismiss the view, that you add a new view to the navigation view controllers viewControllers, then that view is told to remove its predecessor.
You can play with my project to see if you think it works for you.
EDIT: my comment on the selected answer is that this technique obviously works now, but I myself am having a hard time followiing it. The code in my project uses the system in a simple and direct fashion - when the modal view is told to dismiss itself, it calls a method (could be in any class) that adds a new view to the navigation controller's array, then dismisses itself. For a bit of time there are two view controllers of the same time, the new one stacked over the old one. When the new view controller appears, based on seeing a flag it silently and behind the scenes removes the undesired viewController from the nab bar's stack, and poof, it goes away.

I have found the actual solution to the UITabBarController bug(memory warning,app enter back/foreground,dismiss modal). Using UITabBarController as the root view controller is the cause of the bug. So, we could use another view controller as the root view controller and present the tab bar from it. I have tested it on iOS 5.1 simulator.
Of course, the overhead of extra UIViewController is subject to debate. Also, it's against the Apple documentation;
Unlike other view controllers, a tab bar interface should never be installed as a child of another view controller.UITabBarController Class Reference
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// A root view controller other than the actual UITabBarController is required.
self.window.rootViewController = [[UIViewController alloc] init];
[self.window makeKeyAndVisible];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, ..., nil];
[self.window.rootViewController
presentModalViewController:self.tabBarController animated:NO];
}

I have found other solutions;
First one causes the warning: "Application windows are expected to have a root view controller at the end of application launch" although there is root view controller.
Although it seems kludgy, the temporary view controller will be released with the first one.
Second one seems more reasonable.
.
- (void) tabBarBlankScreenFix1
{
self.window.rootViewController = [[UIViewController alloc] init];
[self.window makeKeyAndVisible];
[self.window addSubview:self.tabBarController.view];
self.window.rootViewController = self.tabBarController;
}
- (void) tabBarBlankScreenFix2
{
self.window.rootViewController = [[UIViewController alloc] init];
[self.window makeKeyAndVisible];
[self.window addSubview:self.tabBarController.view];
}

I think you shouldn't assign the view to nil.
If I understand you right you want to refresh/reload content every time the view appears.
So instead of setting the view to nil, you should try to refresh it. You can do it by adding:
- (void)viewWillAppear{
[self.view setNeedsDisplay];}
Please tell me if I understand your issue right

Related

Code in viewDidLoad runs every time it is called

Hi all I am doing a course in Udemy, and the code calls for placing code in the viewDidLoad function as shown below:
override func viewDidLoad() {
super.viewDidLoad()
placesArray.append(["name":"Taj Mahal", "lat":"27.175607", "lon":"78.042112"])
}
The array append should only run once, however, when I segue to another viewController and come back, it runs the code to append again. So I now have an array with 2 rows, both of which are Taj Mahal.
I thought that the viewDidLoad function only runs code once?
Is there a way around this?
Thanks.
Addendum:
I am using Swift, so I don't see any alloc and init while creating and launching the viewController. And weird as it sounds, the video tutorial has it working in the viewDidLoad and the trainer is using the storyboard to segue from the initial table view controller to a map view on a view controller and just has a back button on the map view that segue's back to the table view controller via the storyboard as well. - Could be because I have the latest version of the Swift language and the trainer was using an earlier version, (cause I noticed some slight differences in coding earlier) but you never know. Either way whenever he touches the back button it does not run the append code anymore.
I am trying to get in contact with the trainer as some of the suggestions here, though they are good don't seem to work.
I will put the solution in here once I get in contact with the trainer.
The viewDidLoad method is called when your view controller's view finishes loading. The view will load when a view controller's view property is nil and something attempts to access it.
UIViewController *myVC = [[UIViewController alloc] init];
UIView *aView = myVC.view; // this loads myVC's view; viewDidLoad is called when it completes loading.
If the view has unloaded (usually due to memory limitations), it will be called when the view property is next accessed.
Manipulation of data sets should generally not be done within view methods. Consider moving this to the init of the view controller (or to a different "datasource" class).
I suppose you are trying to do data initialisation in viewDidLoad. If there is no other operation on placesArray before viewDidLoad, then instead of append, what about setting the placesArray directly:
placesArray = ["name":"Taj Mahal", "lat":"27.175607", "lon":"78.042112"]
Then even if your view is unloaded for some reasons. Taj Mahal will still be added once only.
viewDidLoad is called whenever the view controller's view property is set. When does this happen? It depends on how the view controller is contained:
UINavigationController
- View Controller views are loaded as they are added to the navigation stack and "unloaded" (although the viewDidUnload method is deprecated) as they are removed.
UITabBarController
- View Controller views are loaded as they are added to the tab bar regardless of whether they are on screen or not. They stay loaded as you change from tab to tab.
Depending on your needs and use case, you can create your own view controller container that does what you need. Checkout the Apple docs on the proper way to do this:
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html

How does the memory management work for creating view controllers on a navigation controller

I have an app that uses a navigation controller. The views display results from picking a ranom card from a deck. The logic of what card was picked and what options to use is determined in the OnCreate of each view.
After I go through the views then I go back to the root view. Now when I go back through the vues, the same information is being shown and oncreate is not being called. It seems like when I go back to the root views, the vues that get popped off memory are not being freed, so it is using the same object. Is this how its suppose to work, or am I doing something wrong so popToRootViewController is not releasing the meory the views where using?
I'm using the following code to go to the next view
if (mGet == nil) {
mGet = [[cGet alloc] initWithNibName:#"cGet" bundle:[NSBundle mainBundle]];
}
You use -viewDidLoad to perform one-time setup of your view controller at the point that all view objects are instantiated. Just before a viewController presents its view, -viewWillAppear: is executed. Just before a viewController's view goes away, -viewWillDisappear: is executed. So when you need to respond to a viewController displaying its view, or going offscreen, override -viewWillAppear: and -viewWillDisappear: in your view controller.
Check the Apple documentation for UIViewController for these details.

UIViewController incorectly shows on its side

I have two views, when that app loads if the prefs match the users log in I am trying to load the next ViewController immediatly.
Each view normally shows in landscape and I have set the apps settings to only allow landscape. However when I try to load the second view immediatly from the first view "viewdidload" The second view appears on its side.
This is what my code looks like, this code is in a method that gets called from the first viewcontrollers viewdidload
currentProjectListViewController = [[CurrentProjectListViewController alloc] initWithNibName:#"CurrentProjectListViewController" bundle:nil];
[currentProjectListViewController setDelegate:self]; // set the delegate so it can access the returning method to update the view
UIWindow* keyWindow= [[UIApplication sharedApplication] keyWindow];
[keyWindow addSubview: currentProjectListViewController.view];
[self presentViewController:currentProjectListViewController animated:NO completion:nil];
I have no idea where to even start with this. it makes it to the second views viewdidload how ever like I say the view appears to be correct but its just on its side effectivly 90degrees incorrect.
any help making sure this apperar in landscape would be greatly appreciated.
I think your View hierarchy is somewhat wrong.
Why are you adding view of currentProjectListViewController to UIWindow object and immediately after adding it as subview you are presenting it using presentViewController from the current ViewController?
When you load and show the second ViewController, your presentViewController code should work.
You dont need to play with your UIWIndow object. Just make sure you dismiss your present view controller so as to reduce memory footprint and memory leaks.

viewWillAppear, viewDidAppear not called with pushViewController [duplicate]

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.

How to remove itself and superview from super superview?

I have a UIViewController classes A and B. A loads B using: [A.view addSubView B.view].
B has a navigation bar with a Back button. I want to go back to A when I click it, so in the selector I tried [self.view removeFromSuperview], but it only removed the navigation bar. Then I tried [self.view.superview removeFromSuperview], it still just removed the navigation bar. What should I do?
Also, another minor issue with the Back button: setting it's title. I tried these two ways, but it still displays "Back".
navItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Chapter" style:UIBarButtonItemStylePlain target:self action:#selector(handleBackBarButtonItem:)];
navItem.backBarButtonItem.title = #"Chapter";
Thank you in advance!
I don't think you quite understand how navigation (with UINavigationController) works in iOS. Assuming you want animation, this is what you want:
Set up a UINavigationController. This can be done in the app's delegate (to avoid memory leakage, set an instance variable on UINavigationController *navController:
navController = [[UINavigationController alloc] initWithRootViewController:A];
Note that we are adding A as our root view controller.
Push the second view controller when needed. I assume that you are adding B.view after a button is clicked or something. In the implementation of the method that adds the second view controller, run the following code, instead of [A.view addSubview:B.view]. This method should be in the first controller's .m file:
[self.navigationController pushViewController:B animated:YES];
This will also give a nice transition effect.
Pop the second view controller off the stack. With UINavigationController, a pretty arrow-shaped back button is automatically included in a pushed view controller, to navigate back to the last view controller. This means that you don't even need any code to allow backward navigation.
That's it! Now if you need to change the title of B's back button, do this in A's viewDidLoad method:
self.navigationItem.backBarButtonItem = customBackButtonItem;
You can get an array of subviews and then remove the ones you wanted to be removed. This SO post will show you how to remove all subviews or multiple subviews using subviews array.

Resources