How to correct Tab Bar height issue on iPhone X
Asked Answered
D

20

36

I'm having an issue with my app when testing for iPhone X. I'm not sure how to adjust this issue, as well as not make it an issue for non iPhone X sizes. This only seems to be an issue on the iPhone X simulator.

enter image description here

enter image description here

Demark answered 1/11, 2017 at 12:20 Comment(4)
use programmatically TabbarController which will not affect this issuesAliaalias
Did you resolved this problem.I am facing this issue because of custom tabbar heightPickpocket
@ShangariC Your issue is same as mine (custom height), and you can try this, remove the viewWillLayoutSubviews's override, and use this instead to apply custom height.Ventricose
Check this: https://mcmap.net/q/166063/-ios-11-iphone-x-simulator-uitabbar-icons-and-titles-being-rendered-on-top-covering-eachotherThorax
P
10

"File inspector"

"File inspector" from right of Xcode storyboard, enable Safe Area guide layout to support your app in iPhone

This post describes it really well.

Penicillium answered 1/11, 2017 at 12:51 Comment(7)
Sounds like this question is a duplicate then, why not flag it as such?Strigil
@MartijnPieters Both questions are different concepts, but directs to same solutions.Penicillium
Duplicates are all about the same solution.Strigil
This was checked and my constraints are set to Safe Area but I'm still getting that odd gap at the bottom.Demark
@Demark were you able to find a fix?Chaffin
I am getting this issue on iPhone XR only, It is working on XS and XS Max why?Montsaintmichel
Just because the answer is a duplicate does not necessarily make the question a duplicate. I found the question useful and unique.Sarabia
V
17

On iOS 12.1 I've solved this issue by overriding safeAreaInsets in the UITabBar subclass:

class TabBar: UITabBar {
    private var cachedSafeAreaInsets = UIEdgeInsets.zero

    override var safeAreaInsets: UIEdgeInsets {
        let insets = super.safeAreaInsets
    
        if insets.bottom < bounds.height {
            cachedSafeAreaInsets = insets
        }
    
        return cachedSafeAreaInsets
    }
}

For iOS 13.0 onward,

class TabBar: UITabBar {
    private var cachedSafeAreaInsets = UIEdgeInsets.zero

    let keyWindow = UIApplication.shared.connectedScenes
        .filter { $0.activationState == .foregroundActive }
        .compactMap { $0 as? UIWindowScene }
        .first?.windows
        .filter { $0.isKeyWindow }
        .first
    
    override var safeAreaInsets: UIEdgeInsets {
        if let insets = keyWindow?.safeAreaInsets {
            if insets.bottom < bounds.height {
                cachedSafeAreaInsets = insets
            }
        }
        return cachedSafeAreaInsets
    }
}
Vincents answered 6/11, 2018 at 10:33 Comment(0)
B
15

Create a separate file with the following code:

extension UITabBar {
    override open func sizeThatFits(_ size: CGSize) -> CGSize {
        super.sizeThatFits(size)
        guard let window = UIApplication.shared.keyWindow else {
            return super.sizeThatFits(size)
        }
        var sizeThatFits = super.sizeThatFits(size)
        sizeThatFits.height = window.safeAreaInsets.bottom + 40
        return sizeThatFits
    }
}
Bainbrudge answered 28/6, 2018 at 11:54 Comment(1)
Great solution worked for me, although you would never want to do this in an extensionOrontes
P
10

"File inspector"

"File inspector" from right of Xcode storyboard, enable Safe Area guide layout to support your app in iPhone

This post describes it really well.

Penicillium answered 1/11, 2017 at 12:51 Comment(7)
Sounds like this question is a duplicate then, why not flag it as such?Strigil
@MartijnPieters Both questions are different concepts, but directs to same solutions.Penicillium
Duplicates are all about the same solution.Strigil
This was checked and my constraints are set to Safe Area but I'm still getting that odd gap at the bottom.Demark
@Demark were you able to find a fix?Chaffin
I am getting this issue on iPhone XR only, It is working on XS and XS Max why?Montsaintmichel
Just because the answer is a duplicate does not necessarily make the question a duplicate. I found the question useful and unique.Sarabia
C
7

For iOS 11.3 this worked for me:

func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    tabBar.invalidateIntrinsicContentSize()
}
Cartography answered 18/4, 2018 at 6:59 Comment(0)
M
3

In Constraints -

If you are giving bottom space with "Bottom Layout Guide", then this issue will occur.

Solution:

Give bottom space with respect to superview. This will work 100% perfect.

Milligan answered 12/1, 2018 at 6:31 Comment(0)
A
3

This worked for me.

[self.tabBar.bottomAnchor constraintEqualToAnchor:self.view.layoutMarginsGuide.bottomAnchor].active = YES;
Appendicitis answered 8/7, 2019 at 18:18 Comment(1)
This is really the answer that saved meCatawba
Z
2

I had a a similar issue. I was setting the selectionIndicatorImage in viewDidLoad(). Moving the code to viewDidLayoutSubviews() fixed my issue.

Zosima answered 29/4, 2018 at 22:29 Comment(0)
H
2

Just align the bottom of the UITabBar to the superview, not to the safe area. If you align it to safe area it will be like this:

Align to safe area

And when aligned to the superview, it will show correctly:

Align to Superview

I think this is because Apple gave the tab bar items a default margin to the bottom if it is on iPhone X as they want the tab bar to be extended to the bottom of the screen to avoid a floating bar.

Hipparchus answered 6/8, 2018 at 22:53 Comment(0)
D
2

Follow below guidelines for setting the UITabbar selectionIndicatorImage.

  1. UITabBar.appearance().selectionIndicatorImage = #YOUR_IMAGE
  2. Make sure your image height is 48.

The default height of tabbar selectionIndicatorImage is 49, But in iPhone X set image height equals to 48.

Diocesan answered 7/9, 2018 at 11:3 Comment(0)
C
1

The solution for me was that I had a custom UITabBar height set, something like this:

  override func viewWillLayoutSubviews() {            
        var tabFrame = tabBar.frame
        tabFrame.size.height = 60
        tabFrame.origin.y = self.view.frame.size.height - 60
        tabBar.frame = tabFrame
    }

Remove it and the tab bar will display correctly on iPhone X.

Cuckoo answered 8/2, 2018 at 13:41 Comment(0)
S
1

Add this code in viewDidLoad

DispatchQueue.main.async {
            let size = CGSize(width: self.tabBar.frame.width / numberOfTabsFloat,
                              height: self.tabBar.frame.height)
            let image = UIImage.drawTabBarIndicator(color: UIColor.white,
                                                   size: size,
                                                   onTop: false)
            UITabBar.appearance().selectionIndicatorImage = image
            self.tabBar.selectionIndicatorImage = image
        }

and add this extension

extension UIImage{
    //Draws the top indicator by making image with filling color
    class func drawTabBarIndicator(color: UIColor, size: CGSize, onTop: Bool) -> UIImage {
        let indicatorHeight = size.height
        let yPosition = onTop ? 0 : (size.height - indicatorHeight)

        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        color.setFill()
        UIRectFill(CGRect(x: 0, y: yPosition, width: size.width, height: indicatorHeight))
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image!
    }
}
Shirr answered 2/5, 2019 at 16:8 Comment(0)
A
1

Put this into your UITabBarViewController to correct the TabBar height if your UIViewController is rotatable.

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        tabBar.sizeToFit()
    }
Accursed answered 7/5, 2019 at 11:12 Comment(0)
S
0

invalidateIntrinsicContentSize of UITabBar in viewWillLayoutSubviews that may help you.

 override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    self.tabBar.invalidateIntrinsicContentSize()
}
Sportswoman answered 1/11, 2017 at 12:31 Comment(1)
Hi Nishant, trid this, didn't make a difference.Demark
B
0

This worked for me as I am using a selection image.

tabBar.selectionIndicatorImage = UIImage.imageWithColor(color: UIColor.NewDesignColor.yellow, size: tabBarItemSize).resizableImage(withCapInsets: UIEdgeInsets.init(top: 0, left: 0, bottom: 20, right: 0))

Adding a bottom inset helps in my case. Hope this works for your as well. Thanks.

Banger answered 30/3, 2018 at 17:56 Comment(0)
C
0

There is UITabBar subclass that solves all my issues with iPhone X iOS 11 / iOS 12

class TabBar: UITabBar {

private var _safeAreaInsets = UIEdgeInsets.zero
private var _subviewsFrames: [CGRect] = []

@available(iOS 11.0, *)
override func safeAreaInsetsDidChange() {
    super.safeAreaInsetsDidChange()

    if _safeAreaInsets != safeAreaInsets {
        _safeAreaInsets = safeAreaInsets

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

override func sizeThatFits(_ size: CGSize) -> CGSize {
    var size = super.sizeThatFits(size)
    if #available(iOS 12.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
    }
}

override func layoutSubviews() {
    super.layoutSubviews()
    let state = subviews.map { $0.frame }
    if (state.first { $0.width == 0 } == nil) {
        _subviewsFrames = state
    } else {
        zip(subviews, _subviewsFrames).forEach { (view, rect) in
            view.frame = rect
        }
    }

}
}
Crunch answered 28/11, 2018 at 17:4 Comment(0)
B
0

Apple has now fixed this issue in iOS 12.1.1

Bertilla answered 30/1, 2019 at 12:26 Comment(4)
This is not fixed in iOS 12.1.4Unni
Can confirm that this issue is fixed on iOS 12.1.1 (tested on two iPhone 8s, one on 12.1 and the other on 12.1.1)... don't tell me that Apple broke it again...Yanirayank
It's broken on iOS 13Alegre
@tww0003, It's working for me. Can you share how you're doing it?Bertilla
M
0

It's look crazy but I just need to remove this line in my code

self.view.layoutIfNeeded()

I just guess that call layoutIfNeeded on a view that doesn't appear in screen will make this problem happen. Anyway, solution from @mohamed-ali also work correctly. Thanks you so much.

Mammy answered 1/3, 2019 at 4:25 Comment(0)
P
0

Although my answer is late, But let me ensure you, if you are facing any issue like this on iphone x, xs or max screen, Make sure the image size you are uploading as selection must have height = width * 48pxHeight.

Phillada answered 16/10, 2019 at 19:38 Comment(0)
I
0

After trying a few solutions, what worked for me was adding the following line to viewDidLoad:

[self.tabBar.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;

Jonah's and Mehul Thakkar's answers pointed me in the right direction.

Note: I only had a blank tab bar controller in storyboard. The tab bar images and view controllers were setup using the tabBarItem properties (e.g., tabBarItem.title on the view controllers).

Inveterate answered 22/1, 2020 at 21:37 Comment(0)
S
-1

I was facing this cosmetic problem above iOS 11.0 and below 12.0 only.

enter image description here

I was having a CustomTabBar class inherited from UITabBar. I override the frame method like below:

- (CGRect)frame{
    return self.bounds;
}

It resolved this issue in most of the iOS version 11.0 *

Satterfield answered 19/7, 2019 at 6:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.