Is it bad practice to put UIViewControllers in other UIViewControllers? - ios

I know there is the common practice in iOS development of having one UIViewController presented on the screen whose view is loaded from a XIB which will contain all the UIView subclasses in it.
While I was working on a pretty complex widget for an app, I decided to make the widget subclass a UIViewController instead of a UIView. This was because I thought a UIViewController was essentially a UIView with some helper methods around it. Then I could create a XIB for it (I know UIViews can have their own XIBs too), load the views it contains, place ITS view in the presented parent VC's view, and lay it out.
This works fine so far, but I'm wondering if this is bad practice and if I should just subclass a UIView instead and give it a normal NSObject controller. I am seeing some problems with this and I was wondering if anybody could address concerns I have with this approach?
EDIT NOTE: The widget VC does NOT relate to the VC view it is in and is reusable on ANY screen. So the answer is not subclassing the widget VC with the parent VC. The widget is INSIDE the parent VC, but it is NOT a parent VC.
EDIT NOTE 2: I am NOT using Storyboard. Only Autolayout, XIBs, and ARC.
Why can't we have VC's in VC's?
1) Can VC's be simply dropped into ANOTHER VC's XIB and be loaded easily as a subview?
2) I read here: When to use a UIView vs. a UIViewController on the iPhone?
The top answer explains how the VC controls rotation of the screen and laying out the subviews again, so if you add another VC, then the system will automatically think that THAT is the main VC and will attempt to rotate it instead, causing problems. Is this true? Or is he just talking about if you somehow got into a state where 2 VC's were "presented"? I wasn't sure if his answer applied to VC views that were SUBVIEWS of other VC views.
3) In general is this good practice? It certainly seemed more reasonable as it made loading the subview VC's view much easier.
Thanks!

It's absolutely fine. The answer to your problem is ContainerView.
Container View defines a region within a view controller's view subgraph that can include a child view controller. Create an embed segue from the container view to the child view controller in the storyboard.

You almost got it right. Yes, it's good to make a view controller for what you needed. But you shouldn't just add it's view to the parent view, you should also add the view controller as a child view controller of the first view.
You can add many views controllers as child view controllers of a view controller.
You can learn more about this here.

Related

Swift add viewController as a subview to other views

I have multiple view controllers which should load a specific view like in this drawing. I defined that view in the storyboard as a separate view controller. The reusable view controller has some methods which make the view work. What's the best way to do this?
I tried using addSubview method but the IBOutlets won't initialize. Please help me.
You probably want to use a container view.
With a container view, you can embed different view controllers in the same view. Hooking them up is really easy, and can be done within the storyboard.

Why can't I assign a UIView to self.view?

This might be a stupid question, but I'll shoot.
I made a little test project to test out a concept I had for a sliding view controller type of thing. I naively assumed I could create a UIView (let's call it peekView) with an outlet in a controller, and call something like [slidingControllerSlideFrom:self.view] from any visible view controller, the implementation of such being:
- (void)slidingControllerSlideFrom(UIView*)controllersMainView
{
// push side controller to top of navigation stack
self.peekView = controllersMainView;
// sliding animation
}
But there is no effect. No crash, no warning, no change of view in the pushed controller.
Of course, the pushed controller crashes when trying to add self's view as a subview, but assigning it to a predefined UIView just results in nothing.
So, why? And if a mere 'why' is not enough of a question- what happens when I try to assign one controller's view another controller's subview, and what was the reason for designing UIKit where you cannot set views from self.view?
To do that you have two options:
1 - If the controller in the peekView is always the same one in a given scene, use a "Container View". Those are explained here. Basically, they allow you to add a view in your scene that is managed by another controller.
2 - If the controller in the peekView depends on different conditions, you will have to create something similar to a custom tabbarcontroller. That means that you instantiate the controller that you need, add it's view as a subview of peekView (not assign the controller's view to the peekView itself) and then use didmovetoparentviewcontroller to notify the child controller. This question might help.
UPDATE:
Just saw your comment, so let me answer what you actually asked: The peekview property is actually just a reference to the real UIView you placed in the screen. When you do this:
self.peekView = controllersMainView;
You are changing the reference, but no the view object itself. That's why you are not seeing any changes. There are ways of adding a new view to the controller from code, but it is much simpler to simply use addSubview to add your controllers view to a UIView that is already in the controller.
Check out the discussion here: subView Slide up from bottom of the screen
and here: SubView slide in animation, iphone
Hopefully that gives you a bit of framework on how to approach this task!

In iOS, using storyboard, how to setup a view controller inside a container view?

I have created and drawn out a custom UIViewController called AutocompleteVC in my Main storyboard. AutocompleteVC will be used in several different places/storyboards and with different dimensions.
For example, in my Transit storyboard, shown below,, there is a custom UIViewController called TransitVC, shown on the left. In TransitVC, I have a container view with an IBOutlet called autocompleteContainerView. autocompleteContainerView has a segue called autocompleteEmbedSegue to a generic UIViewController, shown on the right in red.
The goal is to have TransitVC hold AutocompleteVC inside autocompleteContainerView. But I'm having trouble getting this to work. The reason I want to do this inside a container view is so I can use autolayout to set constraints on it. Otherwise, I know how to do this purely in code.
I believe my approach might be flawed. What is the correct approach to do this if I want to maximize storyboard usage.
I'm not sure what you are asking. Setting up a parent/child relationship with a container view is very easy, exactly as you have outlined. Just create a container view inside the parent view controller, create the child view controller scene, and then control-drag from the container view to the child view controller to create the embed segue.

iOS VIewController for UIScrollViews content

I have a UIScrollView in my app and I am adding some custom views from xib to it so you can horizontally scroll (tabbing) in ScrollView to change which one is shown. For now this works but I have a problem with connecting views to controllers.
I don't know how to choose structure of ViewControllers (how many controllers should I use, use nested controllers,...).
I have a rootView and its controller. In this rootView there is a ScrollView and this ScrollView contains some custom views (subviews) loaded from xib (using loadNibNamed method).
My question is should I use the same ViewController as for rootView also for these subviews in ScrollView? Problem is that the ViewControllers view property is already bind to the rootView (super view in rootView) so when I bind this view property also to subviews an error is occurred. Also if I create new controller for these subviews an error is occurred as well.
When I am loading subviews to the ScrollView with loadNibNamed method in ViewController of rootView, owner of these subviews is ViewController (owner argument of loadNibNamed method is set to self).
Can you tell me please, how should I solve this? What controller should I use for subviews, should I create new one or should I use existing one. Or should I use some nested controller? I am newbie in iOS development so I have a chaos in using ViewControllers right now...
If there isn't much code that is relative to controlling the sub views you could use just the root view controller. i.e A single controller for a single scene would be a good MVC approach.
If you are using it this way , don't change the view property of view controller as this messes it up for the root view - controller setup. If you just need a reference to this views you already have it with the return value of loadNibNamed. Also if you are setting the owner to self then create additional instance variable to hold the sub views(and not the view property) so that you can specify the owner from the xib itself and connect the references appropriately.
However if you have substantial business logic to be written regarding the sub views then its fine to create separate view-controllers(a single class would be fine if all the subviews behave the more or less same way if you are getting what i mean) for it. In the xib for the subviews, you can specify this class as the owner and when using loadNibNamed: you should create an object of the subviewcontroller class and specify this as the owner. This way you can modularize the whole thing.

How to subview a UITableViewController within another Controller using Storyboards

I have encapsulated all my table view logic on a UITableViewController that is linked to a view. This was done using storyboards.
I would like to embed this logic and view within another view controller / view (kind of like a header information with a scrollable table beneath.)
I have the following components:
CustomViewController which is linked to a UIView (dragged in from storyboard)
CustomTableViewController which is linked to a UITableView (dragged in from storyboard)
Essentially I am trying to mimic the scenario of the Stopwatch in the iOS clock app
What is the beast approach to this?
How is it done programatically?
Can this be done on the storyboard somehow?
Any help would be greatly appreciated. Thanks
Ok figured it out. This solution is iOS 5 specific since this feature was added there. This method works with storyboards.
Setup: The intention is to host one view controllers view and logic within another controller.
Since there is no intrinsic way to reference the child view controller in the storyboard, we need to give the view controller a name. This can be done by filling out the "Identifier" attribute on the controller in the storyboard. NOTE: Make sure you are giving the controller the identifier and not the controllers view.
Instantiate the controller you want to aggregate. This can be done from the hosting controller.
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"ControllerIdentifier"];
Add the child controller to the parent controller
[self addChildViewController: controller];
Add the child controllers view to the parent controllers view. Note if you have a place holder view in the parent controller you wish to add the child view to, then this is where you do it. Here I add it the a UIView called stage in the parent controller.
[self clearStage];
[self.stageView addSubview:controller.view];
presentedController.view.frame = self.stageView.bounds;
And that is it. Pretty simple. I have used it successfully with switching controllers and view in a home made tab control. The sub controller enlists its views in the view lifecycle, so the viewDidLoad, etc all work as expected in this child view controller.
Hopes this helps someone.

Resources