Search code examples
swiftxcodeios11uibarbuttonitemuitoolbar

UIToolbar left and right spacing when rotating device in IOS11


I'm having a strange issue with the padding of the UIToolbar since iOS11. When the device is rotated the left and right padding of the toolbar gets bigger(watch example below).

It doesn't matter if the device is in portrait or landscape mode before the rotation. The extra spacing only occurs after rotating. I think it's an auto resizing issue or something.

View Debugger before rotation (correct spacing): https://www.dropbox.com/s/1wigv1et88t1mvn/Schermafdruk%202018-01-31%2015.51.05.png?dl=0

View Debugger after rotation (wrong spacing): https://www.dropbox.com/s/9gnqi6hzv5czcnw/Schermafdruk%202018-01-31%2020.59.48.png?dl=0

Example:

https://www.dropbox.com/s/s7jbmbsuorump5e/spacing-toolbar.gif?dl=0

I'm using a toolbar class to create the buttons inside the toolbar. In the xcode interface the option 'Autoresize Subview' is checked.

Code:

class ToolbarClass: UIToolbar {

    //Set height of toolbar
    override func sizeThatFits(_ size: CGSize) -> CGSize {
        var size = super.sizeThatFits(size)
        size.height = 60
        return size
    }

    //Toolbar settings
    override func layoutSubviews() {
        super.layoutSubviews()

        //Default
        self.barStyle = UIBarStyle.default
        self.sizeToFit()

        //Buttons ios11+

        //Space
        let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        let spaceBetween:UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
        spaceBetween.width = 1.0

        let nameSpace:UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
        nameSpace.width = 10

        //Logo
        let logoImage = UIImage(named: "MBS-Logo")
        let logoImageView = UIImageView(image: logoImage)
        logoImageView.frame = CGRect(x: -46, y: 0, width: 48, height: 54)
        logoImageView.contentMode = .scaleAspectFit
        let logoView = UIView(frame: CGRect(x: 0, y: 0, width: 48, height: 54))
        logoView.clipsToBounds = false
        logoView.layer.cornerRadius = logoView.frame.width / 2
        logoView.addSubview(logoImageView)
        let logoImg = UIBarButtonItem(customView: logoView)
        logoImg.customView = logoView

        //Profile
        let profileImage = UIImage(named: "No-Profile")
        let profileImageView = UIImageView(image: profileImage)
        profileImageView.frame = CGRect(x: 40, y: 0, width: 50, height: 50)
        profileImageView.contentMode = .scaleAspectFit
        profileImageView.clipsToBounds = true
        profileImageView.layer.cornerRadius = profileImageView.frame.width / 2
        let profileView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
        profileView.clipsToBounds = false
        profileView.addSubview(profileImageView)
        let profileImg = UIBarButtonItem(customView: profileView)
        profileImg.customView = profileView

        //NameLabel
        let nameLbl = UILabel()
        nameLbl.frame = CGRect(x: 0, y: 0, width: 200, height: 60)
        nameLbl.text = "Hi"
        nameLbl.font = UIFont(name: "Roboto", size: 22)
        nameLbl.textColor = UIColor.white
        let nameLabel = UIBarButtonItem()
        nameLabel.customView = nameLbl

        //Settings
        let settingsBtn = UIButton()
        settingsBtn.frame = CGRect(x: 0, y: 0, width: 64, height: 60)
        settingsBtn.setImage(UIImage(named: "Settings-Bar")?.withRenderingMode(.alwaysOriginal), for: .normal)
        settingsBtn.addTarget(self, action: #selector(self.settingsPressed), for: .touchUpInside)
        let settingsButton = UIBarButtonItem()
        settingsButton.customView = settingsBtn

        //Classes
        let classesBtn = UIButton()
        classesBtn.frame = CGRect(x: 0, y: 0, width: 64, height: 60)
        classesBtn.setImage(UIImage(named: "Classes-Bar")?.withRenderingMode(.alwaysOriginal), for: .normal)
        classesBtn.addTarget(self, action: #selector(self.classesPressed), for: .touchUpInside)
        let classesButton = UIBarButtonItem()
        classesButton.customView = classesBtn

        //Set buttons
        self.setItems([profileImg, logoImg, nameSpace, nameLabel, spaceButton, classesButton, spaceBetween, settingsButton], animated: false)

    }

}

enter image description here


Solution

  • First of all layoutSubviews is not the right place to add all the items. For example every time device orientate layoutSubviews is called and all items will be created again. Use init?(coder:) or init(frame:).

    Once you move your code to init?(coder:) or init(frame:) you will see the left and right margin (currently it appears only when you orientate). This is actual behaviour of UIToolBar, It adds margin on both sides automatically.

    To remove that margin just add negative separator of fixed size on start and end of toolbar items.

    let negativeFizedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
    negativeFizedSpace.width = -20 // Spacing 20 for iPad and 16 for iPhone
    

    class ToolbarClass: UIToolbar {
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            commonInit()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            commonInit()
        }
    
        private func commonInit() {
    
            //Space
            let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
            let spaceBetween:UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
            spaceBetween.width = 1.0
    
            let nameSpace:UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
            nameSpace.width = 10
    
            //Logo
            let logoImage = UIImage(named: "MBS-Logo")
            let logoImageView = UIImageView(image: logoImage)
            logoImageView.frame = CGRect(x: -46, y: 0, width: 48, height: 54)
            logoImageView.contentMode = .scaleAspectFit
            let logoView = UIView(frame: CGRect(x: 0, y: 0, width: 48, height: 54))
            logoView.clipsToBounds = false
            logoView.layer.cornerRadius = logoView.frame.width / 2
            logoView.addSubview(logoImageView)
            let logoImg = UIBarButtonItem(customView: logoView)
            logoImg.customView = logoView
    
            //Profile
            let profileImage = UIImage(named: "No-Profile")
            let profileImageView = UIImageView(image: profileImage)
            profileImageView.frame = CGRect(x: 40, y: 0, width: 50, height: 50)
            profileImageView.contentMode = .scaleAspectFit
            profileImageView.clipsToBounds = true
            profileImageView.layer.cornerRadius = profileImageView.frame.width / 2
            let profileView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
            profileView.clipsToBounds = false
            profileView.addSubview(profileImageView)
            let profileImg = UIBarButtonItem(customView: profileView)
            profileImg.customView = profileView
    
            //NameLabel
            let nameLbl = UILabel()
            nameLbl.frame = CGRect(x: 0, y: 0, width: 200, height: 60)
            nameLbl.text = "Hi"
            nameLbl.font = UIFont(name: "Roboto", size: 22)
            nameLbl.textColor = UIColor.white
            let nameLabel = UIBarButtonItem()
            nameLabel.customView = nameLbl
    
            //Settings
            let settingsBtn = UIButton()
            settingsBtn.frame = CGRect(x: 0, y: 0, width: 64, height: 60)
            settingsBtn.setImage(UIImage(named: "Settings-Bar")?.withRenderingMode(.alwaysOriginal), for: .normal)
            settingsBtn.addTarget(self, action: #selector(self.settingsPressed), for: .touchUpInside)
            let settingsButton = UIBarButtonItem()
            settingsButton.customView = settingsBtn
    
            //Classes
            let classesBtn = UIButton()
            classesBtn.frame = CGRect(x: 0, y: 0, width: 64, height: 60)
            classesBtn.setImage(UIImage(named: "Classes-Bar")?.withRenderingMode(.alwaysOriginal), for: .normal)
            classesBtn.addTarget(self, action: #selector(self.classesPressed), for: .touchUpInside)
            let classesButton = UIBarButtonItem()
            classesButton.customView = classesBtn
    
    
            let negativeFizedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
            negativeFizedSpace.width = -20 // Spacing 20 for iPad and 16 for iPhone
    
            //Set buttons
            self.setItems([negativeFizedSpace, profileImg, logoImg, nameSpace, nameLabel, spaceButton, classesButton, spaceBetween, settingsButton, negativeFizedSpace], animated: false)
        }
    
        //Set height of toolbar
        override func sizeThatFits(_ size: CGSize) -> CGSize {
            var size = super.sizeThatFits(size)
            size.height = 60
            return size
        }        
    }
    

    UPDATE

    If using Navigation Controller Toolbar. Alternate is to create a UIViewController extension to add generic toolbar items and call that in viewDidLoad method of your ViewController.

    class ViewController: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            addGeneralToolbarItems()
        }
    }
    
    class ToolbarClass: UIToolbar {
        //Set height of toolbar
        override func sizeThatFits(_ size: CGSize) -> CGSize {
            var size = super.sizeThatFits(size)
            size.height = 60
            return size
        }
    }
    
    extension UIViewController {
        func addGeneralToolbarItems()  {
            //Space
            let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
            let spaceBetween:UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
            spaceBetween.width = 1.0
    
            let nameSpace:UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
            nameSpace.width = 10
    
            //Logo
            let logoImage = UIImage(named: "MBS-Logo")
            let logoImageView = UIImageView(image: logoImage)
            logoImageView.frame = CGRect(x: -46, y: 0, width: 48, height: 54)
            logoImageView.contentMode = .scaleAspectFit
            let logoView = UIView(frame: CGRect(x: 0, y: 0, width: 48, height: 54))
            logoView.clipsToBounds = false
            logoView.layer.cornerRadius = logoView.frame.width / 2
            logoView.addSubview(logoImageView)
            let logoImg = UIBarButtonItem(customView: logoView)
            logoImg.customView = logoView
    
            //Profile
            let profileImage = UIImage(named: "No-Profile")
            let profileImageView = UIImageView(image: profileImage)
            profileImageView.frame = CGRect(x: 40, y: 0, width: 50, height: 50)
            profileImageView.contentMode = .scaleAspectFit
            profileImageView.clipsToBounds = true
            profileImageView.layer.cornerRadius = profileImageView.frame.width / 2
            let profileView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
            profileView.clipsToBounds = false
            profileView.addSubview(profileImageView)
            let profileImg = UIBarButtonItem(customView: profileView)
            profileImg.customView = profileView
    
            //NameLabel
            let nameLbl = UILabel()
            nameLbl.frame = CGRect(x: 0, y: 0, width: 200, height: 60)
            nameLbl.text = "Hi"
            nameLbl.font = UIFont(name: "Roboto", size: 22)
            nameLbl.textColor = UIColor.white
            let nameLabel = UIBarButtonItem()
            nameLabel.customView = nameLbl
    
            //Settings
            let settingsBtn = UIButton()
            settingsBtn.frame = CGRect(x: 0, y: 0, width: 64, height: 60)
            settingsBtn.setImage(UIImage(named: "Settings-Bar")?.withRenderingMode(.alwaysOriginal), for: .normal)
            settingsBtn.addTarget(self, action: #selector(self.settingsPressed), for: .touchUpInside)
            let settingsButton = UIBarButtonItem()
            settingsButton.customView = settingsBtn
    
            //Classes
            let classesBtn = UIButton()
            classesBtn.frame = CGRect(x: 0, y: 0, width: 64, height: 60)
            classesBtn.setImage(UIImage(named: "Classes-Bar")?.withRenderingMode(.alwaysOriginal), for: .normal)
            classesBtn.addTarget(self, action: #selector(self.classesPressed), for: .touchUpInside)
            let classesButton = UIBarButtonItem()
            classesButton.customView = classesBtn
    
    
            let negativeFizedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
            negativeFizedSpace.width = -20 // Spacing 20 for iPad and 16 for iPhone
    
            //Set buttons
            self.setToolbarItems([negativeFizedSpace, profileImg, logoImg, nameSpace, nameLabel, spaceButton, classesButton, spaceBetween, settingsButton, negativeFizedSpace], animated: false)
    
        }
    
        @objc func settingsPressed() {
    
        }
    
        @objc func classesPressed() {
    
        }
    }