背景
TABAnimated
是一個自動化生成骨架屏的工具。
骨架屏是一個定制化程度較高的需求。而TABAnimated
的自動生成策略和開發者的自定制需求天然地存在沖突。
所以在自動化生成元素之后,為開發者開了一個定制接口,引入了調整回調(adjustBlock)
又因為自定制本身需要的是定制某個動畫元素,此時需要定位這個動畫元素。
所以引入了tagIndex(遞歸深度下標),用于標記動畫元素,定義某個元素是哪一個。
所以,一旦iOS系統原始的圖層發生變化,TABAnimated則需要進行適配,以保證與舊的tagIndex匹配
因為涉及到新的處理策略,舊的錯誤說明,所以本文特地做一個簡要說明。
iOS14系統變化
iOS14對UITableViewCell
,UITableViewHeaderFooterView
等類型視圖做了調整。
以UITableViewCell
為例,其增加了一個與UITableViewCellContentView
同級的view,其class類型為_UISystemBackgroundView
。
與此同時,在_UISystemBackgroundView
之上,又加上了一個與之大小一致的UIView,這個view的唯一特征是和_UISystemBackgroundView的大小一樣。
隱藏問題和初步適配策略
因為_UISystemBackgroundView
的class類型是特殊的,所以可以通過className過濾掉。還需要把_UISystemBackgroundView
上的UIView移除掉,才能完全解決這個問題。
這個view它主要有兩種特征,一個是大小和_UISystemBackgroundView
一致,另一個是位于_UISystemBackgroundView
的subViews棧底。
因為我們無法預測iOS的視圖層級是否還會發生變化,所以采取了第一種策略。
在多種視圖的測試下,發現_UISystemBackgroundView
仍然具備特殊性,它在高度為浮點數的場景下,會比原始的TableViewCell高零點幾pt,但也不是取整造成的。
所以不得不更改過濾策略,將寬高大于TableViewCell的view也過濾掉。
過濾代碼如下:
if ((CGRectEqualToRect(view.bounds, rootView.bounds)
|| view.bounds.size.width > rootView.bounds.size.width
|| view.bounds.size.height > rootView.bounds.size.height)) {
return YES;
}
TABAnimated的舊錯誤及兼容策略
背景:在自動化生成的策略中,當視圖的組件很多(包含隱藏組件),大概率存在動畫元素局部覆蓋,同時有很多元素不會被開發者用到,所以增加了篩選策略,對于寬高小于3pt的動畫元素進行自動過濾,同時支持過濾條件自定制。
因為UITableViewCell
最早就包含UITableViewCellContentView
,所以早期直接過濾了這個視圖,并且不會生成動畫元素,我把它定義為根源性移除。但是對于寬高過濾機制而言,使用根源性移除會產生歧義,所以在生成了動畫元素的基礎之上,采取了標記移除。
所以TABAnimated有兩種移除策略,一種是根源性移除,另一種是標記移除。
TABAnimated的舊錯誤在于,將_UITableViewHeaderFooterContentView
進行了標記移除。而根據上述的定義,毫無疑問應該使用根源性移除。此次增加的過濾條件,_UITableViewHeaderFooterContentView
又完美符合了(會先被根源性移除)。為了向下兼容,我們必須將其進行標記移除,不然tagIndex會和過去的不一致,所以我們必須過濾掉_UITableViewHeaderFooterContentView
。
完整過濾代碼如下
if ((CGRectEqualToRect(view.bounds, rootView.bounds)
|| view.bounds.size.width > rootView.bounds.size.width
|| view.bounds.size.height > rootView.bounds.size.height)
&& ![view isKindOfClass:[NSClassFromString(@"_UITableViewHeaderFooterContentView") class]]) {
return YES;
}
不得不保留舊的標記移除條件
// 標記移除
if ([view isKindOfClass:[NSClassFromString(@"_UITableViewCellSeparatorView") class]] ||
[view isKindOfClass:[NSClassFromString(@"_UITableViewHeaderFooterContentView") class]]) {
needRemove = YES;
}
所以,此處的邏輯有些怪異,這都是為了彌補過去不合理的邏輯造成的,但是我們又必須向下兼容。希望后續的朋友看到此處代碼,不要太無語。
適用版本
2.5.0及以上