具有自定义视图的UIBarButtonItem在用作左侧或右侧导航栏项目时在iOS 7上未正确对齐


75

以下代码可通过iOS 6正常运行:

UIButton *myButton = nil;
myButton = [UIButton buttonWithType:UIButtonTypeCustom];
myButton.bounds = CGRectMake(0,0,44,30);
// setup myButton's images, etc.

UIBarButtonItem *item = nil;
item = [[UIBarButtonItem alloc] initWithCustomView:customButton];

这是应该对齐按钮的方式:

正常定位

但是,在iOS 7上,按钮似乎从右侧或左侧偏移了太多像素:

在iOS 7上的位置不正确

如何使自定义栏按钮项正确对齐?


4
也许我的解决方案会为您提供帮助:如何解决iOS 7 UIBarButtonItem间距
MariusKažemėkaitis13年

Answers:


67

适用于iOS11!

您可以使用负的灵活空格和rightBarButtonItems属性,而不是rightBarButtonItem:

UIBarButtonItem *spacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spacer.width = -10; // for example shift right bar button to the right

self.navigationItem.rightBarButtonItems = @[spacer, yourBarButton];

2
我不明白为什么我们要破解这么简单的东西……至少已经存在一年了。苹果无解?
Petar 2014年

3
它违反了《人机界面指南》。这就是为什么它不可定制。他们希望您别管它。对于不阅读指南的设计师来说,这是一个问题。
smileBot 2014年

尽管我曾就这样的事情警告过一位设计师朋友,但我可以确认它是可行的。现在,我们有了一个超大的AccessoriesView。
2015年

1
奇迹般有效。这是我可以找到无需子类就可以在所有iOS 6〜8系统上工作的唯一方法UINavigationBar。好答案!
liuyaodong 2015年

是。Apple更改导航栏的布局。
malex

57

为了修复此错误,必须将UIButton子类化,以便您可以重写alignmentRectInsets。从我的测试中,您需要返回一个UIEdgeInsets,该UIEdgeInsets带有一个正的右偏移量或一个正的左偏移量,具体取决于按钮的位置。这些数字对我来说没有任何意义(根据常识,至少其中一个应该为负数),但这实际上是有效的:

- (UIEdgeInsets)alignmentRectInsets {
    UIEdgeInsets insets;
    if (IF_ITS_A_LEFT_BUTTON) {
        insets = UIEdgeInsetsMake(0, 9.0f, 0, 0);
    } 
    else { // IF_ITS_A_RIGHT_BUTTON
        insets = UIEdgeInsetsMake(0, 0, 0, 9.0f);
    }
    return insets;
}

特别感谢@zev建议我尝试调整alignmentRectInsets。


1
我是否必须仅在iOS7上应用该代码?那iOS6呢?谢谢。
里卡多2013年

克里斯:在我的代码中,我在分配/初始化按钮时设置位置。由于仍然是子类,因此我@property为该位置添加了一个自定义(左或右)。里卡多:这只是针对iOS的7
jaredsinclair

3
这是可行的,但是当我从导航堆栈中弹出视图并将其设置为“后退”动画时,该按钮仍会错误地偏移约半秒钟,然后跳转到正确的位置。知道如何防止这种情况发生吗?
凯尔·贝格曼

当我实现这个时,我有同样的问题。现在我的按钮从其位置跳转。我使用了这个答案stackoverflow.com/questions/15261148/…但这也没有帮助我。有任何想法吗?
莉莉2013年

@KyleBegeman&Lily —我在iOS 7上没有看到此行为。您是否在iOS 6上使用此代码?
jaredsinclair 2013年

28

我已经尝试了以上所有答案,但对我没有任何帮助。如果有人需要,这是可行的方法:

(无需子类化)

// Add your barButtonItem with custom image as the following 
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleBordered target:self action:@selector(categoryButtonPressed)];
// set your custom image
[barButton setImage:categoryImage];
// finally do the magic
barButton.imageInsets = UIEdgeInsetsMake(0.0, -20, 0, 0);

-看一下结果。

请注意左侧按钮和右侧按钮之间的空格(右侧按钮具有默认行为)

在此处输入图片说明


10

好吧,我去了另一个“方向”。我通过iOS 7的Storyboard使所有内容正确排列(假设这将继续发挥作用)。然后使用describe子类方法,UIButton通过以下实现对我进行子类化。

- (UIEdgeInsets)alignmentRectInsets {
    UIEdgeInsets insets;
    if (SYSTEM_VERSION_LESS_THAN(@"7.0")) {
        if ([self isLeftButton]) {
            insets = UIEdgeInsetsMake(0, -10, 0, 0);
        } else {
            insets = UIEdgeInsetsMake(0, 0, 0, -10);
        }
    } else {
        insets = UIEdgeInsetsZero;
    }

    return insets;
}

- (BOOL)isLeftButton {
    return self.frame.origin.x < (self.superview.frame.size.width / 2);
}

因此,此代码仅在设备为iOS 7之前的版本下运行。

感谢您的见识@jaredsinclair!


工作正常,尽管您可能希望UIEdgeInsetsMake(0,0,0,10)而不是-10用于右键按钮。
Şafak基色

我实际上在iOS 6上的动画制作过程中遇到了这个问题,所以我最终退出了这条路线,stackoverflow.com / a / 19169682/250190
Chris Wagner 2013年

3

@jaredsinclair

这是我的代码。

// Create the tweetly button that will show settings
self.tweetlyDisplay = [NavButton buttonWithType:UIButtonTypeCustom];
[self.tweetlyDisplay setFrame:CGRectMake(0, 0, 90, 44)];
[self.tweetlyDisplay setBackgroundColor:[UIColor clearColor]];
[self.tweetlyDisplay setBackgroundImage:[UIImage imageNamed:@"settings.png"] forState:UIControlStateNormal];
[self.tweetlyDisplay setAdjustsImageWhenHighlighted:NO];
[self.tweetlyDisplay addTarget:self action:@selector(tweetlyPressed:) forControlEvents:UIControlEventTouchUpInside];

// Add the Tweetly button as the left bar button item
// This had a glitch that moves the image to the right somewhat
UIBarButtonItem *leftBarButton = [[UIBarButtonItem alloc] initWithCustomView:self.tweetlyDisplay];
self.navigationItem.leftBarButtonItem = leftBarButton;

看到什么不对吗?

这是结果。第二张图像不太明显,因为我不得不花点时间截屏,并且它仍处于过渡状态,但是您可以清楚地看到它是如何不正确地偏移的。

正常图像:

这是很好的形象,然后浏览

错误的偏移图像:

您可以看到偏移现在再次关闭,然后卡入到位

大约半秒钟后,图像将重新回到原始图像位置。

这是我的NavButton.h和.m代码:

/**********************************************

 NavButton.h

 **********************************************/

#import <UIKit/UIKit.h>

@interface NavButton : UIButton

@end






/**********************************************

 NavButton.m

**********************************************/


#import "NavButton.h"

@implementation NavButton {

    int imageHeight;

}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code

        imageHeight = 44;

    }
    return self;
}

/*
 // Only override drawRect: if you perform custom drawing.
 // An empty implementation adversely affects performance during animation.
 - (void)drawRect:(CGRect)rect
 {
 // Drawing code
 }
 */


- (UIEdgeInsets)alignmentRectInsets {
    UIEdgeInsets insets;
    if ([self isLeftButton]) {
        insets = UIEdgeInsetsMake(0, 9.0f, 0, 0);
    }
    else { // IF ITS A RIGHT BUTTON
        insets = UIEdgeInsetsMake(0, 0, 0, 9.0f);
    }
    return insets;
}


- (BOOL)isLeftButton {
    return self.frame.origin.x < (self.superview.frame.size.width / 2);
}


// THIS IS THE TRICK.  We make the height of the background rect match the image.
-(CGRect)backgroundRectForBounds:(CGRect)bounds
{
    CGRect bgRect = bounds;
    bgRect.origin.y = (bounds.size.height - imageHeight)/2.0f;
    bgRect.size.height = imageHeight;

    return bgRect;
}


@end

这一切对我来说都很好。我上面提出的解决方法可能在这种情况下不起作用。您可以尝试使用右上角的按钮进行设计,不是吗?
jaredsinclair 2013年

右上角已经有自己的按钮!我只需要看看我还能做什么。任何想法如何知道弹出堆栈后何时再次加载根视图控制器?也许我可以将按钮添加到导航栏视图中,而不是将其添加为导航栏按钮项,然后在按下视图控制器时将其淡出,然后在弹出到根视图控制器时将其淡入
Kyle Begeman

将自己的视图添加到导航栏会带来很多伤害。除非别无选择,否则我不会这么做。您可以尝试使用UINavigationControllerDelegate方法进行查找。
jaredsinclair 2013年

该解决方案可以在这里找到:stackoverflow.com/questions/19233591/...
rockstarberlin


1

UIButton马里乌斯的答案不是子类或方法的狂热粉丝:https : //stackoverflow.com/a/19317105/287403

我只是使用了一个简单的包装,然后将按钮框的x沿负方向移动,直到找到正确的位置。敲击按钮似乎也很好(尽管您可以根据需要扩展按钮的宽度以匹配负偏移量)。

这是我用来生成新的后退按钮的代码:

- (UIBarButtonItem *) newBackButton {
    // a macro for the weak strong dance
    @weakify(self);
    UIButton *backButton = [[UIButton alloc] init];
    [backButton setTitle:@"Back" forState:UIControlStateNormal];
    backButton.titleLabel.font = [UIFont systemFontOfSize:17];
    CGFloat width = [@"Back" boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesFontLeading|NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:17]} context:nil].size.width + 27;
    backButton.frame = CGRectMake(-17.5, 0, width + 17.5, 44);
    [backButton setImage:[UIImage imageNamed:@"UINavigationBarBackIndicatorDefault"] forState:UIControlStateNormal];
    [backButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 14, 0, 0)];
    [backButton setTitleColor:mTCOrangeColor forState:UIControlStateNormal];
    backButton.contentEdgeInsets = UIEdgeInsetsZero;
    backButton.imageEdgeInsets = UIEdgeInsetsZero;
    [backButton addEventHandler:^(id sender) {
        // a macro for the weak strong dance
        @strongify(self);
        // do stuff here
    } forControlEvents:UIControlEventTouchUpInside];
    UIView *buttonWrapper = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, 44)];
    [buttonWrapper addSubview:backButton];
    return [[UIBarButtonItem alloc] initWithCustomView:buttonWrapper];
}

1

我想在导航按钮中具有文本和/或图像的用户中添加以下问题的覆盖方法,以@jaredsinclair确认的答案,否则文本和图像将不对齐(仅背景图像):

- (UIEdgeInsets) titleEdgeInsets {
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
{
    if (IF_ITS_A_LEFT_BUTTON) {
    {
        return UIEdgeInsetsMake(0, 0, 0, 9.0f);
    }
    else
    { // IF_ITS_A_RIGHT_BUTTON
        return UIEdgeInsetsMake(0, 9.0f, 0, 0);
    }
}
return [super titleEdgeInsets];
}

- (UIEdgeInsets)imageEdgeInsets {
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
{
    if (IF_ITS_A_LEFT_BUTTON)
    {
        return UIEdgeInsetsMake(0, 0, 0, 9.0f);
    }
    else
    { // IF_ITS_A_RIGHT_BUTTON
        return UIEdgeInsetsMake(0, 9.0f, 0, 0);
    }
}

return [super imageEdgeInsets];
}

ps宏是:

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

0

*找到解决方案,请在答案末尾阅读。*

@jaredsinclair

我的情况与凯尔·贝格曼(Kyle Begeman)类似

这是按钮

 - (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
    }
    return self;
}

- (UIEdgeInsets)alignmentRectInsets {
    UIEdgeInsets insets;

    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")){
        if ([self isLeftButton]) {
            insets = UIEdgeInsetsMake(0, 9.0f, 0, 0);
        } else {
            insets = UIEdgeInsetsMake(0, 0, 0, 9.0f);
        }
    }else{
        insets = UIEdgeInsetsZero;
    }

    return insets;
}

- (BOOL)isLeftButton {
    return self.frame.origin.x < (self.superview.frame.size.width / 2);
}

在这种情况下我会用它

PBNavigationBarButton *dashboardButton = [PBNavigationBarButton buttonWithType:UIButtonTypeCustom];
    UIImage *dashboardImage = [UIImage imageNamed:@"btn_dashboard.png"];
    UIImage *dashboardImageHighlighted = [UIImage imageNamed:@"btn_dashboard_pressed.png"];

    NSInteger halfImageWidth = ceilf([dashboardImage size].width/2.0f);

    [dashboardButton setBackgroundImage:[dashboardImage stretchableImageWithLeftCapWidth:halfImageWidth topCapHeight:0] forState:UIControlStateNormal];
    [dashboardButton setBackgroundImage:[dashboardImageHighlighted stretchableImageWithLeftCapWidth:halfImageWidth topCapHeight:0] forState:UIControlStateHighlighted];
    [dashboardButton addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
    [dashboardButton sizeToFit];

    UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc]  initWithCustomView:dashboardButton];

一切看起来都很不错,除了当我回到上一个VC时,按钮本身重新定位。像一个小小的跳跃。对我来说,它确实会立即跳起来。它发生在我从NavigationViewController执行popVC之后。

编辑:下面的答案,从马吕斯的the草方法也帮助了我。


0

我想出了jaredsinclair方法的简化版本:

- (UIEdgeInsets)alignmentRectInsets {
    return [self isLeftButton] ? UIEdgeInsetsMake(0, 9.0f, 0, 0) : UIEdgeInsetsMake(0, 0, 0, 9.0f); 
}

- (BOOL)isLeftButton {
    return self.frame.origin.x < (self.superview.frame.size.width / 2);
}

奇迹般有效。


0

在ios7中,您可以仅添加一个虚拟barbuttonitem来固定左侧空间,您应该首先添加虚拟对象,最后添加右侧对象

例如左图,如果您使用情节提要设置按钮,则应在设置原始项目后或在viewdidload中添加此内容。

NSMutableArray *buttons = [[NSMutableArray alloc] init];
UIBarButtonItem *spacerItem = [[UIBarButtonItem alloc] init];
[buttons addObject:spacerItem];
for(UIBarButtonItem *item in self.leftBarButtonItems){
    [buttons addObject:item];
}
[self setLeftBarButtonItems:[NSArray arrayWithArray:buttons] animated:NO];

0

它只是调整imageEdgeInsets以解决此错误。

尝试这个。

    UIButton *actionBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
    [actionBtn setImage:[UIImage imageNamed:@"arrow.png"] forState:UIControlStateNormal];
    [actionBtn addTarget:self action:@selector(goToSetting) forControlEvents:UIControlEventTouchUpInside];
    actionBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 9, 0, -9);

    UIBarButtonItem *profileBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:actionBtn];
    self.topViewController.navigationItem.rightBarButtonItems = @[profileBarButtonItem];

0

在iOS 11中测试

 public lazy var navigationBackBarButton : UIBarButtonItem = {

        let backBarButtonIcon = <Image Literal here>
        let backBarButton = UIBarButtonItem(image: backBarButtonIcon, style: .plain, target: self, action: #selector(self.backBarButtonTouched(_:)))
        backBarButton.imageInsets = UIEdgeInsets(top: 0.0, left: -9.0, bottom: 0.0, right: 0.0)
        return backBarButton

    }()

0

2018年,iOS 11 +,Swift 4.x,这对我有用。

结合最佳答案:

属性

internal lazy var button_Favorite: UIButton = {
    let button = UIButton(type: .custom)
    button.setImage(.favNormal, for: .normal)
    button.contentHorizontalAlignment = .right
    button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -9.0)
    return button
}()

viewDidLoad

    let barButton = UIBarButtonItem(customView: self.button_Favorite)
    self.button_Favorite.frame = CGRect(x: 0, y: 0, width: 40.0, height: 44.0)
    let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
    negativeSpacer.width = -10.0
    self.navigationItem.rightBarButtonItems = [negativeSpacer, barButton]

-1

为您更改以下内容 UIButton

控制->对齐->水平以右对齐

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.