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

以下代码通过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]; 

这是button应该如何alignment:

正常定位

但是,在iOS 7上,该button看起来偏右或偏左,像素太多:

在iOS 7上定位不正确

我如何让我的自定义栏button项目正确alignment?

适用于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]; 

为了解决这个错误,你必须inheritanceUIButton,这样你才能覆盖alignmentRectInsets 。 从我的testing中,您需要根据button的位置,返回一个带正向右偏移量或正向左偏移量的UIEdgeInsets。 这些数字对我来说是毫无意义的(根据常识,至less其中一个数据应该是负数),但这是实际工作的结果:

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

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

我已经尝试了上面的所有答案,没有为我工作。 这是什么工作,如果有人需要这个:

(不需要子类)

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

– 看看结果。

注意左侧button和右侧button的空间(右侧button具有默认行为)

在这里输入图像说明

好吧,我去了另一个“方向”。 我通过使用iOS 7的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); } 

所以这个代码只能运行在iOS 7以前的设备上。

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

看到什么是不对的?

结果如下。 第二个图像不是很明显,因为我不得不花时间截图,它仍然在转换,但你可以清楚地看到它是如何不正确的抵消。

良好的正常形象:

在浏览之前,这是一个好的形象

坏的偏移图像:

你可以看到偏移现在再次关闭之前,抢购

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

这是我的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 

没有跳跃button的更好的解决scheme可以在这里find:

自定义的UIBarButtonItem与iOS7closures

不是UIButton的子类化或从Marius的答案调整方法的巨大风扇: https : //stackoverflow.com/a/19317105/287403

我只是使用了一个简单的包装,并将button的框架的x向负方向移动,直到我find了正确的位置。 button敲击似乎也很好(虽然如果需要,可以扩展button的宽度以匹配负偏移量)。

这里是我用来生成一个新的后退button的代码:

 - (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确认回答下面的覆盖方法给那些谁有导航button中的文本和/或图像,否则文本和图像将不会alignment(只有背景图像):

 - (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的macros是:

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

* 解决scheme发现,阅读答案的结尾。 *

@jaredsinclair

我和凯尔·贝格曼有类似的情况

这是button

  - (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时,一切看起来都很棒,button重新定位本身。 就像一个小跳跃。 对我来说,它不会马上跳起来。 在我从NavigationViewController中执行popVC之后就会发生这种情况。

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

我想出了一个简短的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); } 

奇迹般有效。

在ios7中,你可以添加一个虚拟barbuttonitem固定左边的空间,你应该添加虚拟作为第一,右为最后

左边的例子,你应该在设置你的原始项目后添加这个,或者如果你正在使用storyboard设置button的话,在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]; 

为您的UIButton更改以下内容

控制 – >alignment – >水平右alignment

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