Geek
Geek

Reputation: 8320

Change UITabBar height

I use UITabBarController as a root view and app supports iOS 6 and above. Project class hierarchy is as below.

UITabBarController
  - tab1
    - UINavigationController
      - UIViewController
      - UIViewController
      .
      .
  - tab2
    - UINavigationController
      - UIViewController
      - UIViewController
      .
      .
      .
  - tab3
    - UIViewController
  - tab4
    - UIViewController

I used below code to change height of UITabBar in one of the UIViewControllers (which is inside UINavigationController) in above hierarchy.

CGRect tabbarFrame = self.tabBarController.tabBar.frame;
tabbarFrame.size.height += 60;
self.tabBarController.tabBar.frame = tabbarFrame;

But its not changing the height. UITabBar is displayed with default height. Though logging its value prints changed value as shown below.

<UITabBar: 0xb528f60; frame = (0 431; 320 109); autoresize = W+TM; layer = <CALayer: 0xb529080>>

How can I change UITabBar's height to achieve something like this:?

enter image description here

Upvotes: 76

Views: 116552

Answers (24)

MarK
MarK

Reputation: 206

I've used a solution with CustomTabBar, but I ended up getting a lot of exceptions which didn't tell me anything. Every time I was getting a different error, or sometimes no error at all. For example one of the errors in a random place:

Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)

Or:

malloc: Incorrect checksum for freed object 0x14ce4c790: probably modified after being freed.
Corrupt value: 0xb000000000000001
malloc: *** set a breakpoint in malloc_error_break to debug

I searched for a solution and on Apple's dev-forum user eskimo advised to use Standard Memory Debugging Tools.

Zombies instrument didn't help, but Address Sanitizer helped to identify the problem at once!

Error log:

SUMMARY: AddressSanitizer: heap-buffer-overflow MyTabBarController.swift in MyTabBarController.CustomTabBar.hasBanner.setter

thread #1: tid = 0x6801d, 0x00000001089bb250 libclang_rt.asan_iossim_dynamic.dylib`__asan::AsanDie(), queue = 'com.apple.main-thread', stop reason = Heap buffer overflow

{
  "access_size": 1,
  "access_type": 1,
  "address": 4918104816,
  "description": "heap-buffer-overflow",
  "instrumentation_class": "AddressSanitizer",
  "pc": 4382625148,
  "stop_type": 

The problem was that I used an instance variable in CustomTabBar. For some reason it was causing crashes. I've switched the var to a static and it solved the problem!

Here is the working code:

class MyTabBarController: UITabBarController {
    
    override func viewDidLoad() {
        // We have to put all init logic here because:
        // `UITabBarController` calls `loadView()` inside `super.init()` method,
        // which causes the call to `viewDidLoad()`.
        // So the `viewDidLoad()` method will be called before `init()` has finished its job.

        object_setClass(tabBar, CustomTabBar.self)
        
        CustomTabBar.hasBanner = InAppPurchaseManager.shared.activeSubscription == nil
        
        super.viewDidLoad()
        // ...
    }
    
    // ...
    
}

extension MyTabBarController {
    
    class CustomTabBar: UITabBar {
        
        // We have to use a static var `hasBanner`
        // because an instance var causes a Heap Buffer Overflow.
        static var hasBanner: Bool = true // <------------------- THE SOLUTION
        
        override func sizeThatFits(_ size: CGSize) -> CGSize {
            var sizeThatFits = super.sizeThatFits(size)
            sizeThatFits.height = Constants.Layout.tabBarHeight + (Self.hasBanner ? 44 : 0)
            return sizeThatFits
        }
        
    }
    
}

Upvotes: 0

D. Rothschild
D. Rothschild

Reputation: 719

For SwiftUI, I use Introspect Introspect

Then in your TabView view modifier, increase the size of your view by adding to the height. Here I am adding 7 pts then another 2.5 pts for a custom divider line. And I added it differently for iPhone vs. iPad.

.introspectTabBarController { UITabBarController in
    if sizeClass == .compact {
      let barIncrease = UIView(frame: CGRect(x: 0, y: -7, width: UITabBarController.tabBar.frame.size.width, height: 9))
      barIncrease.backgroundColor = UIColor(hex: "#508FD6FF")
      UITabBarController.tabBar.addSubview(barIncrease)
      let lineView = UIView(frame: CGRect(x: 0, y: -9.5, width: UITabBarController.tabBar.frame.size.width, height: 2.5))
      lineView.backgroundColor = UIColor(hex: "#508FD6FF")
      UITabBarController.tabBar.addSubview(lineView)
    } else {
      let lineView = UIView(frame: CGRect(x: 0, y: -2.5, width: UITabBarController.tabBar.frame.size.width, height: 2.5))
      lineView.backgroundColor = UIColor(hex: "#508FD6FF")
      UITabBarController.tabBar.addSubview(lineView)
    }
 }

Upvotes: 0

seggy
seggy

Reputation: 1196

Swift 4 & compatible with iphone x

class CustomTabBar : UITabBar {

@IBInspectable var height: CGFloat = 65.0

override open func sizeThatFits(_ size: CGSize) -> CGSize {
    guard let window = UIApplication.shared.connectedScenes
        .compactMap({$0 as? UIWindowScene})
        .first?.windows
        .filter({$0.isKeyWindow}).first else {
      return super.sizeThatFits(size)
    }
    var sizeThatFits = super.sizeThatFits(size)
    if height > 0.0 {
        
        if #available(iOS 11.0, *) {
            sizeThatFits.height = height + window.safeAreaInsets.bottom
        } else {
            sizeThatFits.height = height
        }
    }
    return sizeThatFits
}
}

Upvotes: 11

zeiteisen
zeiteisen

Reputation: 7178

For a UITabBarController subclass

Create a subclass from UITabBar

class CustomTabBar : UITabBar {
    override open func sizeThatFits(_ size: CGSize) -> CGSize {
        super.sizeThatFits(size)
        var sizeThatFits = super.sizeThatFits(size)
        sizeThatFits.height = 200
        return sizeThatFits
    }
}

and set the class in the constructor

init() {
    super.init(nibName: nil, bundle: nil)
    object_setClass(self.tabBar, CustomTabBar.self)
}

Upvotes: 2

Aleksey Egorov
Aleksey Egorov

Reputation: 29

It helped me

override func viewDidLayoutSubviews() {
        super.viewWillLayoutSubviews()

        tabBar.frame.size.height = 60
        tabBar.frame.origin.y = view.frame.height - 60
}

Upvotes: 1

Merichle
Merichle

Reputation: 710

Swift 4

extension UITabBar {
    override open func sizeThatFits(_ size: CGSize) -> CGSize {
        var sizeThatFits = super.sizeThatFits(size)
        sizeThatFits.height = 60 // adjust your size here
        return sizeThatFits
    }
}

Upvotes: 15

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 120082

Just add more inset to the bottom of the safe area:

additionalSafeAreaInsets.bottom = 40

enter image description here

Upvotes: 6

Sahil Omer
Sahil Omer

Reputation: 179

class TabBarVC: UITabBarController {

    //MARK:- Variable
    let HEIGHT_TAB_BAR:CGFloat = 500

    override func viewDidLoad() {
        super.viewDidLoad()
    
        // Do any additional setup after loading the view.
    }


    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        var tabFrame = self.tabBar.frame
        tabFrame.size.height = HEIGHT_TAB_BAR
        tabFrame.origin.y = self.view.frame.size.height - HEIGHT_TAB_BAR
        self.tabBar.frame = tabFrame
    }


}

Worked fine for me.

Upvotes: 2

David
David

Reputation: 889

Swift 5.3.1, XCode 11+, iOS 14:

import UIKit

class CustomTabBar: UITabBar {
    let height: CGFloat = 62
    
    override open func sizeThatFits(_ size: CGSize) -> CGSize {
        guard let window = UIApplication.shared.connectedScenes
                .filter({$0.activationState == .foregroundActive})
                .map({$0 as? UIWindowScene})
                .compactMap({$0})
                .first?.windows
                .filter({$0.isKeyWindow}).first else {
            return super.sizeThatFits(size)
        }

        var sizeThatFits = super.sizeThatFits(size)
        if #available(iOS 11.0, *) {
            sizeThatFits.height = height + window.safeAreaInsets.bottom
        } else {
            sizeThatFits.height = height
        }
        return sizeThatFits
    }
}

Upvotes: 4

Sultan Ali
Sultan Ali

Reputation: 2589

this is also one way to do that

extension UITabBar {

override public func sizeThatFits(size: CGSize) -> CGSize {
    super.sizeThatFits(size)
    var sizeThatFits = super.sizeThatFits(size)
    sizeThatFits.height = 71 // or whatever height you need
    return sizeThatFits
   } 
}

Upvotes: 4

Rushikesh
Rushikesh

Reputation: 1571

I faced this issue and I was able to solve it.

You have to add following code to your subclass of UITabBarController class.

const CGFloat kBarHeight = 80;

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];

    CGRect tabFrame = self.tabBar.frame; //self.TabBar is IBOutlet of your TabBar
    tabFrame.size.height = kBarHeight;
    tabFrame.origin.y = self.view.frame.size.height - kBarHeight;
    self.tabBar.frame = tabFrame;
}

Swift:

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    tabBar.frame.size.height = kBarHeight
    tabBar.frame.origin.y = view.frame.height - kBarHeight
}

Upvotes: 143

BrooklynCoder
BrooklynCoder

Reputation: 49

WORKS WITH ALL SCREEN SIZES: Set the tabBarHeight to the (original height of the tabBar - 20) this is important so you can use it later in the viewDidLayoutSubviews, also better than hard coding the size you want. Since that size might not work on all screens.

Window safe area insets keeps the necessary padding at the bottom of the tab bar height in order to maintain the distance from the bottom edge of the screen.

var tabBarHeight = CGFloat()

override func viewDidLoad() {
        super.viewDidLoad()
        tabBarHeight = self.tabBar.frame.height - 20
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        var tabFrame = self.tabBar.frame
        guard let window = UIApplication.shared.keyWindow else {return}
        tabFrame.size.height = tabBarHeight + window.safeAreaInsets.bottom
        self.tabBar.frame = tabFrame
    }

Upvotes: 3

jonaszmclaren
jonaszmclaren

Reputation: 2489

Edited Kiarash Asar's answers with use of Safe Area:

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    var safeAreaBottomInset: CGFloat = 0.0

    if #available(iOS 11.0, *) {
        safeAreaBottomInset = view.safeAreaInsets.bottom
    }

    let newTabBarHeight: CGFloat = {{myDesiredHeight}} + safeAreaBottomInset

    var newFrame = tabBar.frame
    newFrame.size.height = newTabBarHeight
    newFrame.origin.y = view.frame.size.height - newTabBarHeight

    tabBar.frame = newFrame
}

Upvotes: 5

Abeer Iqbal
Abeer Iqbal

Reputation: 1346

iPhoneX have have different height so if we move to smaller height then tabbar shape will be bad in iPhoneX

- (void)viewWillLayoutSubviews
{
    int requiredHeight = 55;
    CGRect tabFrame = self.tabBar.frame;
    if (tabFrame.size.height < requiredHeight)
    {
        tabFrame.size.height = requiredHeight;
        tabFrame.origin.y = self.view.frame.size.height - requiredHeight;
        self.tabBar.frame = tabFrame;
    }
}

Upvotes: 1

eNeF
eNeF

Reputation: 3280

You can modify the tab bar's height by subclassing it. I actually did this long time ago. xcode 6.0

override func sizeThatFits(_ size: CGSize) -> CGSize {
    return CGSize(width: super.sizeThatFits(size).width, height: 60)
}

That should return its default width with the height of 60pts.

Upvotes: 4

msrdjan
msrdjan

Reputation: 835

Tested in XCode 9.0 and Swift 4

As suggested in previous answers - inherit UITabBar and override sizeThatFits, but mark height as @IBInspectable, so it could be set in the Interface Builder:

import UIKit

class CustomTabBar : UITabBar {
    @IBInspectable var height: CGFloat = 0.0

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        var sizeThatFits = super.sizeThatFits(size)
        if height > 0.0 {
            sizeThatFits.height = height
        }
        return sizeThatFits
    }
}

Set CustomTabBar class for the UITabBar in the Identity Inspector (⌥⌘3):

Tab Bar Identity Inspector

Then set desired Height (greater than 0.0) in the Attributes Inspector (⌥⌘4):

Tab Bar Attributes Inspector

Upvotes: 45

S. Azzopardi
S. Azzopardi

Reputation: 71

For some reason, the answer from @Rushikesh was working pretty well until iOS 10 but I had some issues with iOS 11 and Swift 3.2.

The tabBar was changing its frame every time I touched a new tab.

I fixed this by putting the code in the viewDidLayoutSubviews() function instead of viewWillLayoutSubviews()

Swift 3 :

override func viewDidLayoutSubviews() {

    super.viewDidLayoutSubviews()
    var tabFrame            = tabBar.frame
    tabFrame.size.height    = 65
    tabFrame.origin.y       = view.frame.size.height - 65
    tabBar.frame            = tabFrame
}

Upvotes: 7

Kiarash Asar
Kiarash Asar

Reputation: 429

Swift3.0, Swift 4.0 compatible

Pre-iPhone X default tab bar height: 49pt

iPhone X default tab bar height: 83pt

A universal solution supporting every iOS device including iPhone X screen size would look like this:

  1. Capture UITabBar's default height:

    fileprivate lazy var defaultTabBarHeight = { tabBar.frame.size.height }()
    
  2. Adjust UITabBar's height:

        override func viewWillLayoutSubviews() {
            super.viewWillLayoutSubviews()
    
            let newTabBarHeight = defaultTabBarHeight + 16.0
    
            var newFrame = tabBar.frame
            newFrame.size.height = newTabBarHeight
            newFrame.origin.y = view.frame.size.height - newTabBarHeight
    
            tabBar.frame = newFrame
        }
    

Upvotes: 42

William Hu
William Hu

Reputation: 16189

Swift 3.0+ Replace 200 to your desired height in below code.

   extension UITabBar {
        override open func sizeThatFits(_ size: CGSize) -> CGSize {
            return CGSize(width: UIScreen.main.bounds.width, height: 200)
        }
    }

Upvotes: 11

Mando
Mando

Reputation: 11722

Xamarin implementation:

public override void ViewWillLayoutSubviews()
{
    base.ViewWillLayoutSubviews();
    const float newTabBarHeight = 40f;
    TabBar.Frame = new CGRect(TabBar.Frame.X, TabBar.Frame.Y + (TabBar.Frame.Height - newTabBarHeight), TabBar.Frame.Width, newTabBarHeight);
}

Upvotes: 5

Kqtr
Kqtr

Reputation: 5935

Building up on previous answers and updating for Swift 3.

Subclass UITabController and make sure to assign your new custom class to the Identity Inspector of your UITabController.

Swift 3.0

class MainTabBarController: UITabBarController {

    override func viewWillLayoutSubviews() {
        var newTabBarFrame = tabBar.frame

        let newTabBarHeight: CGFloat = 60
        newTabBarFrame.size.height = newTabBarHeight
        newTabBarFrame.origin.y = self.view.frame.size.height - newTabBarHeight

        tabBar.frame = newTabBarFrame
    }
}

Warning: if you get a blank space below your tab bar, make sure you did put this code in viewWillLayoutSubviews() and not viewDidLoad().

Upvotes: 7

MB_iOSDeveloper
MB_iOSDeveloper

Reputation: 4208

For iOS 8.2, Xcode 6.2 Swift language:

Create a "DNMainTabVC.swift" (DeveloperNameMainTabViewController.swift file) for your UITabBarController (of type UITabBarController) and connect it to your storyboard VC.

Add the following lines:

override func viewWillLayoutSubviews() {
    var tabFrame = self.tabBar.frame
    // - 40 is editable , the default value is 49 px, below lowers the tabbar and above increases the tab bar size
    tabFrame.size.height = 40
    tabFrame.origin.y = self.view.frame.size.height - 40
    self.tabBar.frame = tabFrame
}

This worked for me.

Upvotes: 41

Alvin George
Alvin George

Reputation: 14294

Swift 2.0:

var tabBar:UITabBar?

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

Upvotes: 11

Shaffiulla Khan
Shaffiulla Khan

Reputation: 10374

Create a custom subclass of type UITabBar, then implement the following method :

@implementation CustomTabBar
#define kTabBarHeight = // Input the height we want to set for Tabbar here
-(CGSize)sizeThatFits:(CGSize)size
{
    CGSize sizeThatFits = [super sizeThatFits:size];
    sizeThatFits.height = kTabBarHeight;

    return sizeThatFits;
}
@end

Hope this will work.

Upvotes: 30

Related Questions