137

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

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

iPhone X:

iPhone X UITabBar 错误

iPhone 8

iPhone 8 UITabBar 工作

4

32 回答 32

41

我可以通过简单地在 viewDidLayoutSubviews 中的 UITabBar 上调用 invalidateIntrinsicContentSize 来解决这个问题。

-(void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    [self.tabBar invalidateIntrinsicContentSize];
}

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

于 2017-09-27T01:54:42.913 回答
26

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
于 2017-11-10T14:57:50.043 回答
22

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

只需将您的 UITabBar 放在 UIView 中。

这真的对我有用。

或者您可以点击此链接了解更多详情。

于 2017-11-24T11:11:31.263 回答
19

覆盖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
    }
}
于 2018-05-15T08:52:35.453 回答
13

我将此添加到viewWillAppear我的 customUITabBarController中,因为提供的答案都不适合我:

tabBar.invalidateIntrinsicContentSize()
tabBar.superview?.setNeedsLayout()
tabBar.superview?.layoutSubviews()
于 2018-05-08T12:02:36.447 回答
10

我有同样的问题。

在此处输入图像描述

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

在此处输入图像描述

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

在此处输入图像描述

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

于 2017-12-14T19:51:04.967 回答
7

将标签栏从底部移开 1 点对我有用。

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

于 2017-12-06T20:53:46.563 回答
7

啊哈!这真的很神奇!

经过数小时的诅咒苹果后,我终于明白了这一点。

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

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

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

正确设置

在IB工作

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

有时它仍然不起作用

在 IB 中被打破

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

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

通过使用子类 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
    }
}
于 2017-09-29T10:33:51.227 回答
4

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

[vc dismissViewControllerAnimated:YES completion:^(){ 
     UITabBarController* tabController = [UIApplication sharedApplication].delegate.window.rootViewController;
     [tabController.view setNeedsLayout];
}];
于 2018-04-04T19:50:14.710 回答
2

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/

于 2017-09-14T09:50:45.973 回答
2

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

tabBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -0.2)
于 2018-09-27T22:51:54.790 回答
2

我的情况是我在我的 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 上正确显示的解决方案。

于 2018-02-08T13:45:41.523 回答
1

我已经为这个问题挠头了。它似乎与 tabBar 的初始化方式和添加到视图层次结构的方式有关。我还尝试了上述解决方案,例如调用invalidateIntrinsicContentSize、设置框架以及bottomInsets在 UITabBar 子类中。它们似乎暂时起作用,并且通过导致一些模棱两可的布局问题打破了其他一些场景或使标签栏倒退。当我调试问题时,我尝试将高度约束分配给 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 上执行此操作,我们可能需要修改代码以进行调整。

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

为此创建一个快速的等效项应该很简单。

于 2017-11-16T01:21:07.407 回答
1

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

于 2017-09-28T09:39:06.030 回答
1

当我试图在我的自定义 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);
}
于 2017-09-14T23:27:04.277 回答
1

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

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

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

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

于 2018-09-13T04:49:50.147 回答
1

从本教程:

https://github.com/eggswift/ESTabBarController

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

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

解决了我的标签栏问题。

希望它能解决你的问题

谢谢

于 2017-12-11T13:55:03.343 回答
1

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

在此处输入图像描述

于 2018-01-05T15:37:53.497 回答
0

对于 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()
    }
  }
}

转到情节提要并允许使用安全区域布局指南并将UITabbarController的类更改为MyTabBarController

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

于 2017-12-19T07:39:45.097 回答
0

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

于 2018-01-21T17:05:19.723 回答
0

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

于 2018-01-25T06:21:15.377 回答
0

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

    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)
    }
于 2018-02-07T14:36:26.777 回答
0

我在我的故事板中创建了新的 UITabBarController 并将所有视图控制器推送到这个新的 UITabBarConttoller。因此,在 iPhone X 模拟器中一切正常。

于 2017-10-25T09:33:07.347 回答
0

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

于 2020-08-30T13:42:07.330 回答
0

UITabBarController在情节提要中使用 a ,起初它对我来说工作得很好,但是在升级到较新的 Xcode 版本后,它开始给我带来与 tabBar 高度相关的问题。

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

于 2019-08-13T07:42:07.267 回答
0

如果您对 Tab Bar 有任何高度限制,请尝试删除它。

面临同样的问题,删除它解决了这个问题。

于 2017-09-25T12:13:13.807 回答
0

对于那些以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
  }
于 2020-05-22T15:14:35.913 回答
-1

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

if (@available(iOS 11.0, *)) {
    if ([UIApplication sharedApplication].keyWindow.safeAreaInsets.top > 0.0) {
       self.tabBarBottomLayoutConstraint.constant = 1.0;
    }
} 
于 2017-09-25T17:40:49.010 回答
-1

我相信您可能会根据底部安全布局指南布置标签栏。这可能不是您想要的,因为标签栏似乎会自行计算以将标签栏项目安全地放置在 iPhone X 的主线顶部。针对超级视图底部设置底部约束。您应该会看到它在iPhone X和其他 iPhone 上的布局正确。

于 2017-10-09T00:17:33.677 回答
-1

我在启动屏幕故事板上遇到了这个错误,无法通过子类化进行自定义。经过一些分析,我设法找出原因 - 我UITabBar直接使用了。切换到UITabBarController封装 a 的 aUITabBar解决了这个问题。

注意 - iOS 11.1 可能会修复此问题,因此可能不会影响真正的 iPhone X 应用程序(因为 iPhone X 很可能会在运行 iOS 11.1 的情况下发布)。

于 2017-10-09T00:50:02.187 回答
-1

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

就我而言,问题发生在以下情况:

  1. 有一个自定义视图控制器
  2. 在视图控制器的初始化程序中创建了一个 UITabBar
  3. 标签栏项目是在视图加载之前设置的
  4. 在视图中确实加载了标签栏被添加到视图控制器的主视图中
  5. 然后,按照您提到的方式呈现这些项目。
于 2017-09-14T20:49:37.090 回答