14

I have a UITabBarController where I use this code to set selection indicator image:

let selectedBG = UIImage(named:"tabbarbgtest.png")?.resizableImageWithCapInsets(UIEdgeInsetsMake(0, 0, 0, 0))
UITabBar.appearance().selectionIndicatorImage = selectedBG

But the image does not fill the whole space - see image below:

enter image description here

The image is just a red square with a solution on 82x49px, but with a wider image it still does not fill the whole space. Hope you guys can help - thanks.

Peter Hornsby
  • 4,208
  • 1
  • 25
  • 44
Loc Dai Le
  • 1,661
  • 4
  • 35
  • 70

5 Answers5

10

As of 2017, the other answers didn't work for me. After a couple of days searching for another solution, I found mine - by subclassing the UITabBarController.

It works for multiple devices even with rotation.

Notes:

  1. Make your images' rendering mode as Original.

  2. Assign this class below to your UITabBarController in your Storyboard or as your base class if you're doing your screen programmatically.

    //
    //  BaseTabBarController.swift
    //  MyApp
    //
    //  Created by DRC on 1/27/17.
    //  Copyright © 2017 PrettyITGirl. All rights reserved.
    //
    
    import UIKit
    
    class BaseTabBarController: UITabBarController {
    
        let numberOfTabs: CGFloat = 4
        let tabBarHeight: CGFloat = 60
    
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
    
            updateSelectionIndicatorImage()
        }
    
        override func viewWillLayoutSubviews() {
            super.viewWillLayoutSubviews()
    
            updateSelectionIndicatorImage()
        }
    
        func updateSelectionIndicatorImage() {
            let width = tabBar.bounds.width
            var selectionImage = UIImage(named:"myimage.png")
            let tabSize = CGSize(width: width/numberOfTabs, height: tabBarHeight)
    
            UIGraphicsBeginImageContext(tabSize)
            selectionImage?.draw(in: CGRect(x: 0, y: 0, width: tabSize.width, height: tabSize.height))
            selectionImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
    
            tabBar.selectionIndicatorImage = selectionImage
        }
    
    }
    
blackgreen
  • 34,072
  • 23
  • 111
  • 129
Glenn Posadas
  • 12,555
  • 6
  • 54
  • 95
1

To support iPhone X(below code works for all versions), write your code in viewDidLayoutSubviews().

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    let tabWidth = (tabBar.frame.width/CGFloat(tabBar.items!.count))
    let tabHeight = tabBar.frame.height
    self.tabBar.selectionIndicatorImage = imageWithColor(color: UIColor.white, size: CGSize(width: tabWidth, height: tabHeight)).resizableImage(withCapInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0))
 }

Source: https://github.com/Ramotion/animated-tab-bar/issues/191

0

This relatively simple solution in objective c worked for me for iPhone X, wouldn't be hard to convert to swift:

CGFloat bottomPadding = 0;

if (@available(iOS 11.0, *)) {
  UIWindow *window = UIApplication.sharedApplication.keyWindow;
  bottomPadding = window.safeAreaInsets.bottom;
}

[UITabBar.appearance setSelectionIndicatorImage:[UIImage imageWithColor:[UIColor lightGrayColor] 
          andBounds:CGRectMake(0, 0, self.tabBar.frame.size.width/5, self.tabBar.frame.size.height + bottomPadding)]];
0

This is an adaptation on Glenn's solution above...

    import UIKit

    class BaseTabBarController: UITabBarController {

        var tabBarBounds: CGRect? {
            didSet {
                guard tabBarBounds != oldValue else { return }
                updateSelectionIndicatorColor(UIColor.green)
            }
        }

        override func viewWillLayoutSubviews() {
            super.viewWillLayoutSubviews()
            tabBarBounds = tabBar.bounds
        }

        func updateSelectionIndicatorColor(_ tintColor: UIColor) {
            guard let tabBarItems = self.tabBar.items else { return }

            let tabWidth = tabBar.bounds.width
            let tabHeight = tabBar.bounds.height
            let tabSize = CGSize(width: tabWidth / CGFloat(tabBarItems.count), height: tabHeight)
            var selectionImage = UIImage(color: tintColor, size: tabSize)

            UIGraphicsBeginImageContext(tabSize)
            selectionImage?.draw(in: CGRect(x: 0, y: 0, width: tabSize.width, height: tabSize.height))
            selectionImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()

            tabBar.selectionIndicatorImage = selectionImage
        }

    }

    public extension UIImage {
        public convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
            let rect = CGRect(origin: .zero, size: size)
            UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
            color.setFill()
            UIRectFill(rect)
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()

            guard let cgImage = image?.cgImage else { return nil }
            self.init(cgImage: cgImage)
        }
    }
Marsh
  • 1
-1

you should take a look at this to make the tabbarbgtest.png resizable, then assign the image to selectionIndicatorImage, you can even do this in the storyboard editor.

Allen
  • 6,505
  • 16
  • 19
  • 2
    why don't you provide a full answer instead of an external link that now leads to nowhere? – griga13 Apr 24 '17 at 22:18
  • Use a few detective skills. He provided a link to teach you how to make an image resizeable, if you didn't already know that. Once you have a resizeable image, you can set that image to selectionIndicatorImage on an instance of UITabBar. The bad part about Allen's answer is that he didn't read the question. Because his answer is exactly what the OP indicated did not work / is the problem. – horseshoe7 Oct 26 '20 at 12:21