iOS 11 iPhone X simulator UITabBar icons and titles being rendered on top covering eachother

34,339

Solution 1

I was able to get around the problem by simply calling invalidateIntrinsicContentSize on the UITabBar in viewDidLayoutSubviews.

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

Note: The bottom of the tab bar will need to be contained to the bottom of the main view, rather than the safe area, and the tab bar should have no height constraint.

Solution 2

Answer provided by VoidLess fixes TabBar problems only partially. It fixes layout problems within the tabbar, but if you use viewcontroller that hides the tabbar, the tabbar is rendered incorrectly during animations (to reproduce it is best 2 have 2 segues - one modal and one push. If you alternate the segues, you can see the tabbar being rendered out of place). The code bellow fixes both problems. Good job apple.

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 code:

@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

Solution 3

There is a trick by which we can solve the problem.

Just put your UITabBar inside a UIView.

This is really working for me.

Or you can follow this Link for more details.

Solution 4

override UITabBar sizeThatFits(_) for 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
    }
}

Solution 5

I added this to viewWillAppear of my custom UITabBarController, because none of the provided answers worked for me:

tabBar.invalidateIntrinsicContentSize()
tabBar.superview?.setNeedsLayout()
tabBar.superview?.layoutSubviews()
Share:
34,339
adrian chen
Author by

adrian chen

Updated on August 31, 2020

Comments

  • adrian chen
    adrian chen almost 4 years

    Anyone having issue with the iPhone X simulator around the UITabBar component?

    Mine seem to be rendering the icons and title on top of each other, I'm not sure if I'm missing anything, I also ran it in the iPhone 8 simulator, and one actual devices where it looks fine just as shown on the story board.

    iPhone X:

    iPhone X UITabBar bug

    iPhone 8

    iPhone 8 UITabBar works

  • adrian chen
    adrian chen almost 7 years
    i see, I didn't create the Tab bar programatically, i used the storyboard to build it with constraints set to be the bottom of the screen. It does feel odd to me that we have to detect if it's an iPhone X then go do some rejigging on the layout, while they just bump up the height of the TabBar by it self without any code changes.
  • adrian chen
    adrian chen almost 7 years
    This feels more like a bug to me now, I think i'm going to do a bug report on this and see what comes out of it. Thanks for your help!!
  • roperklacks
    roperklacks almost 7 years
    It's really not a great idea to use the exact height of the main screen bounds to determine if the app is running on iPhone X. What happens if next year's model is the same point size? Or if the app is running in landscape mode?
  • Adama
    Adama almost 7 years
    Transparency doesn't seem to affect my icons being set improperly. They're messed up whether the UITabBar is opaque or transparent.
  • Tiago Veloso
    Tiago Veloso almost 7 years
    What are the values of kPhoneXTabBarHeight ?
  • user3099837
    user3099837 almost 7 years
    can you give more info on what tabBarBottomLayoutConstraint is?
  • Evgeny
    Evgeny over 6 years
    I just added 32 points to the previous value of kTabBarHeight (which was 45). But you don't have to specify exactly the same value, for me it's also works fine without specifying the frame for the tabBar. But if you do specify it, you have to adjust for additional height on iPhone X.
  • Perry
    Perry over 6 years
    It would appear you are actually adding 32px to the height of the frame rather than subtracting from it?
  • Kenny Wyland
    Kenny Wyland over 6 years
    I've got a tab bar with no height constraint, constrained to the Bottom Layout Guide, and this did not work for me. Any other ideas?
  • Kenny Wyland
    Kenny Wyland over 6 years
    Didn't work for me. I was using a UITabBar that was manually added as well.
  • Kenny Wyland
    Kenny Wyland over 6 years
    Total hack, but it was the only one of these solutions that got it looking right for me.
  • Hemang
    Hemang over 6 years
    When you're having a custom view as your tabbar you like to use this hack to make your custom view fit for iPhone X.
  • Hemang
    Hemang over 6 years
    @Evgeny, I guess, the previous value was 49 and not 45.
  • Evgeny
    Evgeny over 6 years
    @Hemang no, it was 45 for this project I borrowed this code from.
  • Raimundas Sakalauskas
    Raimundas Sakalauskas over 6 years
    Solves part of the problems, but not all. Please see the full answer bellow
  • ooops
    ooops over 6 years
    Not working when homeVC present VC A, then dismiss A, and push VC B from homeVC. This is a workaround stackoverflow.com/a/47225653/1553324
  • Tiago
    Tiago over 6 years
    This also fixed it for me. There's something different about the way Xcode 9 IB generates the UITabBarController's XML that fixed the issue.
  • Drew C
    Drew C over 6 years
    Adding this method combined with pinning the tab bar to the bottom of its superview (not the safe area) worked for me.
  • Albert Bori
    Albert Bori over 6 years
    I put this in the viewDidAppear(animated:) in my UITabBarController and it worked fine. Thanks!
  • Iulian Onofrei
    Iulian Onofrei over 6 years
    I also needed to add [self.view layoutIfNeeded].
  • sdsykes
    sdsykes over 6 years
    This worked fine, have a uiview that simply contains the UITabBar. Put the contraints to the safe areas on the UIView (L, R, and Bottom), give it a height (49), and constrain the UITabBar to the UIView on all sides.
  • Siddh
    Siddh over 6 years
    Hello I have programmatically created tab bar controller and shows on successful login in a home screen in that where do I need to write this code which you have suggested
  • sulabh
    sulabh over 6 years
    what param u passed size and coordinator? from view didAppear
  • adrian chen
    adrian chen over 6 years
    This was what i did at the end to get it working, but yeah I have no idea why or understand what it's doing. To me it still feels like a bug
  • Ashish Verma
    Ashish Verma over 6 years
    This worked for iOS 11.0 but it is not working for iOS 11.1 and iOS 11.2
  • Avineet Gupta
    Avineet Gupta over 6 years
    What If we are using a LaunchScreen Storyboard?
  • Sob7y
    Sob7y over 6 years
    it's okay just use the splash new size
  • Riccardo Tesio
    Riccardo Tesio over 6 years
    This solution worked for me. I was not using an UITabController, just UITabBar.
  • francis lanthier
    francis lanthier over 6 years
    Worked for me. Thanks
  • Sayalee Pote
    Sayalee Pote over 6 years
    Complete solution. Thanks.
  • Tai Le
    Tai Le over 6 years
    It worked for me when I try to present VCA, then dismiss VCA, push VCB.
  • Jojo
    Jojo over 6 years
    Thank you for your answer. But how do you use it in a UITabBarController class ?
  • Raimundas Sakalauskas
    Raimundas Sakalauskas about 6 years
    @Jojo, given how much code is needed to populate tabbar via code, I always create uitabbarcontroller using storyboards. There you can assign custom class for tabbar. Hope this helps.
  • Glenn Posadas
    Glenn Posadas about 6 years
    This helped me out! Thank you!
  • RobP
    RobP almost 6 years
    This works but I learned I must also pay attention to the sizes of the images in the tab bar buttons. In landscape mode (on compact devices, but not iPad and iPhone Plus models), the system uses a compact tab bar which is supposed to have smaller images that you can set with the landscapeImage property of the tab bar button. Recommended sizes are in [Apple HIG's] (developer.apple.com/design/human-interface-guidelines/ios/…‌​) under Custom Icon Size
  • yajra
    yajra almost 6 years
    This works for me but I need to put the bottom constraint to the superview and not the safe area.
  • Micah Montoya
    Micah Montoya almost 6 years
    This worked for me as well. Be sure to set the views bottom constraint to the safe area bottom.
  • Jindřich Skeldal
    Jindřich Skeldal almost 6 years
    Brilliant. I have just set custom size of my tabbar and wondering why it doesn't work on iPhoneX. Other solutions doesn't work for me as I am using Tab Bar Controller and it doesn't include constraints.
  • RyanTCB
    RyanTCB over 5 years
    changed all constraints from safe area to superview and worked. Legend Ryan
  • Weizhi
    Weizhi over 5 years
    @ooops stackoverflow.com/a/50233139/1147209 I think this will simply fix the problem.
  • iDev
    iDev over 5 years
    This works but then the "unsafe" area might not be the same color as the tab bar (in case the super view and the tabbar are not of the same color)
  • Jay
    Jay over 5 years
    This helped me fix a problem related to moving the tabBar to the top of the screen. Thank you!
  • Nick
    Nick about 5 years
    But not for me :(
  • user1019042
    user1019042 about 5 years
    What do I do with this new class after I create it? I tried looking through my application for any occurrence of UITabBar and replace them with the SafeAreaFixTabBar... I found these occurrences: func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { SafeAreaFixTabBar.appearance().barTintColor = ... SafeAreaFixTabBar.appearance().backgroundColor = ... SafeAreaFixTabBar.appearance().tintColor = ... But didn't fix anything
  • VoidLess
    VoidLess almost 5 years
    This is also better than subclassing because subclassing UITabBar breaks automatic dark mode support
  • Alan S
    Alan S almost 5 years
    This is the best solution here
  • Akshay Jadhav
    Akshay Jadhav over 4 years
    this issue doesn't occur on real device I've tested this on iPhone11 pro iOS 13
  • picciano
    picciano almost 4 years
    Looks like this technique broke with Xcode 12 / iOS 14.