iOS 11 上导航栏中 UIBarButtonItem 的负间隔符
Negative spacer for UIBarButtonItem in navigation bar on iOS 11
在 iOS 10 及以下版本中,有一种方法可以向导航栏中的按钮数组添加负间隔符,如下所示:
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSpacer.width = -8;
self.navigationItem.leftBarButtonItems = @[negativeSpacer, [self backButtonItem]];
这不再适用于 iOS 11(间隔变为正,而不是负)。我已经检查了条形按钮项的视图层次结构,它现在嵌入到 _UIButtonBarStackView
中。如何调整iOS11上的条形按钮位置?
编辑:
从 iOS 13 开始,这可能不再有效。您可能会收到错误消息:
Client error attempting to change layout margins of a private view
旧答案:
我在 Apple 开发者论坛上发现了一个有点老套的解决方案:
https://forums.developer.apple.com/thread/80075
看起来问题出在 iOS 11 如何处理 UIBarButtonItem
.fixedSpace
按钮以及 UINavigationBar
在 iOS 11 中的布局方式. 导航栏现在使用自动布局和布局边距来布局按钮。 post(底部)中提出的解决方案是将所有布局边距设置为您想要的某个值。
class InsetButtonsNavigationBar: UINavigationBar {
override func layoutSubviews() {
super.layoutSubviews()
for view in subviews {
// Setting the layout margins to 0 lines the bar buttons items up at
// the edges of the screen. You can set this to any number to change
// the spacing.
view.layoutMargins = .zero
}
}
}
要使用这个带有自定义按钮间距的新导航栏,您需要使用以下代码更新您创建任何导航控制器的位置:
let navController = UINavigationController(navigationBarClass: InsetButtonsNavigationBar.self,
toolbarClass: UIToolbar.self)
navController.viewControllers = [yourRootViewController]
根据 keithbhunter 的回答,我创建了一个自定义 UINavigationBar:
NavigationBarCustomMargins.h:
#import <UIKit/UIKit.h>
@interface NavigationBarCustomMargins : UINavigationBar
@property (nonatomic) IBInspectable CGFloat leftMargin;
@property (nonatomic) IBInspectable CGFloat rightMargin;
@end
NavigationBarCustomMargins.m:
#import "NavigationBarCustomMargins.h"
#define DefaultMargin 16
#define NegativeSpacerTag 87236223
@interface NavigationBarCustomMargins ()
@property (nonatomic) BOOL leftMarginIsSet;
@property (nonatomic) BOOL rightMarginIsSet;
@end
@implementation NavigationBarCustomMargins
@synthesize leftMargin = _leftMargin;
@synthesize rightMargin = _rightMargin;
- (void)layoutSubviews {
[super layoutSubviews];
if (([[[UIDevice currentDevice] systemVersion] compare:@"11.0" options:NSNumericSearch] != NSOrderedAscending)) {
BOOL isRTL = [UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
for (UIView *view in self.subviews) {
view.layoutMargins = UIEdgeInsetsMake(0, isRTL ? self.rightMargin : self.leftMargin, 0, isRTL ? self.leftMargin : self.rightMargin);
}
} else {
//left
NSMutableArray *leftItems = [self.topItem.leftBarButtonItems mutableCopy];
if (((UIBarButtonItem *)leftItems.firstObject).tag != NegativeSpacerTag) {
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSpacer.tag = NegativeSpacerTag;
negativeSpacer.width = self.leftMargin - DefaultMargin;
[leftItems insertObject:negativeSpacer atIndex:0];
[self.topItem setLeftBarButtonItems:[leftItems copy] animated:NO];
}
//right
NSMutableArray *rightItems = [self.topItem.rightBarButtonItems mutableCopy];
if (((UIBarButtonItem *)rightItems.firstObject).tag != NegativeSpacerTag) {
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSpacer.tag = NegativeSpacerTag;
negativeSpacer.width = self.rightMargin - DefaultMargin;
[rightItems insertObject:negativeSpacer atIndex:0];
[self.topItem setRightBarButtonItems:[rightItems copy] animated:NO];
}
}
}
- (CGFloat)leftMargin {
if (_leftMarginIsSet) {
return _leftMargin;
}
return DefaultMargin;
}
- (CGFloat)rightMargin {
if (_rightMarginIsSet) {
return _rightMargin;
}
return DefaultMargin;
}
- (void)setLeftMargin:(CGFloat)leftMargin {
_leftMargin = leftMargin;
_leftMarginIsSet = YES;
}
- (void)setRightMargin:(CGFloat)rightMargin {
_rightMargin = rightMargin;
_rightMarginIsSet = YES;
}
@end
之后,我在 Interface Builder 中为我的 UINavigationController 设置了自定义 class,并设置了所需的边距:
Screenshot 1
工作正常。支持 RTL 和 iOS prior 11:
Screenshot 2
这只是针对我的情况的解决方法,可能对某些人有帮助。我想实现这个:
之前我也使用过 negativeSpacer。现在我想出了这个解决方案:
let logoImage = UIImage(named: "your_image")
let logoImageView = UIImageView(image: logoImage)
logoImageView.frame = CGRect(x: -16, y: 0, width: 150, height: 44)
logoImageView.contentMode = .scaleAspectFit
let logoView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 44))
**logoView.clipsToBounds = false**
logoView.addSubview(logoImageView)
let logoItem = UIBarButtonItem(customView: logoView)
navigationItem.leftBarButtonItem = logoItem
这个答案对我有帮助https://whosebug.com/a/44896832
特别是我设置了 imageEdgeInsets 和 titleEdgeInsets 因为我的按钮同时包含图像和标题
在 iOS 10 及以下版本中,有一种方法可以向导航栏中的按钮数组添加负间隔符,如下所示:
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSpacer.width = -8;
self.navigationItem.leftBarButtonItems = @[negativeSpacer, [self backButtonItem]];
这不再适用于 iOS 11(间隔变为正,而不是负)。我已经检查了条形按钮项的视图层次结构,它现在嵌入到 _UIButtonBarStackView
中。如何调整iOS11上的条形按钮位置?
编辑:
从 iOS 13 开始,这可能不再有效。您可能会收到错误消息:
Client error attempting to change layout margins of a private view
旧答案:
我在 Apple 开发者论坛上发现了一个有点老套的解决方案: https://forums.developer.apple.com/thread/80075
看起来问题出在 iOS 11 如何处理 UIBarButtonItem
.fixedSpace
按钮以及 UINavigationBar
在 iOS 11 中的布局方式. 导航栏现在使用自动布局和布局边距来布局按钮。 post(底部)中提出的解决方案是将所有布局边距设置为您想要的某个值。
class InsetButtonsNavigationBar: UINavigationBar {
override func layoutSubviews() {
super.layoutSubviews()
for view in subviews {
// Setting the layout margins to 0 lines the bar buttons items up at
// the edges of the screen. You can set this to any number to change
// the spacing.
view.layoutMargins = .zero
}
}
}
要使用这个带有自定义按钮间距的新导航栏,您需要使用以下代码更新您创建任何导航控制器的位置:
let navController = UINavigationController(navigationBarClass: InsetButtonsNavigationBar.self,
toolbarClass: UIToolbar.self)
navController.viewControllers = [yourRootViewController]
根据 keithbhunter 的回答,我创建了一个自定义 UINavigationBar:
NavigationBarCustomMargins.h:
#import <UIKit/UIKit.h>
@interface NavigationBarCustomMargins : UINavigationBar
@property (nonatomic) IBInspectable CGFloat leftMargin;
@property (nonatomic) IBInspectable CGFloat rightMargin;
@end
NavigationBarCustomMargins.m:
#import "NavigationBarCustomMargins.h"
#define DefaultMargin 16
#define NegativeSpacerTag 87236223
@interface NavigationBarCustomMargins ()
@property (nonatomic) BOOL leftMarginIsSet;
@property (nonatomic) BOOL rightMarginIsSet;
@end
@implementation NavigationBarCustomMargins
@synthesize leftMargin = _leftMargin;
@synthesize rightMargin = _rightMargin;
- (void)layoutSubviews {
[super layoutSubviews];
if (([[[UIDevice currentDevice] systemVersion] compare:@"11.0" options:NSNumericSearch] != NSOrderedAscending)) {
BOOL isRTL = [UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
for (UIView *view in self.subviews) {
view.layoutMargins = UIEdgeInsetsMake(0, isRTL ? self.rightMargin : self.leftMargin, 0, isRTL ? self.leftMargin : self.rightMargin);
}
} else {
//left
NSMutableArray *leftItems = [self.topItem.leftBarButtonItems mutableCopy];
if (((UIBarButtonItem *)leftItems.firstObject).tag != NegativeSpacerTag) {
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSpacer.tag = NegativeSpacerTag;
negativeSpacer.width = self.leftMargin - DefaultMargin;
[leftItems insertObject:negativeSpacer atIndex:0];
[self.topItem setLeftBarButtonItems:[leftItems copy] animated:NO];
}
//right
NSMutableArray *rightItems = [self.topItem.rightBarButtonItems mutableCopy];
if (((UIBarButtonItem *)rightItems.firstObject).tag != NegativeSpacerTag) {
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSpacer.tag = NegativeSpacerTag;
negativeSpacer.width = self.rightMargin - DefaultMargin;
[rightItems insertObject:negativeSpacer atIndex:0];
[self.topItem setRightBarButtonItems:[rightItems copy] animated:NO];
}
}
}
- (CGFloat)leftMargin {
if (_leftMarginIsSet) {
return _leftMargin;
}
return DefaultMargin;
}
- (CGFloat)rightMargin {
if (_rightMarginIsSet) {
return _rightMargin;
}
return DefaultMargin;
}
- (void)setLeftMargin:(CGFloat)leftMargin {
_leftMargin = leftMargin;
_leftMarginIsSet = YES;
}
- (void)setRightMargin:(CGFloat)rightMargin {
_rightMargin = rightMargin;
_rightMarginIsSet = YES;
}
@end
之后,我在 Interface Builder 中为我的 UINavigationController 设置了自定义 class,并设置了所需的边距: Screenshot 1
工作正常。支持 RTL 和 iOS prior 11: Screenshot 2
这只是针对我的情况的解决方法,可能对某些人有帮助。我想实现这个:
let logoImage = UIImage(named: "your_image")
let logoImageView = UIImageView(image: logoImage)
logoImageView.frame = CGRect(x: -16, y: 0, width: 150, height: 44)
logoImageView.contentMode = .scaleAspectFit
let logoView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 44))
**logoView.clipsToBounds = false**
logoView.addSubview(logoImageView)
let logoItem = UIBarButtonItem(customView: logoView)
navigationItem.leftBarButtonItem = logoItem
这个答案对我有帮助https://whosebug.com/a/44896832
特别是我设置了 imageEdgeInsets 和 titleEdgeInsets 因为我的按钮同时包含图像和标题