在UINavigationBar中更改UIBarButtonItem的位置

70

我该怎样改变UINavigationBar中UIBarButtonItem的位置?我想让我的按钮比它正常位置高大约5像素。


您是否也希望UINavigation bar在其后面,还是只想让按钮自由浮动? - Jab
不太确定你的意思 - 我想答案是我希望它的行为与 UINavigationBar 中的普通 UIBarButtonItem 完全相同。 比如,如果它是 leftBarButtonItem... 一切都相同,只是高5像素。 如果有必要,该按钮还使用自定义图像。 - Ben Williams
一种方法是,简单地使用图像代替。尽管这很麻烦,但“总是有效的”。 - Fattie
21个回答

133

这段代码可以创建一个带有图像背景和自定义位置的UINavigationBar的后退按钮。诀窍是创建一个中间视图并修改其边界。

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;

32
然而,这种方法的一个问题是可点击区域仍然受限于默认的UIBarButtonItem区域。移动UIButton可能会导致可点击区域变小,有时会让UIButton无法识别轻触。 - iMaddin
我同意 @iMaddin 的观点,随着偏移量的增加,触摸区域变得越来越小。 - ajmccall
3
在iOS7中,使用CGRectOffset设置UIButton的偏移量无效。在iOS7中,UIBarButtonItem似乎有固定的位置,移动/偏移UIButton的x,y坐标无效。修改UIButton的宽度和高度有效,但更改x,y坐标则没有影响。 - Saurabh Hooda
我不认为这个答案提供了改变UIBarButtonItem位置的解决方案。它只会增加复杂性并引起其他问题,如点击区域。我认为这个问题应该在苹果的问题报告页面上作为一个功能或错误来公开讨论,因为很多开发者都希望改变这些坐标或对按钮有更多的控制。 - Roland
我发现将一个TapGestureRecognizer添加到UIBarButtonItem的customView中可以解决偏移时缩小的点击区域。 - Nate Potter
显示剩余7条评论

42

我使用 transform 和自定义视图解决了问题:

(Swift)

  // create the button
  let suggestImage  = UIImage(named: "tab-item-popcorn-on")!.imageWithRenderingMode(.AlwaysOriginal)
  let suggestButton = UIButton(frame: CGRectMake(0, 0, 40, 40))
  suggestButton.setBackgroundImage(suggestImage, forState: .Normal)
  suggestButton.addTarget(self, action: Selector("suggesMovie:"), forControlEvents:.TouchUpInside)

  // here where the magic happens, you can shift it where you like
  suggestButton.transform = CGAffineTransformMakeTranslation(10, 0)

  // add the button to a container, otherwise the transform will be ignored  
  let suggestButtonContainer = UIView(frame: suggestButton.frame)
  suggestButtonContainer.addSubview(suggestButton)
  let suggestButtonItem = UIBarButtonItem(customView: suggestButtonContainer)

  // add button shift to the side
  navigationItem.rightBarButtonItem = suggestButtonItem

兄弟,你是怎么想出这个解决方案的???谢谢,我已经找了几周的解决方案了。 - irkinosor
这是唯一对我有效的方法!(我尝试了很多其他提出的解决方案) - Joel
1
很遗憾,这种方法会破坏按钮的可触摸区域。 - atereshkov

35

没有特别好的方法来完成这个任务。如果你真的必须这样做,最好的方法是创建一个子类继承自UINavigationBar,并重写layoutSubviews方法,在调用[super layoutSubviews]后寻找并重新定位按钮的视图。


2
实际上,我撤回之前的话。它运行得很好。跳跃是我的错误。谢谢! - Ben Williams
8
你是如何让它不再跳动的?我也遇到了同样的问题。 - Adam
3
谢谢你分享这个。你能提供一个样本吗?提前谢谢。 - Lorenzo B
这是一个很好的例子!看第二个答案更加安全:https://dev59.com/-ljUa4cB1Zd3GeqPVd8z#17434530 - Simone Lai
14
有关使用外观方法,好像没有人知道?在您的应用程序委托中添加以下内容:[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setBackButtonBackgroundVerticalPositionAdjustment:-3 forBarMetrics:UIBarMetricsDefault]; - barndog
显示剩余5条评论

18

对于那些正在开发适用于iOS 5的应用程序并且看到这篇文章而感到沮丧的人们...尝试像这样做:

float my_offset_plus_or_minus = 3.0f;

UIBarButtonItem * item = [[UIBarButtonItem alloc] initWithTitle:@"title" 
                                                          style:UIBarButtonItemStyleDone
                                                          target:someObject action:@selector(someMessage)];

[item setBackgroundVerticalPositionAdjustment:my_offset_plus_or_minus forBarMetrics:UIBarMetricsDefault];

12

最好的方法是按照此处所述来为您的UINavigationBar创建子类:https://dev59.com/-ljUa4cB1Zd3GeqPVd8z#17434530

这是我的示例:

#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

希望能够有所帮助。


1
它的功能很好,但是对于所有子视图使用循环并不是必要的操作。你只需要取出[[navigationItem leftBarButtonItem] customView]或者[[navigationItem rightBarButtonItem] customView],然后直接设置这些框架即可。 - Rostyslav Druzhchenko

6

我需要将我的按钮向右移动一些。以下是我使用Swift中的UIAppearance进行操作的方法。那里还有一个垂直位置属性,所以我想你可以在任何方向上进行调整。

UIBarButtonItem.appearance().setTitlePositionAdjustment(UIOffset.init(horizontal: 15, vertical: 0), forBarMetrics: UIBarMetrics.Default)

这对我来说似乎比直接处理框架或添加自定义子视图要少侵入性。

3
我相信这仅适用于标题文本,而不适用于栏按钮项目。 - mxcl

6
尝试以下代码:
UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithTitle:@"Logout" style:UIBarButtonItemStyleDone target:self action:nil];
[button setBackgroundVerticalPositionAdjustment:-20.0f forBarMetrics:UIBarMetricsDefault];
[[self navigationItem] setRightBarButtonItem:button];

这段代码用于改变'y'位置。根据您的需求更改'y'值(这里是-20.0f)。如果该值为正数,则会向下移动按钮位置。如果该值为负数,则会向上移动按钮位置。


4

使用改变左侧栏位置和图像边缘插入的导航栏

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)

4

如果您只是使用图像而不是默认的Chrome,则可以使用负图像插入(在“大小检查器”中设置)在UIBarButtonItem内部移动您的图像(非常方便,因为默认情况下,水平填充可能导致图像位于您想要的位置之内)。您还可以使用图像插入将图像定位在UIBarButtonItem的范围之外,而且左侧按钮区域周围的整个区域都可以点击,因此您无需担心其定位是否靠近触摸目标。 (至少,在合理范围内。)


[leftButton setImageEdgeInsets:UIEdgeInsetsMake(0, -20, 0, 20)]; - Graham Perks

3
在我的情况下:
  1. 更改barbuttonItem的框架以自定义间距

  2. 动态添加、删除barButtonItems。

  3. 通过tableview的contentOffset.y更改色调颜色。

像这样 enter image description here enter image description here

enter image description here enter image description here

如果您的最低目标是iOS 11,则可以在viewDidLayoutSubviews中更改barButton框架。

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // Change the navigationBar item frames
    if let customView = wishButton.customView?.superview {
        customView.transform = CGAffineTransform(translationX: 7.0, y: 0)
    }

    if let customView = gourmetCountButton.customView?.superview {
        customView.transform = CGAffineTransform(translationX: 9.0, y: 0)
    }
}

但是,它只适用于iOS 11。

我也尝试使用fixedSpace。但在多个navigationBarButton项目中它无效。

 let space = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
    space.width = -10

因此,我将customView的宽度更改为调整水平空间。

这是我的barButtonItem类之一。

final class DetailShareBarButtonItem: UIBarButtonItem {

// MARK: - Value
// MARK: Public
***// Change the width to adjust space***
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 32.0, height: 30.0))

override var tintColor: UIColor? {
    didSet {
        button.tintColor = tintColor
    }
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    setButton()
}

required override init() {
    super.init()
    setButton()
}


// MARK: - Function
// MARK: Private
private func setButton() {
    // Button
    button.setImage( #imageLiteral(resourceName: "navibarIcShare02White").withRenderingMode(.alwaysTemplate), for: .normal)
    button.tintColor              = .white
    button.imageEdgeInsets        = UIEdgeInsetsMake(0, 1.0, 1.0, 0)
    button.imageView?.contentMode = .scaleAspectFill

    let containerView = UIView(frame: button.bounds)
    containerView.backgroundColor = .clear
    containerView.addSubview(button)
    customView = containerView
}
}

这是结果。

我在iOS 9 ~ 11(Swift 4)上进行了测试。

输入图像描述


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接