有人对围绕 UITabBar 组件的 iPhone X 模拟器有疑问吗?
我的似乎将图标和标题呈现在彼此之上,我不确定我是否遗漏了任何东西,我还在 iPhone 8 模拟器中运行了它,以及一个看起来不错的实际设备,如图所示故事板。
iPhone X:
iPhone 8
有人对围绕 UITabBar 组件的 iPhone X 模拟器有疑问吗?
我的似乎将图标和标题呈现在彼此之上,我不确定我是否遗漏了任何东西,我还在 iPhone 8 模拟器中运行了它,以及一个看起来不错的实际设备,如图所示故事板。
iPhone X:
iPhone 8
我可以通过简单地在 viewDidLayoutSubviews 中的 UITabBar 上调用 invalidateIntrinsicContentSize 来解决这个问题。
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self.tabBar invalidateIntrinsicContentSize];
}
注意:标签栏的底部需要包含在主视图的底部,而不是安全区域,并且标签栏应该没有高度限制。
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
覆盖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
}
}
我将此添加到viewWillAppear
我的 customUITabBarController
中,因为提供的答案都不适合我:
tabBar.invalidateIntrinsicContentSize()
tabBar.superview?.setNeedsLayout()
tabBar.superview?.layoutSubviews()
将标签栏从底部移开 1 点对我有用。
当然,这样做你会得到一个空白,你必须以某种方式填补,但文本/图标现在已正确定位。
经过数小时的诅咒苹果后,我终于明白了这一点。
UIKit 实际上确实为您处理了这个问题,并且似乎移动的标签栏项目是由于设置不正确(并且可能是一个实际的 UIKit 错误)。不需要子类化或背景视图。
它甚至可以在界面生成器中使用。
真正愚蠢的是,即使您添加了 IB 在上述步骤中添加的确切约束,您也可以实际看到 IB 中的错误!
通过使用子类 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
}
}
[tabController.view setNeedsLayout];
通过在完成块中关闭模式后调用为我解决了问题。
[vc dismissViewControllerAnimated:YES completion:^(){
UITabBarController* tabController = [UIApplication sharedApplication].delegate.window.rootViewController;
[tabController.view setNeedsLayout];
}];
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/
我发现的最简单的解决方案是在标签栏的底部和 safeAreaLayoutGuide.bottomAnchor 的底部之间简单地添加一个 0.2 pt 的空间,就像这样。
tabBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -0.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 上正确显示的解决方案。
我已经为这个问题挠头了。它似乎与 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 上执行此操作,我们可能需要修改代码以进行调整。
请让我知道这是否有效,将接受建议和改进!
为此创建一个快速的等效项应该很简单。
我今天遇到了这个问题,手动添加了一个 UITabBar 到情节提要中的视图控制器,当以常数 0 对齐到安全区域的底部时,我会遇到问题,但将其更改为 1 会解决问题。对于我的应用程序来说,让 UITabBar 比正常高 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);
}
我遇到了类似的问题,起初它被正确渲染,但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 错误,我们只需要重写/重新设置标签栏约束,以便自动布局引擎可以再次正确布局标签栏。
从本教程:
https://github.com/eggswift/ESTabBarController
并在标签栏初始化后在 appdelegate 类中写入这一行
(self.tabBarController.tabBar as? ESTabBar)?.itemCustomPositioning = .fillIncludeSeparator
解决了我的标签栏问题。
希望它能解决你的问题
谢谢
对于 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 进行测试。
尝试将初始屏幕更改为@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 并将所有视图控制器推送到这个新的 UITabBarConttoller。因此,在 iPhone X 模拟器中一切正常。
对我来说,解决方案是在视图层次结构中选择选项卡栏,然后转到:编辑器 - > 解决自动布局问题,然后在“选定视图”(不是“视图中的所有视图”)下选择“添加缺少的约束”。
我UITabBarController
在情节提要中使用 a ,起初它对我来说工作得很好,但是在升级到较新的 Xcode 版本后,它开始给我带来与 tabBar 高度相关的问题。
对我来说,解决方法是从情节提要中删除现有的并通过从界面构建器对象库UITabBarController
中拖动它来重新创建。
如果您对 Tab Bar 有任何高度限制,请尝试删除它。
面临同样的问题,删除它解决了这个问题。
对于那些以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
}
我认为这是来自 iPhoneX 的错误 UIKit。因为它有效:
if (@available(iOS 11.0, *)) {
if ([UIApplication sharedApplication].keyWindow.safeAreaInsets.top > 0.0) {
self.tabBarBottomLayoutConstraint.constant = 1.0;
}
}
我相信您可能会根据底部安全布局指南布置标签栏。这可能不是您想要的,因为标签栏似乎会自行计算以将标签栏项目安全地放置在 iPhone X 的主线顶部。针对超级视图底部设置底部约束。您应该会看到它在iPhone X和其他 iPhone 上的布局正确。
我在启动屏幕故事板上遇到了这个错误,无法通过子类化进行自定义。经过一些分析,我设法找出原因 - 我UITabBar
直接使用了。切换到UITabBarController
封装 a 的 aUITabBar
解决了这个问题。
注意 - iOS 11.1 可能会修复此问题,因此可能不会影响真正的 iPhone X 应用程序(因为 iPhone X 很可能会在运行 iOS 11.1 的情况下发布)。
我遇到了同样的问题,通过在标签栏布局后设置 tabBar 的项目解决了这个问题。
就我而言,问题发生在以下情况: