1. 程式人生 > >iOS自定義TabBar在螢幕旋轉時出現重影

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設計
最初的UI設計
第一種方法-同步的效果
第一種方法-同步的效果
第二種方法-移除的效果
第二種方法-移除的效果
可以看出來第一種方法的效果跟原本的初衷不一致了,字型大小還有title和image之間的距離也都變了。
從第一種的圖層中也能看出,因為沒有移除UITabBarButton,自定義的XXTabBarButton上面還是多了一個UITabBarButton,同時自定義的XXTabBarButton上的title和image都沒有了,所以影子才不那麼明顯。
第一種方法-同步適配的圖層
那麼使用第二種方法的選擇就很明顯了。