SetNeedLayOut VS LayOutIfNeed

原文連接地址:http://www.iosinsight.com/set...ios

Let’s take a look at the difference between setNeedsLayout and layoutIfNeeded.讓咱們來看下setNeedsLayout與layoutIfNeeded之間的區別.git

This post will explain the difference using a concrete example of animating constraint changes. Before discussing both methods, it is crucial to first have an understanding of the main run loop in an iOS app, and the Auto Layout process, so bear with me for a moment.這篇文章中利用基於約束的動畫來解釋上述二者之間的區別,在咱們討論以前,相當重要的一步是要了解 app應用中的runloop的概念、 Auto Layout 的工做流程, 因此大家對我要有些耐心哈.github

As part of the normal startup process, UIApplication in iOS starts the main run loop for an app, which runs on the main thread. The main run loop processes events (such as user touches) and handles updates to view-based interfaces. As events occur, such as touch, location updates, motion, and multimedia control, the run loop finds the appropriate handler for the events, calling appropriate methods, which call other methods, and so on. At some moment in time(在某個時刻), all events will have been handled and control will return to the run loop.(全部的事件所有處理完而且runloop重新擁有控制權) Let’s label this point where control is returned to the run loop as the update cycle(當run loop 從新得到控制權的時候就認爲是一個更新週期), since this is how Apple refers to it in some of their documentation. You could use other terminology to conceptualize it, such as a break in the action, the redraw cycle, or a free moment.app

While events are being processed and dispatched, and as changes to views are requested, they are not always acted upon right away. Instead, the system records the changes and marks views as needing to be redrawn(系統會記錄view的改變並作一些須要從新繪製的標記). When are the changes drawn? It is in this update cycle, after all existing events have been handled, that attention is now turned to redrawing. Of course to the user it does not look like there is a wait to redraw (in general), because all of this is happening quickly. Knowing that there is an interval periodically (時間間隔), between sets of events being processed, where the system now takes on the task of updating the layout and display, is important for understanding setNeedsLayout and layoutIfNeeded.less

The difference between these two methods can be now be described by referencing the update cycle described above.異步

The method setNeedsLayout for a UIView tells the system that you want it to layout and redraw that view and all of its subviews(繪製view以及subViews), when it is time for the update cycle. This is an asynchronous activity, because the method completes and returns immediately, but it isn’t until some later time that the layout and redraw actually happens, and you don’t know when that update cycle will beasync

In contrast(相對來講), the method layoutIfNeeded is a synchronous call that tells the system you want a layout and redraw of a view and its subviews, and you want it done immediately without waiting for the update cycle. When the call to this method is complete, the layout has already been adjusted and drawn based on all changes that had been noted prior (高權限) to the method call.ide

So, stated succinctly(簡單說), layoutIfNeeded says update immediately please, whereas setNeedsLayout says please update but you can wait until the next update cycle.oop

When I first encountered(碰到) these methods, I can remember thinking that the if needed part of layoutIfNeeded made it sound less urgent or even optional, compared to the setNeedsLayout method that sounded more like a definitive statement to perform a layout. However, names can be a little deceiving.(不要望文取義哈)佈局

Before we look at code, there is one more important concept regarding Auto Layout and the update cycle. There are actually three phases associated with the layout and drawing of the views. The first is update of constraints (constraint pass), which happens bottom up. The second is layout of views and subviews (layout pass), which happens top down and is dependent on constraint settings. The third phase is the display pass, where the views get redrawn based on the layout pass.

Auto Layout工做的三個階段:1 約束更新(從內到外) 2佈局過程(從外到內) 3 繪製

Let’s take a look at how the two methods, setNeedsLayout and layoutIfNeeded, play out for the case of animating constraint changes for a UIView. You should clearly see the impact in this scenario.

The app for this discussion is a simple Single View Application, and I’ve created one blue colored UIView within the main view, as well as a button to trigger update of the blue view height constraint constant. Here is the storyboard after I’ve completed these configurations.

圖片連接:
https://i0.wp.com/www.iosinsi...
https://i1.wp.com/www.iosinsi...

You can animate constraint changes within an animation block, and that is exactly what we’ll do here. With the blueHeight constraint IBOutlet in the ViewController, we’ll be able to update the constraint and cause the blue rectangle to grow or shrink when the button is clicked, and it will be animated. Here is the entire ViewController.

import UIKit
   class ViewController: UIViewController {
     
       @IBOutlet weak var blueHeight: NSLayoutConstraint!
        
       @IBAction func heightPressed(sender: AnyObject) {
        
            view.layoutIfNeeded()
            
            if(self.blueHeight.constant == 25.0)
            {
                self.blueHeight.constant = self.view.bounds.height - 100.0
            }
            else
            {
                self.blueHeight.constant = 25.0
            }
            UIView.animateWithDuration(2.0) {
                self.view.layoutIfNeeded()
            }
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
        }
     
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
        
    }

When the button is pressed, the first thing you see is a call to view.layoutIfNeeded(). Remember that this method forces an immediate layout and display update. You might wonder why this is done before we do the constraint change. Apple considers this a best practice in order to ensure that any previous changes awaiting the update cycle are completed(這是一個比較好的習慣), so I’ve added it for that reason.

The next section of code checks the current value of the constraint constant, so that the button click is alternating between the original constraint of 25, and the view height minus 100.

When the constraint is updated like this, that automatically does the equivalent of a setNeedsLayout, so nothing else would be required to have the view updated during the next update cycle. We could have no more code and you’d see the update, but it would not be animated. In our case, a 2 second animation block is added, and inside that block, we force an immediate layout via the layoutIfNeeded method. Because this layout happens synchronously, the frame movement from the constraint change is captured(捕捉) within the animation block, and therefore if you run the app now, you see how the blue view animates over the 2 seconds to the large or small size.

當咱們改變約束值的時候,就會在後臺進行一個和setNeedsLayout效果相似操做:先標記這個view須要更新,而後在下一個循環週期內更新. 而在塊中 調用layoutIfNeeded方法的做用是:讓view當即刷新, 與此同時 「view.frame的改變可以被塊捕捉到」 就產生了動畫.

Let’s replace layoutIfNeeded with setNeedsLayout, so that the animation block is now as follows.

UIView.animateWithDuration(2.0) {
        self.view.setNeedsLayout()
    }

Now what we are doing within the animation block is marking the view as needing a layout update, but it isn’t forced immediately. Instead, the setNeedsLayout method returns with the view simply being on the list to be updated(須要更新的列表中) in the next update cycle. The net(最終) effect is that nothing takes place in the animation block that can be animated, because there is no change to the view within that block.

Clicking the button in this case immediately updates the view size based on the updated constraint, instead of animating the update. Wait a minute, why is it immediate if we didn’t use layoutIfNeeded? That is a perfectly valid question which I’ll address.

It helps to know that animation actually takes place on its own thread, and not on the main thread where the update cycle occurs. Furthermore, animation is triggered at the next update cycle. During animation, progress updates(更新處理的操做) are sent on the main thread, which is why we see incremental changes for animations. In this case though, animation isn’t making the changes to the view and sending updates for display. Instead, once the code completes in the IBAction, and there are no more events being processed, an update cycle occurs and does an immediate layout pass without any animation.

To our eyes, the change in size of the blue view happens instantaneously. At first it seems counter-intuitive because we didn’t use layoutIfNeeded to force an immediate update. However, we didn’t make the view update happen within the context of the animation block, so it appeared to be immediate because we marked it for update and the update cycle occurred, which is also where the animation would have started. So, instead of starting an animation of the constraint change and frame movement from the update cycle, the view update happened instantaneously in the update cycle because our code had marked that the view needed a layout update via the setNeedsLayout.

上面兩段大概含義:動畫效果的完成在單獨一個線程,runloop、約束更新、發生在主線程.這樣就能夠實現 一種異步行爲 :當view 約束改變的時候 可以被動畫塊(異步)捕捉到,然而setNeedLayout 只是標記view須要在下一個循環週期更新. 而在下一個循環週期更新時,在主線程中發送更新的事件, 可是此時動畫上下文也許已經結束了(上一個循環週期執行)就捕捉不到本次的約束改變事件,因此就看不到動畫效果.

If you want to run this app for yourself and see the animation with layoutIfNeeded versus no animation with setNeedsLayout, the code with animation is here in GitHub.

示例代碼地:https://github.com/lmacfadyen...

若有發現錯誤,請指正謝謝

相關文章
相關標籤/搜索