iOS自定義TabBar在螢幕旋轉時出現重影
自定義TabBar在螢幕旋轉時出現重影這種情況以前並沒有出現,應該是在iOS11之後才有的,最近又開始開發以前的專案才碰到。看起來是這樣的:
就像兩層一樣,在圖層中看時也確實是多了一層按鈕:
所以我想是不是自定義tabBar在旋轉的時候不小心多調了一次新增button的方法,然而並沒有找到多餘的呼叫,並且在自定義tabBar的新增button和layoutSubviews方法中的斷點也並沒有走。
但是可以確定的就是在螢幕旋轉時出現的重影,因此我把螢幕旋轉時對UI的適配程式碼全註釋掉。這樣在橫屏的時候就能看到了tabBar上的重影。如圖:
既然自定義tabbar中佈局子檢視的程式碼根本沒有多餘的呼叫,為什麼看起來效果就像多建立添加了一遍button呢?為了驗證這個問題,我在tabbarController中寫了個方法來列印tabbar的子檢視:
- (void)logTabBarSubviews {
NSLog(@"subviews.count = %ld", self.tabBar.subviews.count);
for (NSInteger i = 0; i < self.tabBar.subviews.count; i++) {
UIView *view = self.tabBar.subviews[i];
NSLog(@"view.frame = %@, class = %@", NSStringFromCGRect(view.frame), NSStringFromClass(view.class ));
}
}
然後在螢幕旋轉的時候呼叫這個方法,由於是旋轉前觸發的,就延遲3秒再呼叫:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
XXTabBarController *tabbarVC = (XXTabBarController *)self.tabBarController;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[tabbarVC logTabBarSubviews];;
});
});
}
列印輸出:
subviews.count = 7
view.frame = {{0, 0}, {736, 49}}, class = _UIBarBackground
view.frame = {{0, 0}, {414, 49}}, class = XXTabBar
view.frame = {{40, 1}, {128.99999998675452, 48}}, class = UITabBarButton
view.frame = {{173, 1}, {128.00000000165568, 48}}, class = UITabBarButton
view.frame = {{305, 1}, {128.00000000165568, 48}}, class = UITabBarButton
view.frame = {{437, 1}, {127.99999998675452, 48}}, class = UITabBarButton
view.frame = {{569, 1}, {126.99999998675452, 48}}, class = UITabBarButton
可以看出UITabBar上除了自定義的tabBar以外多出了5個UITabBarButton,這些多出的UITabBarButton在圖層的左側欄也能看到,由於xcode在橫屏時檢視圖層會先強制轉為豎屏,所以就把打印出來了。(可能橫豎屏切換後UITabBar的高度改變了,系統在做橫豎屏適配時發現沒有UITabBarButton就建立了。因為我這個自定義的tabBar還是UITabBar的子檢視。)
UITabBarButton是系統的私有類,我們無法使用和檢視。系統既然多為我添加了一層button,那麼解決方法就有兩種:
一. 讓自定義的XXTabBarButton跟系統建立的UITabBarButton同步,也做橫豎屏適配。只需要在自定義的tabBarController的螢幕將要旋轉方法中重置自定義tabBar的frame即可。
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
self.xxTabBar.frame = CGRectMake(0, 0, size.width, size.height);
//讓棧頂控制器處理UI適配
UINavigationController *nav = (UINavigationController *)[self.viewControllers objectAtIndex:self.selectedIndex];
[nav.topViewController viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}
二. 移除系統新增的UITabBarButton。系統動畫一般在0.25秒結束,所以在0.3秒後移除。
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
// 刪除系統自動生成的UITabBarButton
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (UIView *childView in self.tabBar.subviews) {
if ([childView isKindOfClass:[UIControl class]]) {
[childView removeFromSuperview];
}
}
//讓棧頂控制器處理UI適配
UINavigationController *nav = (UINavigationController *)[self.viewControllers objectAtIndex:self.selectedIndex];
[nav.topViewController viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
});
});
}
最後結果發現第一種方法的效果不如第二種。
最初的UI設計
第一種方法-同步的效果
第二種方法-移除的效果
可以看出來第一種方法的效果跟原本的初衷不一致了,字型大小還有title和image之間的距離也都變了。
從第一種的圖層中也能看出,因為沒有移除UITabBarButton,自定義的XXTabBarButton上面還是多了一個UITabBarButton,同時自定義的XXTabBarButton上的title和image都沒有了,所以影子才不那麼明顯。
那麼使用第二種方法的選擇就很明顯了。