美文网首页iOS开发指北
UITabBarController系列——TabBarItem

UITabBarController系列——TabBarItem

作者: xjkstar | 来源:发表于2022-03-21 14:42 被阅读0次

问题

兼容性测试发现 iOS 13.3的iPhone 8 Plus上,一个TabBar页面底部BarItem文案变成省略号


8p-rect.png

影响范围

设备类型 结果
iOS >= 13.4的iPhone 8 Plus 正常
iOS 13.0~13.3的刘海屏iPhone 异常省略号
iOS 13.0~13.3的其他非刘海屏iPhone 异常省略号
iOS < 13.0的iPhone 8 Plus 正常

问题排查

iOS 13开始,UITabBar样式设置用standardAppearance,并且iOS 15又新增了scrollEdgeAppearance。所以一般写如下兼容性代码:

- (void)setUpTabBarAppearance {
    UIViewController *codeVC = [[UIViewController alloc] init];
    codeVC.tabBarItem.title = @"扫码1";
    [self addChildViewController:codeVC];

    UIViewController *ticketVC2 = [[UIViewController alloc] init];
    ticketVC2.tabBarItem.title = @"扫码2";
    [self addChildViewController:ticketVC2];

……………………

    UIColor *selectedColor = kSelectedColor;
    NSDictionary *textAttNormal = @{ NSForegroundColorAttributeName : [UIColor whiteColor],
                                     NSFontAttributeName : [UIFont systemFontOfSize:18]};
    NSDictionary *textAttSelected = @{ NSForegroundColorAttributeName : selectedColor,
                                       NSFontAttributeName : [UIFont boldSystemFontOfSize:18]};
    
   if (@available(iOS 13.0, *)) {
        UITabBarAppearance * tabBarAppearance = [UITabBarAppearance new];

        UITabBarItemAppearance *barItemAppearance= [[UITabBarItemAppearance alloc] initWithStyle:UITabBarItemAppearanceStyleInline];
        barItemAppearance.normal.titleTextAttributes = textAttNormal;
        barItemAppearance.selected.titleTextAttributes = textAttSelected;

        tabBarAppearance.stackedLayoutAppearance = barItemAppearance;
        tabBarAppearance.stackedLayoutAppearance = barItemAppearance;
        
        self.tabBar.standardAppearance = tabBarAppearance;

        if (@available(iOS 15.0, *)) {
            self.tabBar.scrollEdgeAppearance = tabBarAppearance;
        }
    }
    else {
        // 问题出在这里
        [self.viewControllers enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            [obj.tabBarItem setTitleTextAttributes:textAttNormal forState:UIControlStateNormal];
            [obj.tabBarItem setTitleTextAttributes:textAttSelected forState:UIControlStateSelected];
        }];
    }
}

查看UILabel尺寸

用Xcode自带的Debug View Hierarchy查看界面元素,确定TabBarItem的文案是UILabel,内容、样式都是上面设置的。对比正常、异常的情况,发现异常时UILabel的尺寸不同


8p-rect.png

打印UILabel

在viewDidLayoutSubviews中、viewDidAppear中打印UILabel

// 不使用UITabBarController.selectedIndex, if the selected view controller is currently the More navigation controller, this property contains the value NSNotFound.
    NSUInteger index = [self.tabBar nvm_selectedIndex];
    UIView *tabBarView = [self.tabBar nvm_tabBarButtonAtIndex:index];

    for (UILabel *obj in tabBarView.subviews) {
        if ([obj isKindOfClass:[UILabel class]]) {
            NSLog(@"== Label %@ : %@", obj.text, obj.font);
        }
    }

发现viewDidLayoutSubviews时,文字已生效,但字号未生效。

== Label 扫码1 : <UICTFont: 0x139eaaca0> font-family: ".SFUI-Medium"; font-weight: normal; font-style: normal; font-size: 10.00pt

而viewDidAppear时,文字、字号才设置上,但字号并未生效。此时调用sizeToFit可触发生效。同时注意到UICTFont属性的实例对象变了。

== Label 扫码1 : <UICTFont: 0x139f67740> font-family: ".SFUI-Semibold"; font-weight: bold; font-style: normal; font-size: 18.00pt

至此,明确问题原因在文字属性未生效。

返回去看上面代码:title设置都会执行,而字号设置,>= iOS 13、 < iOS 13的设备会执行不同逻辑,推测@available(iOS 13.0, *) 块中的代码导致titleTextAttributes未生效问题。

看起来是iOS 13.0~13.3上,standardAppearance会延迟生效?

尝试1

@available(iOS 13.0, *)改为@available(iOS 13.4, *)
titleTextAttributes部分生效,省略号问题解决,文字大小展示正确,但未选中状态颜色不符合预期

尝试2

看代码的else代码块,在 < iOS 13.0时,用UIBarItem setTitleTextAttributes设置样式,查看此方法:

/* You may specify the font, text color, and shadow properties for the title in the text attributes dictionary, using the keys found in NSAttributedString.h.
 */
- (void)setTitleTextAttributes:(nullable NSDictionary<NSAttributedStringKey,id> *)attributes forState:(UIControlState)state API_AVAILABLE(ios(5.0)) UI_APPEARANCE_SELECTOR;

注意到setTitleTextAttributes并未废弃,可以在任何版本上调用。所以先统一设置UIBarItem的setTitleTextAttributes,再针对iOS 13.0以上设置standardAppearance,尝试后问题解决。

解决方案

最终解决方案,先继续设置未废弃的旧方法,再根据iOS设置新方法。此种方案,作为日后新接口适配的策略执行下去。

附上正确代码:

- (void)setUpTabBarAppearance {
    UIViewController *codeVC = [[UIViewController alloc] init];
    codeVC.tabBarItem.title = @"扫码1";
    [self addChildViewController:codeVC];

    UIViewController *ticketVC2 = [[UIViewController alloc] init];
    ticketVC2.tabBarItem.title = @"扫码2";
    [self addChildViewController:ticketVC2];

……………………

    UIColor *selectedColor = kSelectedColor;
    NSDictionary *textAttNormal = @{ NSForegroundColorAttributeName : [UIColor whiteColor],
                                     NSFontAttributeName : [UIFont systemFontOfSize:18]};
    NSDictionary *textAttSelected = @{ NSForegroundColorAttributeName : selectedColor,
                                       NSFontAttributeName : [UIFont boldSystemFontOfSize:18]};

    // 在iOS 13.3上,UITabBarAppearance在首次appear时不生效,包括viewDidLayoutSubviews结束时,而iOS 13.4开始无此问题
    // 因此无论iOS版本,都设置老方法,解决兼容性问题
    [self.viewControllers enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj.tabBarItem setTitleTextAttributes:textAttNormal forState:UIControlStateNormal];
        [obj.tabBarItem setTitleTextAttributes:textAttSelected forState:UIControlStateSelected];
    }];
    
    if (@available(iOS 13.0, *)) {
        UITabBarAppearance * tabBarAppearance = [UITabBarAppearance new];

        UITabBarItemAppearance *barItemAppearance= [[UITabBarItemAppearance alloc] initWithStyle:UITabBarItemAppearanceStyleInline];
        barItemAppearance.normal.titleTextAttributes = textAttNormal;
        barItemAppearance.selected.titleTextAttributes = textAttSelected;
        tabBarAppearance.stackedLayoutAppearance = barItemAppearance;
        tabBarAppearance.stackedLayoutAppearance = barItemAppearance;
        
        self.tabBar.standardAppearance = tabBarAppearance;
        
        if (@available(iOS 15.0, *)) {
            self.tabBar.scrollEdgeAppearance = tabBarAppearance;
        }
    }
}

相关文章

网友评论

    本文标题:UITabBarController系列——TabBarItem

    本文链接:https://www.haomeiwen.com/subject/kifwdrtx.html