IOS11 iPhone X 模拟器 UITabBar 图标和标题在顶部呈现,相互覆盖

有人对 iPhone X 模拟器的 UITabBar 组件有异议吗?

我的设备似乎是把图标和标题一个接一个地呈现出来,我不确定我是否漏掉了什么,我还在 iPhone 8的模拟器上运行了它,还有一个实际的设备,它看起来很好,就像故事板上显示的那样。

IPhone X:

iPhone X UITabBar bug

IPhone 8

iPhone 8 UITabBar works

34871 次浏览

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

作为解决方案,你可以检测 iPhone X,然后将视图高度缩小32px,以确保标签栏显示在主线上方的安全区域。

例如,如果您正在以编程方式创建 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] ;

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

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

在我的案例中,问题发生在:

  1. 有一个自定义视图控制器
  2. 在视图控制器的初始值设定项中创建 UITabBar
  3. 选项卡栏项在视图加载之前设置
  4. 在视图中,选项卡栏被添加到视图控制器的主视图中
  5. 然后,如您所述呈现项。

当我试图在自定义 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);
}

如果标签栏有任何高度限制,请尝试移除它。

面对同样的问题,解决了这个问题。

我认为这是一个来自 iPhoneX 的 bug UIKit。 因为它管用:

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

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

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

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

今天我在故事板中手动添加了一个 UITabBar 到视图控制器中,当对齐到安全区域底部的常数为0时,我会遇到这个问题,但是把它改为1就可以解决这个问题。让 UITabBar 比正常高出1个像素对我的应用程序来说是可以接受的。

修正了使用子类 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
}
}

我相信您可能正在设置的标签栏对底部的安全布局指南。这可能不是你想要的,因为标签栏似乎做自己的计算,以定位标签栏项目安全在 iPhone X。 设置您的底部约束对应于 Superview 底部的主行顶部。您应该看到它在 IPhone X和其他 iPhone 上的布局是正确的。

我在我的启动屏幕故事板上遇到了这个 bug,它不能通过子类定制。经过一些分析,我设法找出了原因-我正在使用一个 UITabBar直接。切换到封装了 UITabBarUITabBarController解决了这个问题。

注意: 这可能会被 iOS 11.1修复,所以可能不会影响真正的 iPhone X 应用程序(因为很可能 iPhone X 会发布运行 iOS 11.1的版本)。

我在故事板中创建了新的 UITabBarController,并将所有视图控制器都推到了这个新的 UITabBarConttoller 上。所以,所有工作在 iPhone X 模拟器很好。

VoidLess 提供的答案仅部分修复了 TabBar 问题。它修复了标签栏内部的布局问题,但是如果你使用视图控制器来隐藏标签栏,那么标签栏在动画过程中会被错误地渲染(为了重现它,最好有两个片段——一个模式,一个推送。如果交替使用接续符,您可以看到标签栏被渲染得不合适)。下面的代码解决了这两个问题。干得好,苹果。

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
}
}
}

目标 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

这个问题我已经摸不着头脑了。它似乎与如何初始化 tabBar 并将其添加到视图层次结构有关。我还尝试了上面的解决方案,比如调用 invalidateIntrinsicContentSize、设置帧,以及在 UITabBar 子类中调用 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];
}];
}
}

假设: 我这样做只是为了 iPhoneX,因为它似乎不会在任何其他设备上重现。 基于这样一个假设: 在未来的 iOS 版本中,苹果不会更改 UITabBarButton 类的名称。 我们在 UITabBarButton 上这样做只是因为如果苹果在 UITabBar 中添加了更多的内部子视图,我们可能需要修改代码来进行调整。

请让我知道如果这个工程,将开放的建议和改进!

为此创建一个快速等效应用程序应该很简单。

我们可以用一个小窍门来解决这个问题。

只要把你的 UITabBar 放在 UIView 中。

这对我真的很管用。

或者你可以按照这个 林克了解更多细节。

移动标签栏 离底部还有1分对我来说很有用。

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

从这个教程:

Https://github.com/eggswift/estabbarcontroller

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

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

解决了标签栏的问题。

希望它能解决你的问题

谢谢

我也有同样的问题。

enter image description here

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

enter image description here

它开始像预期的那样工作..。

enter image description here

这是我唯一的改变,我不知道为什么它的工作,但如果有人做我想知道。

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

前往故事板,并允许 使用安全区域布局指南和改变类的 UITabbar 控制器MyTabBarController

此解决方案不测试万能应用程序和 iPad 的情况下。

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

enter image description here

尝试改变屏幕大小@3x 是(3726 × 6624)

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

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

    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)
}

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

解决了我通过调用 [tabController.view setNeedsLayout];后,在完成块的模式。

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

我把这个添加到我的自定义 UITabBarControllerviewWillAppear中,因为所有提供的答案都不适合我:

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

覆盖 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
}
}

啊哈! 这真的是魔法!

经过几个小时的诅咒苹果公司,我终于明白了这一点。

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

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

甚至在 Interface Builder 里也有效。

正确的设置

Working in IB

  1. 在 Interface Builder 中,以 iphonex 的形式,拖动一个 UITabBar 到安全区域的底部。当你放下它,它应该看起来正确(填补所有的方式空间的底部边缘)。
  2. 然后你可以做一个“添加缺失的约束”和 IB 将添加正确的约束和您的标签栏将神奇地工作在所有的 iPhone!(注意,底部约束看起来像是一个常量值,等于 iPhone X 不安全区域的高度,但是这个常量实际上是0)

有时候还是不行

Broken in IB

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

  1. 拖出一个 UITabBar,不要将其对齐到底部的安全区域
  2. 将前导、尾随和底部约束全部添加到 Superview (非安全区域) 奇怪的是,如果您在约束检查器中为底部约束执行“反向第一和第二项”操作,那么这会自动修复。_ (ツ) _/

我也遇到过类似的问题,一开始渲染是正确的,但是在 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 superview 来确保约束/锚位于相同的视图层次结构中,从而避免崩溃。

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

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

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

我在故事板中使用了 UITabBarController,起初它对我来说还行,但是在升级到新的 Xcode 版本后,它开始给我带来与 tabBar 高度相关的问题。

对我来说,修复是从故事板中删除现有的 UITabBarController,并通过从 Interface Builder 对象库中拖动它来重新创建。

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

因此,在这种情况下,你想添加一个间隔之间的图标和标题使用它在 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
}

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