Where should I be setting autolayout constraints when creating views programmatically
IosCocoa TouchAutolayoutIos Problem Overview
I see different examples where constraints are set. Some set them in viewDidLoad
/ loadView
(after the subview was added). Others set them in the method updateViewConstraints
, which gets called by viewDidAppear
.
When I try setting constraints in updateViewContraints
there can be a jumpiness to the layout, e.g. slight delay before the view appears. Also, if I use this method, should I clear out existing constraints first i.e. [self.view [removeConstraints:self.view.constraints]
?
Ios Solutions
Solution 1 - Ios
I set up my constraints in viewDidLoad
/loadView
(I'm targeting iOS >= 6). updateViewConstraints
is useful for changing values of constraints, e.g. if some constraint is dependent on the orientation of the screen (I know, it's a bad practice) you can change its constant
in this method.
Adding constraints in viewDidLoad
is showed during the session "Introduction to Auto Layout for iOS and OS X" (WWDC 2012), starting from 39:22. I think it's one of those things that are said during lectures but don't land in the documentation.
UPDATE: I've noticed the mention of setting up constraints in Resource Management in View Controllers:
> If you prefer to create views programmatically, instead of using a
> storyboard, you do so by overriding your view controller’s loadView
> method. Your implementation of this method should do the following:
>
> (...)
>
>3.If you are using auto layout, assign sufficient constraints to each of
> the views you just created to control the position and size of your
> views. Otherwise, implement the viewWillLayoutSubviews
and
> viewDidLayoutSubviews
methods to adjust the frames of the subviews in
> the view hierarchy. See “Resizing the View Controller’s Views.”
UPDATE 2: During WWDC 2015 Apple gave a new explanation of updateConstraints
and updateViewConstraints
recommended usage:
> Really, all this is is a way for views to have a chance to make changes to constraints just in time for the next layout pass, but it's often not actually needed.
> All of your initial constraint setup should ideally happen inside Interface Builder.
> Or if you really find that you need to allocate your constraints programmatically, some place like viewDidLoad is much better.
> Update constraints is really just for work that needs to be repeated periodically.
> Also, it's pretty straightforward to just change constraints when you find the need to do that; whereas, if you take that logic apart from the other code that's related to it and you move it into a separate method that gets executed at a later time, your code becomes a lot harder to follow, so it will be harder for you to maintain, it will be a lot harder for other people to understand.
> So when would you need to use update constraints?
> Well, it boils down to performance.
> If you find that just changing your constraints in place is too slow, then update constraints might be able to help you out.
> It turns out that changing a constraint inside update constraints is actually faster than changing a constraint at other times.
> The reason for that is because the engine is able to treat all the constraint changes that happen in this pass as a batch.
Solution 2 - Ios
I recommend creating a BOOL and setting them in the -updateConstraints
of UIView (or -updateViewConstraints
, for UIViewController).
-[UIView updateConstraints]
: (apple docs)
> Custom views that set up constraints themselves should do so by overriding this method.
Both -updateConstraints
and -updateViewConstraints
may be called multiple times during a view's lifetime. (Calling setNeedsUpdateConstraints
on a view will trigger this to happen, for example.) As a result, you need to make sure to prevent creating and activating duplicate constraints -- either using a BOOL to only perform certain constraint setup only once, or by making sure to deactivate/remove existing constraints before creating & activating new ones.
For example:
- (void)updateConstraints { // for view controllers, use -updateViewConstraints
if (!_hasLoadedConstraints) {
_hasLoadedConstraints = YES;
// create your constraints
}
[super updateConstraints];
}
Cheers to @fresidue in the comments for pointing out that Apple's docs recommend calling super
as the last step. If you call super
before making changes to some constraints, you may hit a runtime exception (crash).
Solution 3 - Ios
This should be done in ViewDidLoad, as per WWDC video from Apple and the documentation.
No idea why people recommend updateConstraints. If you do in updateConstraints you will hit issues with NSAutoresizingMaskLayoutConstraint with auto resizing because your views have already taken into account the auto masks. You would need to remove them in updateConstraints to make work.
UpdateConstraints should be for just that, when you need to 'update' them, make changes etc from your initial setup.
Solution 4 - Ios
Do it in view did layout subviews method
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
Solution 5 - Ios
I have this solution to change constraints before those who are in the storyboard are loaded. This solution removes any lags after the view is loaded.
-(void)updateViewConstraints{
dispatch_async(dispatch_get_main_queue(), ^{
//Modify here your Constraint -> Activate the new constraint and deactivate the old one
self.yourContraintA.active = true;
self.yourContraintB.active= false;
//ecc..
});
[super updateViewConstraints]; // This must be the last thing that you do here -> if! ->Crash!
}
Solution 6 - Ios
You can set them in viewWillLayoutSubviews: too:
override func viewWillLayoutSubviews() {
if(!wasViewLoaded){
wasViewLoaded = true
//update constraint
//also maybe add a subview
}
}
Solution 7 - Ios
This worked for me:
Swift 4.2
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Modify your constraints in here
...
}
Although honestly I am not sure if it is worth it. It seems a bit slower to load than in viewDidLoad(). I just wanted to move them out of the latter, because it's getting massive.
Solution 8 - Ios
Following example is to pass any view to another class. create my view from storyboard
Swift 5.0
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async {
self.abcInstance = ABC(frame: self.myView.frame)
}
}
If you miss DispatchQueue.main.async
, it will take time to update constraints in viewWillAppear
. Create myView in storyboard and give constraints same as screen width & height, then try printing frame of myView. It will give accurate value in DispatchQueue.main.async
or in viewDidAppear
but not give accurate value in viewWillAppear
without DispatchQueue.main.async
.
Solution 9 - Ios
Add your constraints in viewWillLayoutSubviews()
to add constraints programmatically
See Apple Documentation in Custom Layout Section
> If possible, use constraints to define all of your layouts. The > resulting layouts are more robust and easier to debug. You should only > override the viewWillLayoutSubviews or layoutSubviews methods when you > need to create a layout that cannot be expressed with constraints > alone.