iOS 11 iPhone X 模拟器 TabBar 图标和标题呈现在顶部相互覆盖

Posted

技术标签:

【中文标题】iOS 11 iPhone X 模拟器 TabBar 图标和标题呈现在顶部相互覆盖【英文标题】:iOS 11 iPhone X simulator UITabBar icons and titles being rendered on top covering eachother 【发布时间】:2018-02-23 04:19:42 【问题描述】:

任何人对围绕 UITabBar 组件的 iPhone X 模拟器有疑问?

我的似乎将图标和标题呈现在彼此之上,我不确定我是否遗漏了任何东西,我也在 iPhone 8 模拟器中运行了它,以及一个看起来不错的实际设备显示在故事板上。

iPhone X:

iPhone 8

【问题讨论】:

我的类似问题:选择指示器图像视图在 iPhone X 中的 tabBar 视图中下沉了大约 16 个点。 仅供参考 - 不幸的是,这在 Xcode 9.2beta 中没有修复 这是一个仍然存在的uikit bug。约束必须设置得恰到好处。请参阅下面的答案! 我有类似的问题检查这个答案***.com/a/53524635/5441253 【参考方案1】:

我只需在 viewDidLayoutSubviews 中的 UITabBar 上调用 invalidateIntrinsicContentSize 即可解决此问题。

-(void)viewDidLayoutSubviews

    [super viewDidLayoutSubviews];
    [self.tabBar invalidateIntrinsicContentSize];

注意:标签栏的底部需要包含在主视图的底部,而不是安全区域,并且标签栏应该没有高度限制。

【讨论】:

我有一个没有高度限制的标签栏,受限于底部布局指南,这对我不起作用。还有其他想法吗? 当 homeVC 存在 VC A,然后关闭 A,并从 homeVC 推送 VC B 时不工作。这是一种解决方法***.com/a/47225653/1553324 添加此方法并结合将标签栏固定到其超级视图的底部(不是安全区域)对我有用。 我还需要添加[self.view layoutIfNeeded] 这行得通,但我知道我还必须注意标签栏按钮中 图像 的大小。在横向模式下(在紧凑型设备上,但不是 iPad 和 iPhone Plus 型号),系统使用紧凑型标签栏,它应该具有较小的图像,您可以使用标签栏按钮的landscapeImage 属性进行设置。推荐的尺寸在 [Apple HIG's] (developer.apple.com/design/human-interface-guidelines/ios/…) 中,位于 Custom Icon Size【参考方案2】:

VoidLess 提供的答案仅部分修复了 TabBar 问题。它修复了选项卡栏内的布局问题,但如果您使用隐藏选项卡栏的视图控制器,则选项卡栏在动画期间渲染不正确(重现它最好 2 有 2 个转场 - 一个模式和一个推送。如果您交替转场,您可以看到标签栏被渲染得不合适)。下面的代码解决了这两个问题。干得好苹果。

class SafeAreaFixTabBar: UITabBar 

var oldSafeAreaInsets = UIEdgeInsets.zero

@available(iOS 11.0, *)
override func safeAreaInsetsDidChange() 
    super.safeAreaInsetsDidChange()

    if oldSafeAreaInsets != safeAreaInsets 
        oldSafeAreaInsets = safeAreaInsets

        invalidateIntrinsicContentSize()
        superview?.setNeedsLayout()
        superview?.layoutSubviews()
    


override func sizeThatFits(_ size: CGSize) -> CGSize 
    var size = super.sizeThatFits(size)
    if #available(iOS 11.0, *) 
        let bottomInset = safeAreaInsets.bottom
        if bottomInset > 0 && size.height < 50 && (size.height + bottomInset < 90) 
            size.height += bottomInset
        
    
    return size


override var frame: CGRect 
    get 
        return super.frame
    
    set 
        var tmp = newValue
        if let superview = superview, tmp.maxY != 
        superview.frame.height 
            tmp.origin.y = superview.frame.height - tmp.height
        

        super.frame = tmp
        
    

Objective-C 代码:

@implementation VSTabBarFix 
    UIEdgeInsets oldSafeAreaInsets;



- (void)awakeFromNib 
    [super awakeFromNib];

    oldSafeAreaInsets = UIEdgeInsetsZero;



- (void)safeAreaInsetsDidChange 
    [super safeAreaInsetsDidChange];

    if (!UIEdgeInsetsEqualToEdgeInsets(oldSafeAreaInsets, self.safeAreaInsets)) 
        [self invalidateIntrinsicContentSize];

        if (self.superview) 
            [self.superview setNeedsLayout];
            [self.superview layoutSubviews];
        
    


- (CGSize)sizeThatFits:(CGSize)size 
    size = [super sizeThatFits:size];

    if (@available(iOS 11.0, *)) 
        float bottomInset = self.safeAreaInsets.bottom;
        if (bottomInset > 0 && size.height < 50 && (size.height + bottomInset < 90)) 
            size.height += bottomInset;
        
    

    return size;



- (void)setFrame:(CGRect)frame 
    if (self.superview) 
        if (frame.origin.y + frame.size.height != self.superview.frame.size.height) 
            frame.origin.y = self.superview.frame.size.height - frame.size.height;
        
    
    [super setFrame:frame];



@end

【讨论】:

完整解决方案。谢谢。 感谢您的回答。但是如何在 UITabBarController 类中使用它呢? @Jojo,考虑到通过代码填充标签栏需要多少代码,我总是使用情节提要创建 uitabbarcontroller。在那里,您可以为标签栏分配自定义类。希望这会有所帮助。 这个问题在真机上不会出现我已经在 iPhone11 pro iOS 13 上测试过【参考方案3】:

有一个技巧可以解决这个问题。

只需将您的 UITabBar 放在 UIView 中即可。

这真的对我有用。

或者你可以关注这个Link了解更多详情。

【讨论】:

这很好用,有一个只包含 UITabBar 的 uiview。将约束放在 UIView 上的安全区域(L、R 和底部),给它一个高度 (49),并将 UITabBar 约束到 UIView 的所有侧面。 这个解决方案对我有用。我没有使用 UITabController,只是 UITabBar。 为我工作。谢谢 这帮助了我!谢谢! 这可行,但是“不安全”区域可能与标签栏的颜色不同(如果超级视图和标签栏颜色不同)【参考方案4】:

为安全区域覆盖 UITabBar sizeThatFits(_)

extension UITabBar 
    static let height: CGFloat = 49.0

    override open func sizeThatFits(_ size: CGSize) -> CGSize 
        guard let window = UIApplication.shared.keyWindow else 
            return super.sizeThatFits(size)
        
        var sizeThatFits = super.sizeThatFits(size)
        if #available(iOS 11.0, *) 
            sizeThatFits.height = UITabBar.height + window.safeAreaInsets.bottom
         else 
            sizeThatFits.height = UITabBar.height
        
        return sizeThatFits
    

【讨论】:

太棒了。我刚刚设置了我的标签栏的自定义大小,想知道为什么它在 iPhoneX 上不起作用。其他解决方案对我不起作用,因为我使用的是 Tab Bar Controller,并且它不包含约束。 看起来这种技术在 Xcode 12 / iOS 14 中被破坏了。【参考方案5】:

我将此添加到我的自定义 UITabBarControllerviewWillAppear,因为提供的答案都不适合我:

tabBar.invalidateIntrinsicContentSize()
tabBar.superview?.setNeedsLayout()
tabBar.superview?.layoutSubviews()

【讨论】:

这帮助我解决了与将 tabBar 移动到屏幕顶部相关的问题。谢谢! 但不适合我:(【参考方案6】:

我遇到了同样的问题。

如果我在 UITabBar 的底部约束上将任何非零常量设置为安全区域:

它开始按预期工作......

这是我所做的唯一更改,我不知道它为什么会起作用,但如果有人这样做,我很想知道。

【讨论】:

这是我最后为了让它工作而做的,但是我不知道为什么或不明白它在做什么。对我来说,它仍然感觉像一个错误【参考方案7】:

移动标签栏离底部1点对我有用。

当然,这样做会导致您必须以某种方式填补空白,但文本/图标现在已正确定位。

【讨论】:

【参考方案8】:

啊哈!真的很神奇!

在诅咒 Apple 几个小时后,我终于明白了这一点。

UIKit 实际上确实为您处理了这个问题,并且似乎移动的选项卡栏项目是由于设置不正确(并且可能是一个实际的 UIKit 错误)。不需要子类化或背景视图。

如果 UITabBar 被限制在超级视图的底部,不限制在底部安全区域,它将“正常工作”。

它甚至可以在界面生成器中使用。

正确设置

    在界面构建器中,查看为 iPhone X,将 UITabBar 拖出到它与底部安全区域插图对齐的位置。当你放下它时,它应该看起来是正确的(一直填充到底部边缘的空间)。 然后您可以执行“添加缺少的约束”,IB 将添加正确的约束,您的标签栏将神奇地在所有 iPhone 上运行! (注意底部约束看起来有一个常数值,等于 iPhone X 不安全区域的高度,但这个常数实际上是 0)

有时还是不行

真正愚蠢的是,即使您添加了 IB 在上述步骤中添加的确切约束,您也可以实际看到 IB 中的错误!

    拖出一个 UITabBar 并且不要将其捕捉到底部安全区域插图 将前导、尾随和底部约束全部添加到超级视图(非安全区域) 奇怪的是,如果您在约束检查器中为底部约束执行“反转第一项和第二项”,这将自行修复。 ¯_(ツ)_/¯

【讨论】:

将所有约束从安全区域更改为超级视图并正常工作。传奇瑞恩【参考方案9】:

通过使用子类 UITabBar 应用 safeAreaInsets 修复:

class SafeAreaFixTabBar: UITabBar 

    var oldSafeAreaInsets = UIEdgeInsets.zero

    @available(iOS 11.0, *)
    override func safeAreaInsetsDidChange() 
        super.safeAreaInsetsDidChange()

        if oldSafeAreaInsets != safeAreaInsets 
            oldSafeAreaInsets = safeAreaInsets

            invalidateIntrinsicContentSize()
            superview?.setNeedsLayout()
            superview?.layoutSubviews()
        
    

    override func sizeThatFits(_ size: CGSize) -> CGSize 
        var size = super.sizeThatFits(size)
        if #available(iOS 11.0, *) 
            let bottomInset = safeAreaInsets.bottom
            if bottomInset > 0 && size.height < 50 
                size.height += bottomInset
            
        
        return size
    

【讨论】:

解决了部分问题,但不是全部。请看下面的完整答案 当我尝试展示 VCA,然后关闭 VCA,推送 VCB 时,它对我有用。 这对我有用,但我需要将底部约束放在超级视图而不是安全区域。 创建这个新类后我该怎么做?我尝试在我的应用程序中查看是否有任何出现的 UITabBar 并将它们替换为 SafeAreaFixTabBar... 我发现这些情况: func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool SafeAreaFixTabBar.appearance ().barTintColor = ... SafeAreaFixTabBar.appearance().backgroundColor = ... SafeAreaFixTabBar.appearance().tintColor = ... 但没有解决任何问题【参考方案10】:

在完成块中关闭模式后调用[tabController.view setNeedsLayout]; 为我解决了问题。

[vc dismissViewControllerAnimated:YES completion:^() 
     UITabBarController* tabController = [UIApplication sharedApplication].delegate.window.rootViewController;
     [tabController.view setNeedsLayout];
];

【讨论】:

【参考方案11】:

UITabBar 的高度增加至高于主页按钮/行,但在其原始位置绘制子视图并将 UITabBarItem 覆盖在子视图上。

作为一种解决方法,您可以检测到 iPhone X,然后将视图的高度缩小 32 像素,以确保标签栏显示在主线上方的安全区域中。

例如,如果您以编程方式创建 TabBar 替换

self.tabBarController = [[UITabBarController alloc] init];
self.window.rootViewController = self.tabBarController;

有了这个:

#define IS_IPHONEX (([[UIScreen mainScreen] bounds].size.height-812)?NO:YES)
self.tabBarController = [[UITabBarController alloc] init];    
self.window.rootViewController = [[UIViewController alloc] init] ;
if(IS_IPHONEX)
    self.window.rootViewController.view.frame = CGRectMake(self.window.rootViewController.view.frame.origin.x, self.window.rootViewController.view.frame.origin.y, self.window.rootViewController.view.frame.size.width, self.window.rootViewController.view.frame.size.height + 32) ;
[self.window.rootViewController.view addSubview:self.tabBarController.view];
self.tabBarController.tabBar.barTintColor = [UIColor colorWithWhite:0.98 alpha:1.0] ;
self.window.rootViewController.view.backgroundColor = [UIColor colorWithWhite:0.98 alpha:1.0] ;

注意:这很可能是一个错误,因为视图大小和标签栏布局由操作系统设置。它可能应该按照 Apple 在 iPhone X 人机界面指南中的屏幕截图显示:https://developer.apple.com/ios/human-interface-guidelines/overview/iphone-x/

【讨论】:

我明白了,我没有以编程方式创建标签栏,我使用情节提要来构建它,并将约束设置为屏幕底部。让我觉得奇怪的是,我们必须检测它是否是 iPhone X,然后对布局进行一些调整,而他们只是自行提高 TabBar 的高度,而无需更改任何代码。 这对我来说更像是一个错误,我想我会对此做一个错误报告,看看会发生什么。谢谢你的帮助!! 使用主屏幕边界的确切高度来确定应用程序是否在 iPhone X 上运行真的不是一个好主意。如果明年的模型是相同的磅值会发生什么?或者如果应用程序以横向模式运行? 透明度似乎不会影响我的图标设置不当。无论 UITabBar 是不透明的还是透明的,它们都搞砸了。 看起来你实际上是在框架的高度上增加了 32px,而不是从中减去?【参考方案12】:

我的情况是我在我的 UITabBarController 中设置了一个自定义 UITabBar 高度,如下所示:

  override func viewWillLayoutSubviews()             
        var tabFrame = tabBar.frame
        tabFrame.size.height = 60
        tabFrame.origin.y = self.view.frame.size.height - 60
        tabBar.frame = tabFrame
    

删除此代码是 TabBar 在 iPhone X 上正确显示的解决方案。

【讨论】:

【参考方案13】:

我找到的最简单的解决方案是在标签栏底部和 safeAreaLayoutGuide.bottomAnchor 底部之间添加一个 0.2 pt 的空间,就像这样。

tabBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -0.2)

【讨论】:

【参考方案14】:

当我尝试在自定义 TabBarController 中设置 UITabBar 的框架时遇到了同样的问题。

self.tabBar.frame = CGRectMake(0, self.view.frame.size.height - kTabBarHeight, self.view.frame.size.width, kTabBarHeight);

当我刚刚将它调整到新尺寸时,问题就消失了

if(IS_IPHONE_X)
    self.tabBar.frame = CGRectMake(0, self.view.frame.size.height - kPhoneXTabBarHeight, self.view.frame.size.width, kPhoneXTabBarHeight);

else
    self.tabBar.frame = CGRectMake(0, self.view.frame.size.height - kTabBarHeight, self.view.frame.size.width, kTabBarHeight);

【讨论】:

kPhoneXTabBarHeight 的值是多少? 我刚刚在 kTabBarHeight 的前一个值(即 45)上增加了 32 个点。但是您不必指定完全相同的值,对我来说,它也可以在不指定 tabBar 的框架的情况下正常工作。但如果你确实指定了它,你必须在 iPhone X 上调整额外的高度。 完全破解,但它是这些解决方案中唯一适合我的解决方案。 当你有一个自定义视图作为你的标签栏时,你喜欢使用这个技巧来使你的自定义视图适合 iPhone X。 @Hemang 不,我借用了这个代码的这个项目是 45。【参考方案15】:

我今天遇到了这个问题,在情节提要的视图控制器中手动添加了一个 UITabBar,当以常量 0 对齐到安全区域的底部时,我会遇到问题,但将其更改为 1 会解决问题。对于我的应用程序来说,让 UITabBar 比正常高 1 个像素是可以接受的。

【讨论】:

对我不起作用。我使用的也是手动添加的 UITabBar。【参考方案16】:

我已经为这个问题摸不着头脑了。它似乎与如何初始化 tabBar 并添加到视图层次结构有关。我还尝试了上述解决方案,例如在 UITabBar 子类中调用invalidateIntrinsicContentSize、设置框架以及bottomInsets。它们似乎暂时起作用,并且通过导致一些模棱两可的布局问题打破了其他一些场景或使标签栏倒退。当我调试问题时,我尝试将高度约束分配给 UITabBar 和 centerYAnchor,但都没有解决问题。我在视图调试器中意识到 tabBar 的高度在问题重现之前和之后都是正确的,这让我认为问题出在子视图中。我使用下面的代码成功地解决了这个问题,而没有回归任何其他场景。

- (void) viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator

    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    if (DEVICE_IS_IPHONEX())
    
        [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) 
            for (UIView *view in self.tabBar.subviews)
            
                if ([NSStringFromClass(view.class) containsString:@"UITabBarButton"])
                
                    if (@available (iOS 11, *))
                    
                        [view.bottomAnchor constraintEqualToAnchor:view.superview.safeAreaLayoutGuide.bottomAnchor].active = YES;
                    
                
            
         completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) 
            [self.tabBar layoutSubviews];
        ];
    

假设:我只为 iPhone X 这样做,因为目前它似乎无法在任何其他设备上重现。 基于 Apple 不会在未来的 iOS 版本中更改 UITabBarButton 类的名称的假设。 只有当苹果在 UITabBar 中添加更多内部子视图时,我们才在 UITabBarButton 上执行此操作,我们可能需要修改代码以进行调整。

请让我知道这是否有效,我们会接受建议和改进!

为此创建一个 swift 等效项应该很简单。

【讨论】:

我把它放在我的UITabBarController 中的viewDidAppear(animated:) 中,效果很好。谢谢! 你传递了什么参数 size 和 coordinator?从视图didAppear【参考方案17】:

来自本教程:

https://github.com/eggswift/ESTabBarController

在标签栏初始化之后,在 appdelegate 类中写入这一行

(self.tabBarController.tabBar as? ESTabBar)?.itemCustomPositioning = .fillIncludeSeparator

解决了我的标签栏问题。

希望它能解决你的问题

谢谢

【讨论】:

【参考方案18】:

选择标签栏并在检查器编辑器中设置“保存区域相对边距”复选框,如下所示:

【讨论】:

【参考方案19】:

我遇到了类似的问题,起初它被正确渲染,但在其中一个 tabBarItem 上设置 badgeValue 后,它破坏了布局。 在我已经创建的UITabBarController 子类上,它对我没有子类UITabBar 的工作是这样的。

-(void)viewDidLayoutSubviews 
    [super viewDidLayoutSubviews];
    NSLayoutYAxisAnchor *tabBarBottomAnchor = self.tabBar.bottomAnchor;
    NSLayoutYAxisAnchor *tabBarSuperviewBottomAnchor = self.tabBar.superview.bottomAnchor;
    [tabBarBottomAnchor constraintEqualToAnchor:tabBarSuperviewBottomAnchor].active = YES;
    [self.view layoutIfNeeded];

我使用 tabBar 超级视图来确保约束/锚点位于同一视图层次结构中并避免崩溃。

根据我的理解,由于这似乎是一个 UIKit 错误,我们只需要重写/重新设置标签栏约束,以便自动布局引擎可以再次正确布局标签栏。

【讨论】:

【参考方案20】:

如果您对 Tab Bar 有任何高度限制,请尝试将其移除。

遇到了同样的问题,删除它解决了这个问题。

【讨论】:

【参考方案21】:

我在情节提要中创建了新的 UITabBarController,并将所有视图控制器推送到这个新的 UITabBarConttoller。所以,在 iPhone X 模拟器中都运行良好。

【讨论】:

这也为我修复了它。 Xcode 9 IB 生成修复问题的 UITabBarController 的 XML 的方式有些不同。【参考方案22】:

对于 iPhone,你可以这样做,子类 UITabBarController。

class MyTabBarController: UITabBarController 

override func viewDidLayoutSubviews() 
    super.viewDidLayoutSubviews()
    if #available(iOS 11, *) 
        self.tabBar.heightAnchor.constraint(equalToConstant: 40).isActive = true
        self.tabBar.invalidateIntrinsicContentSize()
    
  

转到 Storyboard 并允许 Use Safe Area Layout Guide 并将 UITabbarController 的类更改为 MyTabBarController

P.S 此解决方案未针对通用应用程序和 iPad 进行测试。

【讨论】:

【参考方案23】:

尝试将初始屏幕更改为@3x 大小为 (3726 × 6624)

【讨论】:

如果我们使用 LaunchScreen Storyboard 会怎样? 用splash new size就好了【参考方案24】:

对我来说,删除 [self.tabBar setBackgroundImage:] 工作,可能是 UIKit 错误

【讨论】:

【参考方案25】:

对我来说,这解决了所有问题:

    override func viewDidLayoutSubviews() 
        super.viewDidLayoutSubviews()
        let currentHeight = tabBar.frame.height
        tabBar.frame = CGRect(x: 0, y: view.frame.size.height - currentHeight, width: view.frame.size.width, height: currentHeight)
    

【讨论】:

【参考方案26】:

我在情节提要中使用了UITabBarController,起初它对我来说还可以,但在升级到较新的 Xcode 版本后,它开始给我带来与 tabBar 高度相关的问题。

对我来说,解决方法是从情节提要中删除现有的UITabBarController,然后通过从界面构建器对象库中拖动它来重新创建。

【讨论】:

【参考方案27】:

对于以编程方式编写整个UITabBarController的人,可以使用UITabBarItem.appearance().titlePositionAdjustment调整标题位置

所以在这种情况下,您想在 Icon 和 Title 之间添加一个间隙,在 viewDidLoad 中使用它:

    override func viewDidLoad() 
        super.viewDidLoad()

        // Specify amount to offset a position, positive for right or down, negative for left or up
        let verticalUIOffset = UIOffset(horizontal: 0, vertical: hasTopNotch() ? 5 : 0)

        UITabBarItem.appearance().titlePositionAdjustment = verticalUIOffset
    

检测设备是否有刘海屏:

  func hasTopNotch() -> Bool 
      if #available(iOS 11.0, tvOS 11.0, *) 
        return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 20
      
      return false
  

【讨论】:

【参考方案28】:

对我来说,解决方案是在视图层次结构中选择 Tab Bar,然后转到: 编辑器 -> 解决自动布局问题,并在“选定视图”(不是“视图中的所有视图”)下选择“添加缺少的约束”。

【讨论】:

【参考方案29】:

我遇到了同样的问题,通过在标签栏布局后设置标签栏的项目解决了这个问题。

在我的情况下,问题发生在:

    有一个自定义视图控制器 在视图控制器的初始化程序中创建了一个 UITabBar 标签栏项目是在视图加载之前设置的 在视图中确实加载了标签栏被添加到视图控制器的主视图中 然后,按照您提到的方式呈现项目。

【讨论】:

【参考方案30】:

我认为这是来自 iPhoneX 的一个错误 UIKit。 因为它有效:

if (@available(iOS 11.0, *)) 
    if ([UIApplication sharedApplication].keyWindow.safeAreaInsets.top > 0.0) 
       self.tabBarBottomLayoutConstraint.constant = 1.0;
    
 

【讨论】:

你能提供更多关于什么是 tabBarBottomLayoutConstraint 的信息吗?

以上是关于iOS 11 iPhone X 模拟器 TabBar 图标和标题呈现在顶部相互覆盖的主要内容,如果未能解决你的问题,请参考以下文章

无法排毒找到 iPhone X 或 iPhone 11 之前的模拟器

XCode 11.3 崩溃,iOS 13.2.2 模拟器 iPhone X 和 watchOS 4.2 Apple Watch 42mm

iOS 11 和 iPhone X:UINavigationBar 的工具栏间距在嵌入到 UITabBarController 时不正确

`react-native run-ios` 返回错误:找不到 iPhone X 模拟器

iOS 11开发教程iOS11模拟器介绍一

iOS - 无法在真实设备 iPhone 11 中运行 Flutter 应用程序。iOS - 15.2,Xcode 13.2.1,它在模拟器中工作