具有自定义视图的 UIBarButtonItem 在 iOS 7 上用作左侧或右侧导航栏项目时未正确对齐
Posted
技术标签:
【中文标题】具有自定义视图的 UIBarButtonItem 在 iOS 7 上用作左侧或右侧导航栏项目时未正确对齐【英文标题】:UIBarButtonItem with custom view not properly aligned on iOS 7 when used as left or right navigation bar items 【发布时间】:2013-09-22 13:28:53 【问题描述】:以下代码适用于 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 上,按钮似乎从右侧或左侧偏移了太多像素:
如何让我的自定义栏按钮项目正确对齐?
【问题讨论】:
也许我的解决方案会对您有所帮助:How to fix iOS 7 UIBarButtonItem spacing 【参考方案1】:工作到 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];
【讨论】:
我无法理解为什么我们必须破解如此简单的东西......这至少已经存在了一年。苹果没有解决方案? 它违反了人机界面指南。这就是它不可定制的原因。他们希望你别管它。这是不阅读指南的设计师的问题。 虽然我警告过一位设计师朋友这样的事情,但我可以确认它有效。我们现在有一个超大的附件视图。 像魅力一样工作。这是我能找到在所有 iOS 6 ~ 8 系统上工作而无需子类化UINavigationBar
的唯一方法。很好的答案!
是的。苹果改变了导航栏的布局。【参考方案2】:
为了修复这个错误,你必须继承 UIButton 以便你可以覆盖alignmentRectInsets
。根据我的测试,您需要返回一个 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 上应用该代码? iOS6呢?谢谢。 Chris:在我的代码中,我在分配/初始化按钮时设置了位置。因为它无论如何都是一个子类,所以我为左或右的位置添加了一个自定义的@property
。 Ricardo:这仅适用于 iOS 7。
这可行,但是当我从导航堆栈中弹出一个视图并动画“返回”时,按钮仍然错误地偏移大约半秒,然后它跳转到正确的位置。知道如何防止这种情况发生吗?
我在实现这个时遇到了同样的问题。现在我的按钮跳出它的位置。我使用了这个答案***.com/questions/15261148/…,但这也没有帮助我。有什么想法吗?
@KyleBegeman & Lily — 我在 iOS 7 上看不到这种行为。您在 iOS 6 上使用此代码吗?【参考方案3】:
我已经尝试了上述所有答案,但对我没有任何帮助。如果有人需要,这就是有效的方法:
(不需要子类化)
// 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);
-看看结果。
注意左侧按钮和右侧按钮之间的空格( 右键具有默认行为)
【讨论】:
【参考方案4】:好吧,我去了另一个“方向”。我通过 iOS 7 的 Storyboard 将所有内容正确排列(假设这将继续工作)。然后使用describe sub-class方法,我用下面的实现子类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 用于右键案例。 我实际上在 iOS 6 上的动画中遇到了这个问题,所以我最终把它拉出来走这条路,***.com/a/19169682/250190【参考方案5】:@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
【讨论】:
这一切对我来说都很好。可能是我上面提出的解决方法在这种特殊情况下不起作用。你可以试试右上角那个按钮的设计,不是吗? 右上角已经有了自己的按钮!我只需要看看我还能做什么。知道如何知道弹出堆栈后何时再次加载根视图控制器吗?也许我可以将按钮添加到导航栏视图而不是作为栏按钮项,然后在推送视图控制器时将其淡出,然后在弹出到根视图控制器时将其淡入 将自己的视图添加到导航栏是一件很痛苦的事情。除非我别无选择,否则我不会那样做。您可以尝试使用 UINavigationControllerDelegate 方法进行查找。 解决方案可以在这里找到:***.com/questions/19233591/…【参考方案6】:可以在此处找到无需跳转按钮的更好解决方案:
Custom UIBarButtonItem alignment off with iOS7
【讨论】:
【参考方案7】:不喜欢继承 UIButton
或从 Marius 的回答中调配方法:https://***.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];
【讨论】:
【参考方案8】:我想在@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)
【讨论】:
【参考方案9】:*找到解决方案,阅读答案末尾。*
@jaredsinclair
我和凯尔·贝格曼有类似的情况
这是按钮
- (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 之后。
编辑:下面的答案,Marius 的 swizzling 方法也帮助了我。
【讨论】:
【参考方案10】:我想出了一个 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);
像魅力一样工作。
【讨论】:
【参考方案11】:在 ios7 中,您可以添加一个虚拟 barbuttonitem 要修复左侧空间,您应该首先添加 dummy,最后添加右侧
左边的例子,你应该在设置你的原始项目之后添加它,或者如果你使用故事板设置按钮,你应该在 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];
【讨论】:
【参考方案12】:它只是简单地调整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];
【讨论】:
【参考方案13】:在 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
()
【讨论】:
【参考方案14】: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]
【讨论】:
【参考方案15】:为您的UIButton
更改以下内容
控制 -> 对齐 -> 水平右对齐
【讨论】:
以上是关于具有自定义视图的 UIBarButtonItem 在 iOS 7 上用作左侧或右侧导航栏项目时未正确对齐的主要内容,如果未能解决你的问题,请参考以下文章
UINavigationController 的自定义 UIBarButtonItem/UISegmentedControl
添加 UIStackView 作为 UIBarButtonItem 的自定义视图
使用多个导航栏视图自定义 UIBarButtonItem 外观