iOS 【如何解決 UIView 在佈局時的"詭異"動畫效果】

王中堯發表於2018-11-15

 

場景

在專案中,UIView 的佈局時,發生了詭異的動畫效果。演示視訊如下:

iOS UIView 在佈局時的詭異動畫效果

 

根本

看到官方文件中的一句話:

// animatable. do not use frame if view is transformed since it will not correctly reflect the actual location of the view. use bounds + center instead.
open var frame: CGRect

含義:frame 是可以使用動畫的。如果檢視發生了形變,則不要使用 frame,因為它不能正確反映檢視的實際位置。使用 bounds + center 代替。

通俗的講就是 frame 發生改變時是不能使用動畫的

 

原因一

在視訊的場景中,點選輸入框,撥出鍵盤,由於鍵盤的彈出遮擋住了部分聊天介面,所以我們讓 ① 底部的聊天氣泡列表(UITableView)緩緩上移。此時恰巧使用者在瀏覽歷史聊天記錄,那麼我們預設是要讓 ② 聊天氣泡列表滾動到最近的一條氣泡資訊的(類似 WeChat 的效果),在滾動的過程中,cell 發生了重用,佈局(frame)隨之發生了變化。

上述事件中,藍色文字按閱讀順序恰好滿足了“詭異”動畫產生的兩個重要因素:① 產生了動畫效果;② frame 發生了改變。

 

解決一

首先我們看一下兩個重要因素的產生程式碼:

// ① table 整體上移 
self.letterDetailTable.transform = CGAffineTransform(translationX: 0, y: -move_h)
// ② table 滾動到最後一行
letterTable.scrollToRow(at: IndexPath(item: letterDetailVMArr.count - 1, section: 0), at: UITableViewScrollPosition.bottom, animated: false)

實際上這兩個因素共同導致了問題的發生,那麼我們完全可以控制其中一個不再出現。

要麼讓動畫不執行,要麼讓 frame 不發生改變

但是 cell 的滾動,佈局肯定要重新整理,所以 cell 的 frame 一定會發生改變,那麼就只能讓動畫不執行。我嘗試去關閉這個過程的動畫。

我首先將 程式碼① 不執行。然後執行程式,發現單單保留 程式碼② 也可以出現之前的詭異動畫。

 

懷疑

文件的描述不會錯,也就是 程式碼②? 即存在 frame 的改變,也存在動畫的執行,但是該方法我已經將動畫屬性置為 false,動畫是從何而來?

open func scrollToRow(at indexPath: IndexPath, at scrollPosition: UITableViewScrollPosition, animated: Bool)

 

原因二

其實每一個 UIView 都預設關聯一個 CALayer,我們將這個 layer 認為是 root layer。而所有的非 root layer 都有存在預設的隱式動畫,隱式動畫預設是 0.25s

在執行上面 程式碼② 時,cell 上面的子控制元件隱式動畫開啟,而 frame 也在實時發生的重新整理,這才是詭異動畫產生的真實原因。

 

解決二

/* Accessors for the "disableActions" per-thread transaction property.
 * Defines whether or not the layer's -actionForKey: method is used to
 * find an action (aka. implicit animation) for each layer property
 * change. Defaults to NO, i.e. implicit animations enabled. */
    
open class func disableActions() -> Bool

open class func setDisableActions(_ flag: Bool)
CATransaction.begin() // 1
CATransaction.setDisableActions(true) // 2 關閉layer隱式動畫

// 滾動到最後一行
scrollToRow(at: IndexPath(item: letterDetailVMArr.count - 1, section: 0), at: UITableViewScrollPosition.bottom, animated: false)

CATransaction.commit() // 3

 

相關文章