自定义视图的 UIBarButtonItem 在 iOS7上用作左或右导航栏项时没有正确对齐

以下代码通过 iOS6运行:

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];

按钮应该是这样对齐的:

Normal positioning

然而,在 iOS7上,这个按钮似乎偏离了右边或者左边太多的像素:

Incorrect positioning on iOS 7

我怎样才能让我的自定义条形按钮项目正确对齐?

59789 次浏览

为了修复这个 bug,您必须子类化 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。

好吧,我走了另一个“方向”。我通过 iOS7的 Storyboard 把所有东西都正确地排列起来(假设这是它将继续工作的方式)。然后采用描述子类的方法,对子类 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);
}

所以这段代码只能在 iOS7之前的设备上运行。

谢谢你的洞察力@jaredsinclair!

@ 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;

看到什么不对劲的地方了吗?

结果是这样的。第二张图片不是很明显,因为我不得不花时间截屏,它仍然处于过渡阶段,但是你可以清楚地看到它是如何不适当地偏移。

良好的正常图像:

This is the good image, before navigating around

坏的抵消图片来源:

You can see the offset is now off again before snapping into place

大约半秒钟后,图像会快速回到原始图像的位置。

下面是我的 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

我和 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 之后就会发生这种情况。

编辑: 下面的答案,马吕斯的调酒方法对我也有帮助

在这里可以找到一个不需要跳转按钮的更好的解决方案:

自定义 UIBarButtonItem 与 iOS7对齐

以上所有的答案我都试过了,没有一个对我有用。如果有人需要的话,以下是有效的方法:

(不需要子类化)

// 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);

看看结果。

注意左边按钮和右边按钮之间的空间( 右键具有默认行为)

enter image description here

不是一个子类化 UIButton或者从 Marius 的答案: 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];
}

我想添加到@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];
}

另外,宏观来看:

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

我想出了一个简化版的贾里德辛克莱尔的方法:

- (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);
}

非常有效。

在 ios7中,你可以添加一个假的 barbuttonitem 为了固定左边的空间,你应该先加上假人,然后再加上右边的假人

例如,如果你使用故事板设置按钮,你应该在设置你的原始项目之后添加这个。

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];

一直工作到 iOS11!

你可以使用负的弹性空格和 right BarButtonItems 属性来代替 right BarButtonItem:

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];

UIButton更改以下内容

控件-> 对齐-> 水平对齐到右对齐

它只是简单地调整 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];

在 iOS11中测试

 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


}()

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]