iOS 11 iPhone X模拟器UITabBar图标和标题呈现在顶部,彼此覆盖


134

关于UITabBar组件的iPhone X模拟器有人遇到问题吗?

我的图标和标题似乎相互叠加,我不确定是否丢失了任何东西,我也在iPhone 8模拟器中运行过它,还有一个实际的设备,它看起来很不错,如上图所示。故事板。

iPhone X:

iPhone X UITabBar错误

iPhone 8

iPhone 8 UITabBar可以工作


1
我的类似问题:选择指示器图像视图沉没在tabBar视图中,在iPhone X中大约有16个点。–
Itachi

仅供参考-这是不幸的是没有固定在Xcode 9.2beta
tdios

这是一个仍然存在的uikit错误。必须设置正确的约束。请在下面查看我的答案!
RyanM

我有类似的问题检查此答案stackoverflow.com/a/53524635/5441253
Maxime Ashurov

Answers:


40

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

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

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


我有一个没有高度限制的标签栏,但仅限于“底部布局指南”,这对我不起作用。还有其他想法吗?
肯尼·怀兰德

1
当homeVC提供VC A,然后关闭A并从homeVC推送VC B时,此功能不起作用。这是一个变通方法stackoverflow.com/a/47225653/1553324
哎呀,2017年

1
将这种方法与将标签栏固定在其视图的底部(而不是安全区域)相结合,对我来说很有效。
Drew C

3
我还需要添加[self.view layoutIfNeeded]
Iulian Onofrei

1
这行得通,但我知道我还必须注意标签栏按钮中图像的大小。在横向模式下(在紧凑型设备上,但不适用于iPad和iPhone Plus机型),系统使用紧凑型标签栏,该标签栏应具有较小的图像,您可以使用landscapeImage标签栏按钮的属性进行设置。推荐的尺寸在[Apple HIG's](developer.apple.com/design/human-interface-guidelines/ios/…)中的“ 自定义图标大小”下
RobP

25

VoidLess提供的答案只能部分解决TabBar问题。它修复了选项卡内的布局问题,但是如果您使用隐藏该选项卡的viewcontroller,则该选项卡在动画过程中会错误地呈现(为重现效果,最好2个有2个segue-一个模态和一个推动。如果交替使用这些segue,则可以请参见不正确呈现的标签栏)。下面的代码解决了这两个问题。干得好苹果。

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

完整的解决方案。谢谢。
Sayalee Pote'3

4
谢谢您的回答。但是如何在UITabBarController类中使用它?
Jojo

2
@Jojo,考虑到通过代码填充标签栏需要多少代码,我总是使用情节提要板创建uitabbarcontroller。在那里,您可以为标签栏分配自定义类。希望这可以帮助。
Raimundas Sakalauskas

在真实设备上不会发生此问题,我已经在iPhone11 pro iOS 13上对此进行了测试
Akshay Jadhav,

22

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

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

这真的对我有用。

或者,您可以点击此链接以获取更多详细信息。


3
这很好用,有一个只包含UITabBar的uiview。将约束放置在UIView的安全区域(L,R和Bottom)上,为其设置高度(49),并将UITabBar约束到所有侧面的UIView上。
sdsykes

1
这个解决方案对我有用。我没有使用UITabController,只是使用UITabBar。
Riccardo Tesio

1
为我工作。谢谢
弗朗西斯

1
这帮助了我!谢谢!
格伦

2
此方法有效,但是“不安全”区域的颜色可能与选项卡栏的颜色不同(以防超级视图和选项卡栏的颜色不同)
iDev

16

覆盖UITabBar sizeThatFits(_)safeArea

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上不起作用。其他解决方案对我不起作用,因为我正在使用“标签栏控制器”,并且不包含约束。
英德日赫Rohlík

12

我将其添加到viewWillAppear我的自定义中UITabBarController,因为没有提供的答案对我有用:

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

1
这有助于我解决与将tabBar移至屏幕顶部有关的问题。谢谢!
杰伊,

但不适合我:(
尼克,

9

我有同样的问题。

在此处输入图片说明

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

在此处输入图片说明

它开始按预期运行...

在此处输入图片说明

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


1
这是我最终使它工作的结果,但是是的,我不知道为什么或不知道它在做什么。在我看来,它仍然像个虫子
adrian chen

7

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

当然,这样做会产生一定的差距,您必须以某种方式来填补这一差距,但是现在正确放置了文本/图标。


7

啊哈!这实际上是魔术!

经过数小时的咒骂,我终于明白了。

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

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

它甚至可以在Interface builder中使用。

正确设置

在IB工作

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

有时候还是不行

IB破损

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

  1. 拖出UITabBar,不要将其卡入底部安全区域插图
  2. 奇怪的是,如果在约束检查器中对底部约束进行“反转第一项和第二项”,则可以将前导约束,尾随约束和底部约束全部添加到超级视图(而非安全区域)中。¯_(ツ)_ /¯

将所有约束条件从安全区域更改为监督并起作用。传奇瑞安
RyanTCB '18 -10-2

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

解决了部分问题,但不是全部。请看下面的完整答案
Raimundas Sakalauskas

当我尝试展示VCA,然后关闭VCA,按下VCB时,它对我有用。
大勒

这对我有用,但我需要将下层约束置于超级视图而非安全区域。
yajra

创建新类后该如何处理?我尝试查看我的应用程序中是否出现UITabBar,然后将它们替换为SafeAreaFixTabBar ...我发现了这些情况:func application(_ application:UIApplication,didFinishLaunchingWithOptions launchOptions:[UIApplicationLaunchOptionsKey:Any]?)->布尔{SafeAreaFixTabBar.appearanceance ().barTintColor = ... SafeAreaFixTabBar.appearance()。backgroundColor = ... SafeAreaFixTabBar.appearance()。tintColor = ...但没有解决任何问题
user1019042

4

通过[tabController.view setNeedsLayout];在完成块中消除模态后调用为我解决了。

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

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] ;

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


1
我知道,我没有以编程方式创建“标签栏”,而是使用情节提要板将约束设置为屏幕底部来构建它。让我感到奇怪的是,我们必须检测它是否是iPhone X,然后再对布局进行一些调整,而它们只是通过TabBak自行提高了TabBar的高度,而没有任何代码更改。
阿德里安·陈

现在我觉得这更像是个错误,我想我将对此做一个错误报告,然后看看产生了什么。谢谢你的帮助!!
阿德里安·陈

使用主屏幕边界的确切高度来确定应用程序是否在iPhone X上运行确实不是一个好主意。如果明年的模型尺寸相同,该怎么办?或者,如果应用程序以横向模式运行?
rodrklacks

透明度似乎不会影响我的图标设置不正确。无论UITabBar是不透明还是透明,他们都搞砸了。
阿达玛(Adama)

看来您实际上是在向框的高度添加32px而不是从中减去?
佩里

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上正确显示的解决方案。


2

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

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

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

1
的值是kPhoneXTabBarHeight 多少?
Tiago Veloso

我只是在kTabBarHeight的先前值(之前为45)上添加了32点。但是您不必指定完全相同的值,对我来说,无需指定tabBar的框架也可以正常工作。但是,如果您指定了它,则必须调整iPhone X上的其他高度
。– Evgeny

1
完全骇客,但这是这些解决方案中唯一让我觉得合适的解决方案。
肯尼·怀兰德

当你有一个自定义视图作为你的TabBar你喜欢使用这个技巧,使为iPhone X.自定义视图契合
Hemang

1
@Hemang不行,这个项目是45岁,我从中借了此代码。
叶夫根尼

1

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


没为我工作。我使用的也是手动添加的UITabBar。
肯尼·怀兰德

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上执行此操作,我们可能需要修改代码以对此进行调整。

请让我知道这是否有效,欢迎您提出建议和改进!

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


我把它放在viewDidAppear(animated:)我的UITabBarController,它工作正常。谢谢!
艾伯特·博里

1
您通过了什么尺寸和协调员?从didappear的角度看
sulabh

1

从本教程中:

https://github.com/eggswift/ESTabBarController

并在标签栏初始化后在appdelegate类中写此行

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

解决了我的标签栏问题。

希望它能解决您的问题

谢谢


1

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

在此处输入图片说明


1

我有类似的问题,起初它是正确呈现的,但是在设置了badgeValuetabBarItem之一后,它破坏了布局。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超级视图来确保约束/锚位于相同的视图层次结构上,并避免崩溃。

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


0

如果对标签栏有任何高度限制,请尝试将其删除。

面对相同的问题,删除该问题即可解决。


0

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


这也为我解决了。Xcode 9 IB生成可解决此问题的UITabBarController XML的方式有所不同。
Tiago

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测试。


0

尝试将@ 3x大小的启动屏幕更改为(3726×6624)


如果我们正在使用LaunchScreen故事板怎么办?
Avineet Gupta

没关系,只需使用新的启动大小
Sob7y

0

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


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

0

UITabBarController在情节提要板上使用了a ,起初它对我来说还可以,但是升级到更新的Xcode版本后,它开始给我有关tabBar高度的问题。

对我而言,解决方法是UITabBarController从情节提要中删除现有内容,然后通过将其从界面生成器对象库中拖动来重新创建。


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
    }

检测设备是否具有Notch屏幕:

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

-1

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

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

  1. 有一个自定义的视图控制器
  2. 在视图控制器的初始化程序中创建一个UITabBar
  3. 标签栏项目是在加载视图之前设置的
  4. 在视图加载后,选项卡栏被添加到视图控制器的主视图中
  5. 然后,将按照您提到的方式渲染项目。

-1

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

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

您可以提供有关tabBarBottomLayoutConstraint是什么的更多信息吗?
user3099837

-1

我相信您可能会根据底部安全布局指南来布置选项卡栏。这可能不是您想要的,因为选项卡栏似乎确实进行了自己的计算,以将选项卡栏项目安全地放置在iPhone X的主行顶部。针对Superview底部设置底部约束。您应该看到它在iPhone X以及其他iPhone 上的布局正确。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.