关于UITabBar组件的iPhone X模拟器有人遇到问题吗?
我的图标和标题似乎相互叠加,我不确定是否丢失了任何东西,我也在iPhone 8模拟器中运行过它,还有一个实际的设备,它看起来很不错,如上图所示。故事板。
iPhone X:
iPhone 8
关于UITabBar组件的iPhone X模拟器有人遇到问题吗?
我的图标和标题似乎相互叠加,我不确定是否丢失了任何东西,我也在iPhone 8模拟器中运行过它,还有一个实际的设备,它看起来很不错,如上图所示。故事板。
iPhone X:
iPhone 8
Answers:
我只需在viewDidLayoutSubviews中的UITabBar上调用invalidateIntrinsicContentSize即可解决此问题。
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self.tabBar invalidateIntrinsicContentSize];
}
注意:标签栏的底部将需要包含在主视图的底部,而不是安全区域,并且标签栏应没有高度限制。
[self.view layoutIfNeeded]
。
landscapeImage
标签栏按钮的属性进行设置。推荐的尺寸在[Apple HIG's](developer.apple.com/design/human-interface-guidelines/ios/…)中的“ 自定义图标大小”下
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
覆盖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
}
}
经过数小时的咒骂,我终于明白了。
UIKit实际上确实为您处理了此问题,并且似乎选项卡栏项目的移位是由于设置不正确(可能是实际的UIKit错误)所致。无需子类化或背景视图。
它甚至可以在Interface builder中使用。
真正愚蠢的是,即使您添加了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
}
}
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/
尝试在自定义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
多少?
我已经为这个问题挠头了。它似乎与如何初始化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
,它工作正常。谢谢!
从本教程中:
https://github.com/eggswift/ESTabBarController
并在标签栏初始化后在appdelegate类中写此行
(self.tabBarController.tabBar as? ESTabBar)?.itemCustomPositioning = .fillIncludeSeparator
解决了我的标签栏问题。
希望它能解决您的问题
谢谢
我有类似的问题,起初它是正确呈现的,但是在设置了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超级视图来确保约束/锚位于相同的视图层次结构上,并避免崩溃。
根据我的理解,由于这似乎是UIKit的错误,我们只需要重写/重新设置选项卡栏约束,以便自动布局引擎可以再次正确地布局选项卡栏。
如果对标签栏有任何高度限制,请尝试将其删除。
面对相同的问题,删除该问题即可解决。
我在情节提要中创建了新的UITabBarController,并将所有视图控制器推到了这个新的UITabBarConttoller。因此,所有这些都可以在iPhone X模拟器中正常运行。
对于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测试。
对于那些以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
}
我认为这是iPhoneX的错误UIKit。因为它有效:
if (@available(iOS 11.0, *)) {
if ([UIApplication sharedApplication].keyWindow.safeAreaInsets.top > 0.0) {
self.tabBarBottomLayoutConstraint.constant = 1.0;
}
}
我相信您可能会根据底部安全布局指南来布置选项卡栏。这可能不是您想要的,因为选项卡栏似乎确实进行了自己的计算,以将选项卡栏项目安全地放置在iPhone X的主行顶部。针对Superview底部设置底部约束。您应该看到它在iPhone X以及其他iPhone 上的布局正确。