Sharing the Same UIViewController as the rootViewController with Two UINavigationControllers - ios

Update: I have decided to go a different route with my problem.
Instead of trying to reuse the same UIViewController directly, I use two vanilla UIViewControllers that are set as rootViewControllers. In their loadView methods, they make a call to [storyboard instantiateViewControllerWithIdentifier:] to get the former UIViewController and set their views to the controller's view. This is probably the preferred approach anyway, since I need to set several variables and delegates.
I have a UIStoryBoard with a UITabBarController as the entry point connected with two UINavigationControllers. Each of those share a common UIViewController as their root view controller. When the app starts, the first UITabBarItem is selected and the view loads as-expected. However, when I select the second UITabBarItem, the same view is not visible. I see the UINavigationBar with a black background. Am I doing something incorrect with the Storyboard interface, or do I need to manually instantiate the UIViewController via each UINavigationController's method--loadView for instance?

Strangely this is a question that no one else is asking. As far as I know it is not possible to share the rootViewController which I think is without a doubt a bug since when you inspect the connection on the storyboard you can see that the view controller is connected to both navigation controllers. I consider this a flaw in storyboarding because duplicating viewControllers and reapplying all of their connections is quite error prone and makes the storyboards overly complex.
I see your solution to the problem. Workarounds like this make me question if the current storyboard functionality in iOS is ready for creating apps. I think that there is a conceptual problem with the storyboards, Apple needs to decide if a viewController on a storyboard represents an instance or if it represents just the class, right now it is not consistent as you can see that multiple segues can actually point to the same viewController but in reality each segue has its own instance, why this is not also followed for rootViewController connections?, I don't know.
Just as a note, with your solution take into account the following from Apple's documentation:
"Important A view controller is the sole owner of its view and any subviews it creates. It is responsible for creating those views and for relinquishing ownership of them at the appropriate times, including during low-memory conditions and when the view controller itself is released. If you use a storyboard or a nib file to store your view objects, each view controller object automatically gets its own copy of these views when the view controller asks for them. However, if you create your views manually, you should never use the same view objects with multiple view controllers."
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html

Definitely a bug in Storyboards. Another way to do this is to create basic UIViewControllers for each UINavigationController, then have a UIContainerView that points to the same UIViewController in each of the basic view controllers.

I think the easiest solution is to set no root view controller for your nav controller in the storyboard and then do something like this with your nav controller:
- (void)viewDidLoad
{
[super viewDidLoad];
UIViewController *topVC = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"myTopVC"];
[self pushViewController:topVC animated:NO];
}
The first-pushed VC becomes the root.

Related

Relationship among window, rootviewcontroller, childviewcontroller, navigationcontroller in iOS

I haven't really seen any resource that gives a good and simple explanations on relationship among window, rootviewcontroller, childviewcontroller, navigationcontroller and how they piece together in iOS development. Anyone one knows how to put this in a easy-to-understand way or any online resource or book that does a good job in explaining it?
Per the documentation on UIWindow:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIWindow_Class/
A UIWindow object provides the backdrop for your app’s user interface and provides important event-handling behaviors. Windows do not have any visual appearance of their own, but they are crucial to the presentation of your app’s views.
Xcode typically provides your application's main window, but you can add more if you need to.
From the documentation link you can see that UIWindow is actually a UIView
Enter your first view controller. Like providing a main window, when you start a new Project in Xcode the project template usually wires up your initial view controller, which as the name implies controls a view (UIView).
You could call this initial view controller your RootViewController but if you get a handle on the UIWindow you could just as easily swap out the current initial view controller's view for any other view controller view you like.
That probably doesn't help with hard and fast rules for things, but if I understand what you are asking, RootViewController is likely the initial view controller for you application. For example, if you are using Storyboards, Xcode typically makes Main.storyboard, you will see a gray arrow pointing to the UIViewController representation.
This is pointing to the Storyboards Initial View Controller. You can verify this from the Attributes Inspector. Select the view controller then select the attribute inspector:
So that's basically RootViewController. ChildViewController is just any other view controller that is a child of a view controller.
I assume what you are referring to is:
addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:
You can read more about these methods here:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/
Under Implementing a Container View Controller
The quick gist of it is, A View Controller controls a view. View's can have subviews. Those subviews can come from other View Controllers. The methods outlined above pretty much just enable things like viewWillAppear, or viewWillDiappear to be called on the child view controller automatically when those methods are invoked on the parent view controller.
Per the docs:
By default, rotation and appearance callbacks are automatically forwarded to children. You may optionally override the shouldAutomaticallyForwardRotationMethods and shouldAutomaticallyForwardAppearanceMethods methods to take control of this behavior yourself.
a NavigationController is just like any other View Controller. It contains some special behavior for transitioning between views, but like other View Controllers it has a View (UIView) that it manages. A navigation controller could be your Initial View Controller / RootViewController just as any other View Controller can be, it all just depends on what you are trying to do. For example, a simple app that is just a list view, where you can tap an item and get a detail view could be constructed as:
1) Initial View Controller -> NavigationController
2) The NavigationController's first ViewController (Apple calls this a RootViewController) would then be a TableViewController.
3) Selecting a TableCell in the TableView (TableViewController manages a TableView) would then transition you to your Detail View Controller. The Navigation Controller knows how to do all that Sliding back and forth drama.
That's a pretty simplistic overview you can search the internet/youtube for more full featured tutorials outlining the same thing in more detail.
Example: https://www.raywenderlich.com/113388/storyboards-tutorial-in-ios-9-part-1
It's worth your time to do a few of these to get your bearings. Yes, it will likely cost you a few hours of your day. Take heart, everyone who ever started doing iOS development had to go though the same thing. =)

Please clear some confusions regarding UIViewController

Please clear some confusions regarding UIViewController
I found this article Abusing UIViewController and here are the links link1 & link2
and summarised points
This is author's (and Apple’s) advice In a nutshell:
One (and only one) view controller should be responsible for a whole hierarchy (or screenful) of UIViews.
Mostly, you should only use one view controller per screen. Essentially the rootViewController of the current UIWindow should be the only UIViewController with a visible view.
Each different screen should have a different view controller i.e. one controller should not control more than one screen.
You should NOT nest custom UIViewControllers within a view hierarchy.
If more than one UIViewController hangs off the application’s UIWindow, only one of these will get the messages for changes in orientation. The other(s) will NOT get these messages.
Nested UIViewControllers are not guaranteed, or likely, to receive messages for changes in orientation or lifecycle messages such as viewDidAppear:, viewWillAppear:, viewDidDisappear: and viewWillDisappear: even though they inherit from UIViewController. Only the topmost UIViewController is certain to get these messages.
Please clear point number 2 and 3
because when we use UINavigationController or UITabBarController we use multiple subclasses of UIViewController. And ios device has only one screen.....
This article Abusing UIViewController highlight apple suggestion
Note: If you want to divide a view hierarchy into multiple subareas
and manage each one separately, use generic controller objects (custom
objects descending from NSObject) instead of view controller objects
to manage each subarea. Then use a single view controller object to
manage the generic controller objects.
and in apple docs under heading of Coordinating Efforts Between View Controllers apple saying
Few iOS apps show only a single screenful of content. Instead, they
show some content when first launched and then show and hide other
content in response to user actions. These transitions provide a
single unified user interface that display a lot of content, just not
all at once.....
My requirement is NOT to use any container or modal or popover, I want to do manual management, I have two view controllers VC1 & VC2. VC1 is the root view controller now I want to switch/transit/move to VC2 what should I do?
VC1 should be the only subclass of UIViewController and VC2 should be the subclass of NSObject to manage a particular view in VC1 hierarchy?(the show hide thing by apple doc).
VC2 can also be the subclass of UIViewController, I just remove VC1 from root view and add VC2 as root view?
or what is the correct way?
Container view controllers (like UINavigationController) allow working around the one-VC-per-screen rule. Since iOS 5, developers have been able and allowed to write our own container controllers (which aren't actually much different from normal VCs). Generally this means that writing non-VC controller objects is less necessary than it used to be.
In your situation, where you want to replace the root view controller, your option 2 makes more sense. Use VCs where you can, and non-VC controller objects only when you can't. Since you're replacing the whole screen's content, just switching the UIWindow rootViewController makes the most sense (edit: alternately, many devs would just use a navigation controller to present the second view, because it's simple and convenient).

Reuse childs from custom UIVIewController using storyboard

I have a storyboard with a navigation controller that leads to an UIVIewController that I want to reuse. That UIVIewController has a ParentUIViewController that has all the basic functionalities for all the UIVIewControllers that I am reusing.
Currently I am copying and pasting (meh) and then I change the class of the UIViewController to the ChildUIVIewController that I want to use (ChildUIViewController extends ParentUIViewController).
But this sounds like a bad solution. Everytime I want to change the ParentViewController visually I need to update, manually, all other ChildViewControllers.
I have tried to create a xib for the ParentViewController but the xib isn't loaded because I need a xib with the name of the ChildViewController. I have created it and then said the class is the ParentViewController but it crashes in the segue.
EDIT
I have created an example of the status of my problem
https://github.com/tiagoalmeida/storyboardexample
Note that the ParentViewController has a set of logic way more complicated that is not illustrated there. Also note that I am also using a TableView. I hope that this can illustrate the problem.
Keep the logic on the parentViewController and the UI Part on the child UIViewControllers. If you need to create a new UIViewController, you will create a child that will have a corresponding XIB (or get rid of XIBs and create the interface by hand).
Have you considered looping back into the same UIViewController via a "phantom button"?
Have a look at this: UIStoryboard Power Drill, Batteries included
Essentially you can drag a Bar Button Item into the little black bar under the View Controller in Storyboard (the 1 with View Controller, First Responder, and Exit icons; sorry, I don't recall what this is called exactly), then you can control+drag from that button back into the UIViewController for a Push segue. This should create a loop segue in your Storyboard. All you need to do next is give that segue an identifier, programmatically call it from your code using [self performSegueWithIdentifier:], then implement -(void)prepareForSegue: and use [segue destinationViewController] to conditionally set the title and perhaps some flags so you can identify when to use different kinds of fetches (or other code variations) in the same Class code.

iOS App ECSlidingViewController: add new viewcontroller and transient to it

I use ECSlidingViewController in my App. There're four ViewControllers come with the sample code. These four ViewControllers corresponds to four option in the "MenuViewContoller".
Now I want to add a TableViewController in the "FirstTopViewController". Each cell of this TableViewController leads to a ViewController which is not one of the four predefined ViewControllers.
I saw that ECSlidingViewController ships a 'SampleTableViewController". But I don't know how to 'transient' to this ViewController.
I did work with UINavigationController before. I know how to push a view controller to NavigationController. I don't know how to cooperate UINavigationController with ECSlidingViewController.
Thanks a lot.
Here is a demonstration:
http://www.penghou.net/file/question.png
ECSlidingViewController is a container, just like UINavigationController. You give it view controllers and it displays and transitions between them.
ECSlidingViewController provides methods for setting view controllers and transitioning between them by calling anchor/reset. Check the header file for documentation on these methods.

Subview management within master/detail view in iOS (with ARC)

I have a master-detail controller for my app. The master controller is a UITabBarController and each tab is a UITableViewController that contains different types of data.
I plan on having a main header / image on the main detail view but then need to add different subviews to the main detail view to detail specific information depending on which tab I am using.
I am currently adding the relevant subview in my
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Function like so:
UIViewController *subview = [[UIViewController alloc] initWithNibName:#"ItemNotFoundViewController" bundle:nil];
subview.view.frame = CGRectMake(20, 160, subview.view.frame.size.width, subview.view.frame.size.height);
[self.detailViewController.view addSubview:subview.view];
However, I believe that this is a poor way of doing things - every time someone clicks on a table cell another subview will be thrown on top of the stack of previously added subviews, creating memory issues.
What I am wondering is, does ARC take care of this for me? Is my approach passable? and even if it is passable, is there a better way of doing this?
First of all, no. ARC does not take care of this for you. It's not it's purpose to do that and even if it was, how could it know, that you don't want the previously added subviews anymore?
You have to remove those subviews yourself and then ARC will take care of deallocating them (if there are no other references to them).
Anyway that's not the way you're supposed to use a UISplitViewController (the master-detail view controller). As you noticed the split view controller handles two other view controllers. The master- and the detailViewController. In most cases the master view controller isn't changing while the app runs (it's content changes, but usually that's handled by a container view controller like UINavigationController which is assigned as the masterViewController), but the detail view controller does.
Instead of adding subviews to your existing detailViewController you should replace it by a new one. So you should create separate XIBs (what you've apparently done already) for all the view controllers that you want to present in the detail-section. And modify your code to
self.detailViewController = newDetailViewController; //newDetailViewController would be the vc you called subview in your code
instead of
[self.detailViewController.view addSubview:subview.view];
Edit: Notice that this assumes that your detailViewController property does 'the right things' when you set it's value. By default the UISplitViewController only has a property called viewControllers which is an NSArray in which the first object is the masterVC and the second is the detailVC.
Take a look at MultipleDetailViews for an example of how to manage that.
Since you want to have a header view in all your detail view controllers you have various choice of achieving that (which may or may not be applicable in your case, depending on your design):
add the header view to every details vc's XIB
instead of creating many XIBs for all detailVCs, create a new custom UIViewController subclass that modifies it's content based on some parameters you give it, i.e. which tableViewCell was tapped by the user
create a custom container view controller that manages two child view controllers, one for the headline and one for the content above it.
For more information about UISplitViewController and custom container view controller, please refer to:
View Controller Basics
Creating Custom Container View Controllers
No, ARC will not take of this for you, because detailViewController.view will keep a reference to its subviews. It's hard to say what approach is best without knowing more about what you're doing with these views. It would probably be better to just present the new view controller -- it will be deallocated after it's dismissed if you don't have a property pointing to it.

Resources