[英]layoutSubviews vs frame didSet to detect when the view's frame changed
假設我有一個UIView子類,當其框架更改時,該子類需要更新某些內容。 例如,當它在偶數像素上時,它將是紅色,而當它在奇數像素上時將是藍色。
我已經看到至少四種方法可以做到這一點:
override func layoutSubviews() { super.layoutSubviews() backgroundColor = calculateColorFromFrame(frame) }
view.frame
didSet
:
override var frame: CGRect { didSet { backgroundColor = calculateColorFromFrame(frame) } }
在視圖的框架上添加一個KVO觀察器。
在視圖的包裝UIViewController上,實現viewDidLayoutSubviews()
。
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() view.updateBackgroundColorForNewFrame() }
您知道其他方式嗎?
這些方法中的哪一個是首選,並且在本質上優於另一種? 蘋果的官方建議是什么?
layoutSubviews
:您可以依靠在大小已更改的視圖上調用該方法,也可以依靠該視圖接收到setNeedsLayout
。 如果視圖的位置發生變化,但是其大小沒有變化,則系統不會自動在該視圖上調用layoutSubviews
。
frame didSet
:適用於使用autoresizingMask
布局的視圖。 它不適用於使用普通約束布局的視圖。 相反,自動布局會設置視圖的center
和bounds
。 但是,這些是實現細節,可能會在不同版本的UIKit中發生變化。 我在Xcode 10 beta 6隨附的iOS 12.0 Simulator上進行了測試。
frame
KVO:出於某些原因,該功能在某些情況下與#2相同。 另外, frame
沒有文檔證明是KVO兼容的,因此允許UIKit在不通知觀察者的情況下更改frame
。
viewDidLayoutSubviews
:僅當視圖是視圖控制器的view
屬性時才有效,然后僅在view
收到layoutSubviews
消息時才有效(請參閱#1)。 同樣重要的是要理解,當調用此方法時,視圖控制器的view
及其直接子視圖已經被布局,但是更深的子視圖尚未被布局。 (見這個答案的更全面的解釋。)
還要注意,視圖的frame
是根據其layer
某些屬性計算的:該圖層的position
, bounds.size
, anchorPoint
和transform
。 如果有任何方法直接設置這些圖層屬性,則以上方法均無效。
因此,在視圖位置發生變化時,沒有特別好的通知方法。 技術上正確的方法可能是使用CAAction
。 當圖層檢測到其position
正在改變時,它會請求其委托(對於視圖圖層來說,是視圖本身)來執行某項操作。 它通過發送actionForLayer:forKey:
消息進行詢問。 因此,您可以在視圖子類中重寫action(forLayer:forKey:)
以可靠地得到通知。 但是,該層在更改其position
(並因此更改其frame
) 之前會先請求操作。 更改其position
后,它將運行該動作。 因此,您必須經過以下舞蹈:
override func action(for layer: CALayer, forKey event: String) -> CAAction? {
class MyAction: CAAction {
init(view: MyView, nextAction: CAAction?) {
self.view = view
self.nextAction = nextAction
}
let view: MyView
let nextAction: CAAction?
func run(forKey event: String, object: Any, arguments: [AnyHashable : Any]?) {
// THIS IS WHERE YOU LOOK AT THE NEW FRAME AND ACT ACCORDINGLY.
print("This is the action. Frame now: \(view.frame)")
nextAction?.run(forKey: event, object: object, arguments: arguments)
}
}
let action = super.action(for: layer, forKey: event)
if event == "position" {
return MyAction(view: self, nextAction: action)
} else {
return action
}
}
actionForLayer:forKey:
的UIView
實現通常返回nil
。 但是在動畫塊內(包括當界面在縱向和橫向之間旋轉時),它返回(對於某些鍵)一個將CAAnimation
安裝在圖層上的動作。 因此,如果有的話,運行super
返回的動作很重要。
我可能對您的問題理解不正確或對Apple文檔的理解有誤,但以下是我對使用什么來查看鏡架變化的看法:
我很好奇的是,您如何有效地計算奇數和偶數像素,因為幀位置和大小基於點,並且一個點可以跨越一個以上的像素。
我希望這能回答您的問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.