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

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.

Related

IOS: One navigation view controller with many different views - how should I do it?

I'm trying to create an IOS app that has one navigation view controller with several bar buttons, where each button changes the content of the view itself. Most of my views are custom views anyway, so I won't really see any clear previews in the storyboard anyway.
I'm thinking about creating one navigation view controller, and several NIB files to represent each view. Each NIB will have its own class with all the IBOutlets and IBActions.
Then, when a button is clicked in the navigation view controller, just switch to a new view by clearing the old one (removeFromSuperview), and call loadNibNamed to load a new view.
Does this sound reasonable or will all this deserializing be expensive in terms of CPU? Would it be better to just create different viewcontrollers in the storyboard, with segues etc, and copy those nav bar buttons to each view controller?
The simplest solution:
ViewController->
View->
rootViewOne
rootViewTwo
rootViewThree
...
upon your bar button's action,set specific rootView's hidden property to true or false:
self.rootViewOne.hidden = currentIndex == 0
self.rootViewTwo.hidden = currentIndex == 1
self.rootViewThree.hidden = currentIndex == 2
You can add your other view controller/s as child view controller.
In this way, you will have code for different views in their respective view controllers and your main/parent view controller will not be messy.
To add child view controller:
// Get instance of your view controller
UIViewController *childViewController = [UIViewController new];
// Add your view controller as a child view controller
[self addChildViewController:self.loginView];
// Set frame for your childViewController's view
[childViewController.view setFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, self.view.frame.size.height)];
// Add childViewController's view as a subview to your viewcontroller's view
[self.view addSubview:childViewController.view];
// Notify your main/parent controller that a child view controller is being added
[childViewController didMoveToParentViewController:self];
To remove child view controller:
// Notify the childViewController that it will be removed
[childViewController willMoveToParentViewController:nil];
// Remove childViewController's view
[childViewController.view removeFromSuperview];
// Remove childViewController from your main/parent view controller
[childViewController removeFromParentViewController];
Hope this helps.
UPDATE - Embedded swapping by Sandmoose Software:
http://sandmoose.com/post/35714028270/storyboards-with-custom-container-view-controllers
Here's a snippet from the "how-it-works" section:
The segue between ViewController and ContainerViewController is an
embed segue. The segues between ContainerViewController and its two
child view controllers is a custom segue. This custom segue is named
Empty and is a subclass of UIStoryboardSegue. It contains an empty
perform method. The ContainerViewController will take care of moving
the child view controllers into and out of place. However, the fake
segues are needed to create the connections in the storyboard.
If you’re like me you cringe at the thought of having a fake/empty
class like this but the benefit is that it allows us to stay firmly in
the world of storyboard idioms without resorting to programmatically
loading isolated storyboard scenes. It’s one tiny bit of ugliness
which allows us to preserve the usefulness and elegance of
storyboards. The storyboard still visually represents the scenes and
their relationships accurately. We can still use segues to control
what happens.
---------- Old answer below (phfffft!) ------
You pose several different questions here: one regarding custom buttons in the nav bar of a nav controller, another about assigning multiple views to the nav controller, and one about placing the view in separate XIB files, while maintaining IBOutlet connections and such.
The first one calls for a tab bar controller, which you can customize to look in whatever way you deem fit, even like a nav bar (ADC has ready-to-use sample code, "UIKit Catalog (iOS): Creating and Customizing UIKit Controls", which fits your needs).
The second can be done easily in Interface Builder by following any number of instructions freely available on YouTube, at Apple, and elsewhere on the Internet; you'll find it an extremely intuitive process. If you can drag-connect a tab bar controller to a view controller, you can intuit the rest. Otherwise, Apple has sample code for customizing the navigation or transition between views in a single view controller interface. "State Restoration of Child View Controllers" provides the best structure for what you want (just pull out the restoration-related code, and use the rest).
The third is not intuitive at all; and, as far as I know, there are only a couple of sets of instructions that exist for separating views (or other view controllers and their views) from a parent or top-level view controller. Here's one I haven't tried yet, so I'll be interested to hear how it worked for you:
http://digginginswift.com/2015/08/30/making-reusable-views-in-separate-xibs
The technique I use involves creating a storyboard with a single view controller to start, followed by a new view controller embedded in a container view for each of your intended views; then, deleting the view in each of the child view controllers, which leaves place for the views you set up in separate XIBs. They are connected by setting the File's Owner to the parent view controller. I can send you sample code, if you'd like to try a different approach than the aforementioned website offers.

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. =)

iOS: Embed Table View in other View (but table view has a complex table view controller..)

I'm struggling with this problem, so I need your help. Basically I've programmed a complex table view controller (with NSFetchedResults protocol etc) and already used it in my app and it worked great. Since I want now exactly this table view inside another view (so I can add a small subview at the bottom of the screen), I'm really struggling how to do this! I know by know how to embed a simple table view inside another view and link it to it's view controller.
But how can I do this in my case with as little effort as possible? I mean can I somehow use this table view controller I already have even though the superview must have its own view controller?! And how would I do that? Sorry I'm still a beginner :)
Since you already have a TableViewController. To add it as an subview to another ViewController's (self) view, do this:
TVC = <your tableViewController instance>;
[self addChildViewController:TVC];
TVC.view.frame = <your desired frame>;
[self.view addSubview:TVC.view];
adding the TVC as childViewController will forward UI methods of 'self' like willAppear and all to TVC.

How to make a high level controller load a view controller

I have a high level controller class that is in charge of managing several classes and driving a state machine.
Part of the state logic involves the temporary display of a couple of different screens, there is a dedicated view controller for each of these screens and the high level controller creates these other controller classes and presents them as modal view controllers as necessary.
However one of these view controllers is very simple, it has a button and some text which can change as the state progresses. The high level controller is the class that knows what text to display at what stage and also it needs to know when the button has been pressed.
Therefore the view controller would need to present an API which the high level controller can call to set the text, and also the high level controller would need to be a delegate of the view controller so it can be called back when the button is pressed.
This seems unnecessary plumbing which I'd like to remove.
So I could move the outlets and actions in the view controller into the high level controller so this plumbing isn't needed. But then there's nothing left in the view controller (its being instantiated from a nib). So I'd like to get rid of it entirely.
Then once its gone I was planning on having this in the high level controller:
UIViewController *vc = [[UIViewController alloc] initWithNibName:#"TheNib" bundle:nil];
or should it be:
UIViewController *vc = [[UIViewController alloc] init];
[[NSBundle mainBundle] loadNibNamed:#"TheNib" owner:vc options:nil];
[[UIApplication sharedApplication].delegate.window.rootViewController presentViewController: self.vc animated: NO completion:nil];
However how should IB be set up in order to achieve this - specifically
a) what should the File's Owner be set to?
b) what can the view within the IB be set to?
Note that the high level controller is already inheriting from another class and thus I cannot derive it from UIViewController thus it does not have a view property to connect to the view in IB.
Is there a easy way of connecting the xib contents to the UIViewController that is created within the high level view controller?
A view and its controller is an 'element of modularity' Thus, if the view has a button then the view's controller, not the parent, should be the target of the button. The view's controller, figuring that parents/others will be interested in button events, should define a delegate interface or a notification interface that parents/others can participate in.
In your case by 'removing unnecessary plumbing' you are 1) moving away from the 'element of modularity' and 2) creating difficulties that lead you to ask a question about a problem related to removing plumbing.
It is best just to use the standard patterns.
You can try to change the file owner or add an class object to your NIB…
Just drag "Object" to the objects list and set the right class. Then, you can use IBactions connected to this object.
Maybe this question I made (and answered) can help you: Coordinating Controller design pattern in Cocoa Touch
If all your view is doing is displaying some text and a button why not replace it with an UIAlertView? It's a simple control for displaying some text and a button, or two, with a well defined interface for getting which button was pressed back.
Ofcourse this might not fit with your design as UIAlertView can't really be customized unless you use a 3rd party replacement (i.e. http://cocoacontrols.com/platforms/ios/controls/tweetbot-like-uialertview-and-uiactionsheet-replacement).

iOS container view controller - Why does transitionFromViewController need the fromViewController?

When using custom container view controller, I don't quite understand why the presenting view controller needs to specify the from, because being the container class, it should already know what's in the view hierarchy, no?
transitionFromViewController:toViewController:duration:options:animations:completion:
Container view controllers sometimes need to put the views of the contained controllers into specific subviews of their own view. (For example, a SplitViewController reimplementation might have left and right positioning views holding the master and detail controller views respectively.) Providing the fromViewController tells UIViewController where in the view hierarchy the new controller's view should be inserted, and also which specific view should be removed after the animation.
(contrary to another answer, the frames of the views aren't set for you at all. You do that, before the call, and in the animation block. The "Creating Custom Container View Controllers" system guide in the docs has a pretty good example.)
As it happens, actually using transitionFromViewController:... appears to be optional. You can manage your view hierarchy manually, with or without animations, and it works fine. I'm still forming my opinions, but I think I prefer to do it manually, to more easily handle cases where one of the VCs is nil.
This is done this way to allow you to have a view controller that has views with viewControllers in it. The from defines the originating view controller and gives the system the ability to position the animations appropriately.
Imaging you had a view with 4 views in it like tiles. the main view controller can consecutively call this on its "child" view controllers and with the from -> to specification, it won't make the assumption that the caller is the from viewController.

Resources