Negative Spacer for Uibarbuttonitem in Navigation Bar on iOS 11

Negative spacer for UIBarButtonItem in navigation bar on iOS 11

EDIT:

This may no longer work as of iOS 13. You may get the error:

Client error attempting to change layout margins of a private view

OLD ANSWER:

I found a somewhat hacky solution on the Apple developer forums:
https://forums.developer.apple.com/thread/80075

It looks like the problem comes from how iOS 11 handles the UIBarButtonItem .fixedSpace buttons and how a UINavigationBar is laid out in iOS 11. The navigation bars now use autolayout and the layout margins to layout the buttons. The solution presented in that post (at the bottom) was to set all the layout margins to some value you want.

class InsetButtonsNavigationBar: UINavigationBar {

override func layoutSubviews() {
super.layoutSubviews()

for view in subviews {
// Setting the layout margins to 0 lines the bar buttons items up at
// the edges of the screen. You can set this to any number to change
// the spacing.
view.layoutMargins = .zero
}
}

}

To use this new nav bar with custom button spacing, you will need to update where you create any navigation controllers with the following code:

let navController = UINavigationController(navigationBarClass: InsetButtonsNavigationBar.self, 
toolbarClass: UIToolbar.self)
navController.viewControllers = [yourRootViewController]

iOS 11 - how to change the position of rightBarButtonItems?

We have acomplished this by subclassing UINavigationBar:

#import <UIKit/UIKit.h>

@interface InsetButtonsNavigationBar : UINavigationBar

@end

and setting layoutMargins

#import "InsetButtonsNavigationBar.h"

@implementation InsetButtonsNavigationBar

- (void)layoutSubviews {

[super layoutSubviews];

for (UIView * view in self.subviews) {

view.layoutMargins = UIEdgeInsetsMake(0, 8, 0, 8);

}

}

@end

How to Edit Empty Spaces of Left, Right UIBarButtonItem in UINavigationBar [iOS 7]

I was also facing this problem. I also have feelings that in iOS 7 there is more space. And I figured out that this is about 10 points more. I usually use negative spaces when I want for LeftBarItemButton to start from the edge. This can be useful for you as well.

UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];

negativeSpacer.width = -16; // it was -6 in iOS 6

[self.navigationItem setLeftBarButtonItems:@[negativeSpacer, requiredButton]; /* this will be the button which you actually need */] animated:NO];

How to adjust space between two UIBarButtonItem in rightBarButtonItems

Updated at Jul 2015

A better way to do this is to use storyboard (tested in Xcode 6.4). First, add a UINavigationItem; secondly, add a Bar Button Item; thirdly, add a view to the Bar Button Item you just created in step 2; fourthly, add as many buttons as you wish into that view you just dragged in; lastly, adjust the space with your mouse and constraints.

Related Questions

Can't assign multiple Buttons to UINavigationItem when using Storyboard with iOS 5

How to add buttons to navigation controller visible after segueing?


Old Answer (Only acceptable for small insets)

Use imageInsets property:

leftButton.imageInsets = UIEdgeInsetsMake(0.0, 0.0, 0, -15);
rightButton.imageInsets = UIEdgeInsetsMake(0.0, -15, 0, 0);

for three or more buttons, the middle one(s) get both insets:

leftButton.imageInsets = UIEdgeInsetsMake(0.0, 0.0, 0, -15);
middleButton.imageInsets = UIEdgeInsetsMake(0.0, -15, 0, -15);
rightButton.imageInsets = UIEdgeInsetsMake(0.0, -15, 0, 0);

For the right side buttons, be careful: the FIRST button in the item array is the RIGHT one:

rightButton.imageInsets = UIEdgeInsetsMake(0.0, -15, 0, 0);
middleButton.imageInsets = UIEdgeInsetsMake(0.0, -15, 0, -15);
leftButton.imageInsets = UIEdgeInsetsMake(0.0, 0.0, 0, -15);

IMPORTANT: Split the inset between the two neighbors; if apply the entire inset to one edge, it will become obvious that the buttons are overlapping in the "blank" space - one button gets all of the "gap" touches. Even when "split" the adjustment like this, at -40 on both edges, the tap will definitely go to wrong button sometimes. -15 or -20 is the most to consider using with this technique.

By applying this method, the button could even be moved around in four directions.

iOS 11 UINavigationBar bar button items alignment

There is good article on this : http://www.matrixprojects.net/p/uibarbuttonitem-ios11/

Using this we can at least push rightbarbuttonitems to right till it leaves 8 pixel margin from trailing of the UINavigationBar.

Explained really well.

Placing a custom view based UIBarButtonItem in the navigation bar without default horizontal padding

55As commented above, the solution I went with is based on this answer to a different, but very much related question: How to adjust UIToolBar left and right padding. It is also facilitated by (and depends on) iOS5, which allows you to set multiple buttons on the left or right side, instead of just one.

Here's an example of removing the padding to the left of a custom left button item:

UIBarButtonItem *backButtonItem // Assume this exists, filled with our custom view

// Create a negative spacer to go to the left of our custom back button,
// and pull it right to the edge:
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
target:nil action:nil];
negativeSpacer.width = -5;
// Note: We use 5 above b/c that's how many pixels of padding iOS seems to add

// Add the two buttons together on the left:
self.navigationItem.leftBarButtonItems = [NSArray
arrayWithObjects:negativeSpacer, backButtonItem, nil];

And with this, the left padding for the left bar button item in a navigation bar, is gone!

NOTE: This has worked for me in iOS5 and iOS6. Given that iOS7 is considerably different (from the public demos), those of you with the early seeds of iOS7 should test if something so unintentional, like this hack, will actually continue to work for you beyond iOS6.

UIToolbar left and right spacing when rotating device in IOS11

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() {

}
}

reduce left space and right space from navigation bar left and right bar button item

UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
target:nil action:nil];
negativeSpacer.width = -16;
[self.navigationItem setLeftBarButtonItems:[NSArray arrayWithObjects:negativeSpacer,yourbutton, nil] animated:YES];

Use like that.



Related Topics



Leave a reply



Submit