When and why is updateConstraints called? And when overriding, what kind work is meant to be done in the override?
I've read and watched lots of information on how Auto Layout works, but i can't seem wrap my head around it completely.
Do you update the constant of any special interest constraints you saved references to, or to do customising do you remove all constraints from your view and recreate them entirely like one guy did in a WWDC video?
Some thoughts about the matter are to be found here:
https://oleb.net/blog/2015/08/how-to-use-updateconstraints/
And of course there is occasional information on this topic in WWDC videos.
But on the whole the answer is probably: don't override updateConstraints. It isn't called very often under normal circumstances, so you'd only need to override it if you were also calling setNeedsUpdateConstraints, and the result is probably an unnecessarily confusing and elaborate architecture. There is generally no need for an updateConstraints override. The WWDC 2018 video on auto layout shows that such overrides are often written incorrectly and just cause a bunch of highly inefficient layout "churn". If you need to change constraints, just change them at the point where the need for the change arises.
As the documentation now says:
It is almost always cleaner and easier to update a constraint immediately after the affecting change has occurred. For example, if you want to change a constraint in response to a button tap, make that change directly in the button’s action method.
You should only override this method when changing constraints in place is too slow, or when a view is producing a number of redundant changes.
That last sentence refers to a situation that is extraordinarily rare, and in any case you're unlikely to know what to do to implement updateConstraints properly to gain efficiency. So don't.
Related
I had a conversation with my colleagues about setNeedsLayout() and layoutIfNeeded().
Starting from the conclusion, my colleague says
setNeedsLayout() 'may' or 'should' be called before layoutIfNeeded(), because layoutIfNeeded() may not conduct layout if we don't set the 'layout flag' to true by calling setNeedsLayout().
Whereas, my thought is, calling both of them in the same place will lead to the same result as we call layoutIfNeeded() only. Because...
What I know about them is, as described here and here,
setNeedsLayout() is just invalidating the current layout and then the coming update cycle would take care of the rest.
layoutIfNeeed() is updating the layout immediately, so don't want to the coming update cycle.
Simply saying, async call and sync call. And that's it.
If there is no animation, I don't recall I've seen a lot of cases I had to use layoutIfNeeded(Perhaps one or two). Most of the cases, it worked quite well only with setNeedsLayout(). Moreover, if there is a case that needs to call both of them in the same place, I would rather say it's a bug from UIKit.
Can someone explain me which one is correct, and why?
First, a little discussion what these methods do, the answer to your question is below that.
From the documentation of setNeedsLayout():
This method makes a note of the request and returns immediately. Because this method does not force an immediate update, but instead waits for the next update cycle, you can use it to invalidate the layout of multiple views before any of those views are updated.
Usually, you need to call this if there's some external factor affecting the layout that UIKit doesn't know about (e.g. old fashioned manual layouting code by overriding layoutSubviews()). It doesn't trigger an immediate re-layout, that usually only happens on the next main runloop iteration. This allows you to mark several views as needing layouts, you can call it on the same view multiple times within the same runloop iteration, but UIKit only does the actual layouting once (as I just noted, usually on the next runloop iteration).
But if you need the layout to get settled right now (e.g. because you need to measure its result), you need to call layoutIfNeeded().
If no layout updates are pending, this method exits without modifying the layout or calling any layout-related callbacks.
This is a crucial point about layoutIfNeeded(): if the layout system doesn't know there are pending changes, it simply won't do any. Calling setNeedsLayout() is the way we tell the layout system that there are changes that require a layout run.
If you only use auto-layout and manipulate just the constraints of the view you are about to re-layout, you can skip setNeedsLayout() since that has already been called indirectly; but setNeedsLayout() is very cheap, so there's no harm in calling it if you're unsure.
So, after calling setNeedsLayout(), do you need to call layoutIfNeeded()? To simply update the view on screen, no, you do not need to call layoutIfNeeded() since UIKit will do so on its own on the next main runloop iteration. In fact, you might negatively impact render performance if you always call layoutIfNeeded() even though your code doesn't need the updated layout right away since this can result in multiple layouting runs within the same runloop iteration.
But if you rely on the layout information immediately (for example, you need to measure the height of a view), you do need to call it. Just beware to have your design not do this too often.
Your comparison with "async" and "sync" is indeed somewhat fitting: in a way, you can see setNeedsLayout() as triggering an asynchronous operation and layoutIfNeeded() "waits" until it has happened (that's not what's actually going on, but from a certain point of view that is the observable effect).
I usually set all my auto layout code in the updateCOnstratins method of my view controller for the constraints of all the subclasses defining the view. Then in the subviews I place my constraints in the updateConstraints methods there. This makes me have a property of every single view in my class so I can reference it later on after I set translates.... to false. But Im reading that you don't have to set it in updateConstraints. Just not I read an article where the person says an apple engineer said that if the constraints are only made once then you can put them pretty much where ever. Yet, if you have constrains that change during the views lifecycle you place them in updateConstraints? Here are the links http://swiftandpainless.com/where-to-put-the-auto-layout-code/ http://swiftandpainless.com/dont-put-view-code-into-your-view-controller/.
So where should It go? Was this just an old way of doing this and now it has changed?
What you said in your post is what you would generally want to do. Put any constraints that might change in updateConstraints. This also means you should keep a reference to them to be able to update them or remove/replace them. Any static ones can be put after your initialization code (the init method of a UIView or the viewDidLoad method of a UIViewController, for instance). The only real requirement there is you can only add constraints to views that are actually in a view hierarchy together, so anytime after you've added the appropriate views would be fine.
There is usually no reason not to put your constraint creation code in viewDidLoad, which has the advantage of being called only once. For constraints that change, I like to associate that code with whatever directly precipitates the change, such as a change in size class or the removal or insertion of a view.
I want to practice creating simple apps using no storyboard. I am able to do the constraints programmatically (slowly) but I want to also practice separating my code into MVC. Is there a particular place/method that I am supposed to write the programatic constraints? Or does it not matter?
Good discussion in the comments. My thoughts, based on that discussion?
With an understanding that the question is subjective, you place your constraints:
The earliest in a view controller's life cycle where they work.
As "close" to the view as possible.
If it's something common, make it as universal as possible.
Understand how your specifics fit into everything.
(Understand, the question isn't limited to constraints. It could apply to hierarchies, UI, even database tables when you get down to it!)
Sticking to constraints, and my answer....
(1) Use the UIViewController and UIView lifecycles.
Generally the view life cycle is loadView, viewDidLoad, viewWillAppear, viewWillLayoutSubviews, viewDidLayoutSubviews, and viewDidAppear. great SO answer detailing this.
I believe that loadView is too early for constraints, but not viewDidLoad - **provided you aren't expecting to know the frame size. While many say viewDidLayoutSubviews is the right place for that, I've found that viewWillLayoutSubviews most times works just as well. Either way, get your constraints set as soon as possible!
(2) Do it as close to the view as possible.
If you have subviews - I have a "ToolBar" class of objects - you want the constraints, at least as much as possible, to be coded inside the class. For instance, in my tool bar, it slides out, has buttons, and even rotates upon orientation. The only constraints not inside these classes is for orientation - that owner is (and I believe should be) the view controller instantiating it.
(3) Make it universal.
I plan to use this tool bar across a few apps. So the first thing I did was add it to a framework. This framework was needed because I had an app that I delivered a photo editing exension - and the "edit" screen is as much the same as possible. In the end I move all my constraints there. (At least as much as possible.) Anything that I believe is reusable.
(4) Understand the specific requirements of your app.
This should be obvious. If you need to code for various orientations, use arrays and activate/deactivate them. (YES, a common mistake is replacing them! That's setting yourself up for some major headaches.)
If you can keep things active, declare the constraint, set `isActive = true1, and forget about it. If you need to adjust that constraint's constant or multiplier, in the declaration name it and then where you need to alter it, do it.
My conclusion? Auto layout is a very useful tool - more so in code. But the placement of code is like asking "how does one code an OOP app for auto rentals" or " how does one design a database for auto rentals". It not just an art, there are many answers. These are the "rules" I try to follow - YMMV.
To get started with this style of development I recommend checking out Let's Build That App as he goes through very in-depth examples of setting up complex apps entirely in code, without storyboards.
The way he structures the constraints is using a custom implementation of UIView, that way your view code is separated from the ViewController. Then, in the viewDidLoad method you can instantiate your implementation of UIView with something like self.view = MyView().
I wrote a few apps like this. The major drawbacks are that it can become very difficult to make quick adjustments, and you really need to learn about all the different types of constraints you can use.
Here's a pastebin of some extensions I used when doing this. I hope this helps.
I know, how to create autolayout constraints with size classes perfectly.
But I am not getting when to call layOutIfNeeded(), setNeedsDisplay(), layOutSubViews(), setUpdateConstraints().
Can someone tell how to properly call this function to update UI after constraints changed.
Another my concern is, when to call only single function out of above and call with other functions.
It must be really clear that your layout is calculated by a routine that is called at specific times at runtime.
It could happen that you need to modify the current layout, for instance changing the constant of a specific constraint. If you just do that you will notice no changes in the UI, this is because the routine is still not called.
What you can do is force the layout routine to be called, and you do that by these two methods:
setNeedsLayout : You are telling that the view needs a layout. The next time the routine is called knows that this view need to have a layout refresh
layOutIfNeeded(): You don't want to wait the next call and you are telling the system to force layout calculation ASAP
Same thing happen with setNeedsDisplay() and displayIfNeeded(), with the first you tell that a view needs to be rendered again, and with the second you tell do ASAP.
If you are asking yourself why, the reason is performance. Is useless to re-render everything each time, this lazy approach will save system resources.
The methods - setNeedsUpdateConstraints and -updateConstraintsIfNeeded are basically the same concept applied to constraints, the difference is that you will not see any changes in UI until you force a layout, why this methods are useful? because sometimes you need to check after a change in constraint if the layout is still valid without changing the aspect of your UI.
Is there any possible way to detect every change on User Interface during runtime??
I'm trying to find all objects in the current app interface.
I'm trying to to get all nodes inspecting recursively the main Window, but, for example, how to know if the top viewcontroller changes or if it's added a uiview dynamically, or is presented a modalview??
The main objective is to have a library to do this..
Any idea, help?
Thanks!
You could write your own library based on this, using advanced Objective-C techniques. I do not recommend you to do this, since it mostly breaks MVC patterns on iOS. Depends on what do you want to use it for, maybe analytics?
So these are the options I believe, if you want to actively inspect UIView hierarchy. All options are pretty complicated though.
Swizzle methods such as addSubview and removeFromSuperview of UIView, so you could know when changes like that happens. Including the getters of frame and bounds, if you wish to know the position.
You could use KVO to watch properties such as: subviews, frame, bounds, superview to notice any changes. But at one point you would have to add the same object as the observer (could be singleton).
Decide for an interval that is fired by a NSTimer and go through the hierarchy recursively beginning at keyWindow on UIApplication. This would have a big performance impact though.
There may be other options, but these are the ones I believe to be the best choices.