How To Properly Add Child View Controller in iOS 8 With Swift
If you want the red controller to be the child controller, delete the yellow one, and control-drag from the container to the red controller. There's no need to add it in code, or do any resizing. The red controller will be set to the same size as the container in the storyboard.
Add child view controller swift
When I use your code, run the app, and pause the app, and look at the view hierarchy, I see the following:
(lldb) po [[UIWindow keyWindow] recursiveDescription]
<UIWindow: 0x156bdc30; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x156be750>; layer = <UIWindowLayer: 0x156aa3c0>>
| <UIView: 0x156c5440; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x156c55c0>>
| | <UIScrollView: 0x156c2740; frame = (0 0; 0 568); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x156c4d00>; layer = <CALayer: 0x156c2a80>; contentOffset: {0, 0}; contentSize: {960, 568}>
| | | <UIView: 0x156c6df0; frame = (640 0; 0 536); autoresize = W+H; layer = <CALayer: 0x156c6d80>>
| | | <UIView: 0x156c7100; frame = (320 0; 0 536); autoresize = W+H; layer = <CALayer: 0x156c70a0>>
| | | <UIView: 0x156c73f0; frame = (0 0; 0 536); autoresize = W+H; layer = <CALayer: 0x156c7390>>
| | | <UIImageView: 0x156c8bd0; frame = (0 564.5; 600 3.5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x156c8c50>>
| | | <UIImageView: 0x156c9020; frame = (-3.5 32; 3.5 568); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x156c90a0>>
| | <_UILayoutGuide: 0x156c5620; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x156c5800>>
| | <_UILayoutGuide: 0x156c5c90; frame = (0 568; 0 0); hidden = YES; layer = <CALayer: 0x156c5d10>>
If you're not seeing your subviews there, then likely culprits include:
You may not have specified the view controller base class in your scene, and thus this code isn't being run. You can confirm this with a
println
log statement or breakpoint in side thisviewDidLoad
and make sure you're hitting this routine.You may not have hooked up the
@IBOutlet
for thescrollView
, and thusscrollView
isnil
. Confirm this by putting breakpoint inviewDidLoad
and examining thescrollView
property.
In your revised question, we can now see that the three subviews are present and appear to be the correct size. That's great.
Now the question is why you don't see anything. If you have these defined as scenes in your storyboard, you should:
Make sure the "base class" and the "storyboard identifier" is defined for each of these three child scenes; and
When your main view controller instantiates the three child view controllers, you would instantiate them from the storyboard using the storyboard identifier (in my example, I used storyboard identifiers of
A
,B
, andC
, respectively):let Avc = storyboard.instantiateViewControllerWithIdentifier("A") as AViewController
let Bvc = storyboard.instantiateViewControllerWithIdentifier("B") as BViewController
let Cvc = storyboard.instantiateViewControllerWithIdentifier("C") as CViewController
If you do the above and repeat the recursiveDescription
you should see your scene's subviews (e.g. the labels you added) appear in the output.
iOS : Why do we need to add child view controller when adding view as subview does the work?
Certainly viewDidLoad
was called. That happened instantly as soon as you referred to ViewController2's view
in your code.
But let's say your ViewController2 does other things besides load a view. Suppose its view contains a button that is hooked through an action to a function in ViewController2. If you now tap that button nothing happens.
That's because the ViewController2 itself is dead: it has vanished in a puff of smoke.
You can see that by implementing deinit
in ViewController2. You will see that, just as viewDidLoad
is called, so is deinit
. You are left with a view controller's view that has no view controller. That is bad.
There is a view controller hierarchy that is responsible for maintaining relations between view controllers. When you add ViewController2 as a child view controller of ViewController1, you maintain that hierarchy, and you maintain it correctly according to the rules, which say:
If VC2's view is somewhere inside VC1's view, then VC2 needs to be a child (at some depth) of VC1.
In other words, the view hierarchy and the view controller hierarchy must run together. Otherwise, the responder chain will break and life will become chaos.
(There are other requirements when you make one view controller the child of another, like sending didMoveToParent
to the child as part of the opening dance, along with other message forwarding responsibilities later, so as to ensure that the child view controller gets other messages like viewDidAppear
at the right time. It's a complex business. However, I've focussed my answer on the very basic part of what you asked.)
I should add: if your goal was merely to fetch a view out of a nib and stuff it into your own view, you can certainly do that, no problem. What you must not do is use a view controller as a kind of magnet or vacuum cleaner to fetch a view for you if your intention is then to let go of the view controller itself.
Add child view controller to current view controller
didMoveToParentViewController must be the last.
How to add Child View Controller to TableViewController
It's not prefereed nor right to add child vcs to a tableController , as the content will scroll with the parent table as view = tableView
inside tableController it could work but it needs some a tweak inside scrollViewDidScroll
, so it's better to add it to a regular vc and add the table as a property
How to properly access a child view controller
Since you mention a "container view" I am assuming you invoke the child view controller using an embed segue.
Assuming that's the case you should implement a prepare(for:sender)
method that saves a pointer to the child:
Give the parent view controller an instance variable:
var childView: ChildView?
And then in your prepare(for:sender)
method
func prepare(for segue: NSStoryboardSegue, sender: Any?) {
if let dest = segue.destination as? ChildView {
childView = dest
}
}
Then when you need to send messages to the childView:
childView?.runFunction()
(Method names should start with lower-case letters)
Child View Controller and Margin
You don't give the child view neither a frame nor constraints
child.view.translatesAutoresizingMaskIntoConstraints = false
Also you should add view not imageView
view.addSubview(child.view)
NSLayoutConstraint.activate([
child.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
child.view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 8),
child.view.leftAnchor.constraint(equalTo: view.leftAnchor),
child.view.rightAnchor.constraint(equalTo: view.rightAnchor),
])
and set
navigationController?.navigationBar.prefersLargeTitles = false
topLayoutGuide in child view controller
While this answer might be correct, I still found myself having to travel the containment tree up to find the right parent view controller and get what you describe as the "real topLayoutGuide
". This way I can manually implement automaticallyAdjustsScrollViewInsets
.
This is how I'm doing it:
In my table view controller (a subclass of UIViewController
actually), I have this:
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
_tableView.frame = self.view.bounds;
const UIEdgeInsets insets = (self.automaticallyAdjustsScrollViewInsets) ? UIEdgeInsetsMake(self.ms_navigationBarTopLayoutGuide.length,
0.0,
self.ms_navigationBarBottomLayoutGuide.length,
0.0) : UIEdgeInsetsZero;
_tableView.contentInset = _tableView.scrollIndicatorInsets = insets;
}
Notice the category methods in UIViewController
, this is how I implemented them:
@implementation UIViewController (MSLayoutSupport)
- (id<UILayoutSupport>)ms_navigationBarTopLayoutGuide {
if (self.parentViewController &&
![self.parentViewController isKindOfClass:UINavigationController.class]) {
return self.parentViewController.ms_navigationBarTopLayoutGuide;
} else {
return self.topLayoutGuide;
}
}
- (id<UILayoutSupport>)ms_navigationBarBottomLayoutGuide {
if (self.parentViewController &&
![self.parentViewController isKindOfClass:UINavigationController.class]) {
return self.parentViewController.ms_navigationBarBottomLayoutGuide;
} else {
return self.bottomLayoutGuide;
}
}
@end
Hope this helps :)
Related Topics
Phimageresultisdegradedkey/Phimagefileurlkey Is Not Found
Which Phassetcollection to Use for Saving an Image
How to Register Undomanager in Swift
Taking Photo with Custom Camera Swift 3
All Notifications Disappearing After Opening One of Them
How to Disable Qlpreviewcontroller Print Button
How to Get Index(Of:) to Return Multiple Indices
Crashlytics Does Not Show Crashes
iOS - Custom Table Cell Not Full Width of Uitableview
Private VS. Fileprivate on Declaring Global Variables/Consts in Swift3
How to Comment or Like a Photo in Facebook Through Fbconnect or Graph API in iPhone Sdk
In-App Purchase Sandbox Environment Loop
Swift - Add Gesture Recognizer to Object in Table Cell
Swift- Custom Uitableviewcell Delegate to Uiviewcontroller Only One Protocol Works
How to Convert Ciimage to Uiimage in Swift 3.0