How can I change the position of a UIBarButtonItem in a UINavigationBar? I would like my button to be about 5px higher than its normal position.
Init UIBarButtonItem with custom view and overload layoutSubviews in custom view, like this
-(void) layoutSubviews {
[super layoutSubviews];
CGRect frame = self.frame;
CGFloat offsetY = 5;
frame.origin.y = (44 - frame.size.height) / 2 - offsetY;
self.frame = frame;
}
There is no particularly good way to do this. Your best bet if you really must is to subclass UINavigationBar, and override layoutSubviews to call [super layoutSubviews] and then find and reposition the button's view.
This code creates a back button for UINavigationBar with image background and custom position. The trick is to create an intermediate view and modify its bounds.
Swift 5
let menuBtn = UIButton(type: .custom)
let backBtnImage = UIImage(named: "menu")
menuBtn.setBackgroundImage(backBtnImage, for: .normal)
menuBtn.addTarget(self, action: #selector(showMenuTapped), for: .touchUpInside)
menuBtn.frame = CGRect(x: 0, y: 0, width: 45, height: 45)
let view = UIView(frame: CGRect(x: 0, y: 0, width: 45, height: 45))
view.bounds = view.bounds.offsetBy(dx: 10, dy: 3)
view.addSubview(menuBtn)
let backButton = UIBarButtonItem(customView: view)
navigationItem.leftBarButtonItem = backButton
Objective C
UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *backBtnImage = [UIImage imageNamed:@"btn-back"];
UIImage *backBtnImagePressed = [UIImage imageNamed:@"btn-back-pressed"];
[backBtn setBackgroundImage:backBtnImage forState:UIControlStateNormal];
[backBtn setBackgroundImage:backBtnImagePressed forState:UIControlStateHighlighted];
[backBtn addTarget:self action:@selector(goBack) forControlEvents:UIControlEventTouchUpInside];
backBtn.frame = CGRectMake(0, 0, 63, 33);
UIView *backButtonView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 63, 33)];
backButtonView.bounds = CGRectOffset(backButtonView.bounds, -14, -7);
[backButtonView addSubview:backBtn];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithCustomView:backButtonView];
self.navigationItem.leftBarButtonItem = backButton;
As @Anomie said, we need to subclass UINavigationBar, and override layoutSubviews().
This will place all right bar button items firmly attached to the right side of the navigation bar (as opposed to being slightly left-adjusted by default):
class AdjustedNavigationBar: UINavigationBar {
override func layoutSubviews() {
super.layoutSubviews()
if let rightItems = topItem?.rightBarButtonItems where rightItems.count > 1 {
for i in 0..<rightItems.count {
let barButtonItem = rightItems[i]
if let customView = barButtonItem.customView {
let frame = customView.frame
customView.frame = CGRect(x: UIApplication.sharedApplication().windows.last!.bounds.size.width-CGFloat(i+1)*44, y: frame.origin.y, width: frame.size.width, height: frame.size.height)
}
}
}
}
}
The only place to set the UINavigationBar property of UINavigationController is in its init(), like so:
let controllerVC = UINavigationController(navigationBarClass: AdjustedNavigationBar.self, toolbarClass: nil)
controllerVC.viewControllers = [UIViewController()]
The second line sets the root view controller of UINavigationController.
(Since we cannot set it via init(rootViewController:)
The best way is to subclass your UINavigationBar, as described here: https://stackoverflow.com/a/17434530/1351190
Here is my example:
#define NAVIGATION_BTN_MARGIN 5
@implementation NewNavigationBar
- (void)layoutSubviews {
[super layoutSubviews];
UINavigationItem *navigationItem = [self topItem];
UIView *subview = [[navigationItem rightBarButtonItem] customView];
if (subview) {
CGRect subviewFrame = subview.frame;
subviewFrame.origin.x = self.frame.size.width - subview.frame.size.width - NAVIGATION_BTN_MARGIN;
subviewFrame.origin.y = (self.frame.size.height - subview.frame.size.height) / 2;
[subview setFrame:subviewFrame];
}
subview = [[navigationItem leftBarButtonItem] customView];
if (subview) {
CGRect subviewFrame = subview.frame;
subviewFrame.origin.x = NAVIGATION_BTN_MARGIN;
subviewFrame.origin.y = (self.frame.size.height - subview.frame.size.height) / 2;
[subview setFrame:subviewFrame];
}
}
@end
Hope it helps.
Navigation bar using change left bar position and image edge insets
swift 4
let leftBarButtonItem = UIBarButtonItem.init(image: UIImage(named:"ic_nav-bar_back.png"), landscapeImagePhone: nil, style: .plain, target: viewController, action: #selector(viewController.buttonClick(_:)))
leftBarButtonItem.imageInsets = UIEdgeInsets(top: 0, left: -15, bottom: 0, right: 0)
leftBarButtonItem.tintColor = UIColor(hex: 0xED6E19)
viewController.navigationItem.setLeftBarButton(leftBarButtonItem, animated: true)