添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

相信很多同学都知道在iOS7之后调整导航栏两侧按钮距离左右间距,其实就是在左右barButtonItem的数组中添加一个宽度为负的占位item。

- (void)addLeftBarButtonItem:(UIBarButtonItem *)leftBarButtonItem
    UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
    space.width = -8;
    [self setLeftBarButtonItems:@[space, leftBarButtonItem]];
 

但是在iOS11之后,发现该方法失效了。新的思路和iOS7的完全不一样,我们给UINavigationBarContentView加一条约束。怎么加呢?

1 自定义一个customView,使用initWithCustomView创建UIBarButtonItem。

2 在customView的layoutSubviews方法中找到UINavigationBarContentView,添加customView和UINavigationBarContentView之间的约束。

customView定义如下:

#import "UIView.h"
typedef NS_ENUM(NSInteger, LFBarButtonItemViewType) {
    LFBarButtonItemViewTypeLeft,
    LFBarButtonItemViewTypeRight,
@interface LFBarButtonItemView : UIView
@property (nonatomic, assign) LFBarButtonItemViewType type;
  
@implementation LFBarButtonItemView
- (void)layoutSubviews {
    [super layoutSubviews];
    if (iOSVersion < 11.0) {
        return;
    //Here is a workaround on iOS 11 UINavigationBarItem init with custom view, position issue
    UIView *view = self;
    while (![view isKindOfClass:[UINavigationBar class]] && [view superview] != nil)
        view = [view superview];
        if ([view isKindOfClass:[UIStackView class]] && [view superview] != nil)
            if (self.type == LFBarButtonItemViewTypeLeft) {
                CGFloat margin = 0.0f;
                // 5.5寸plus间距大一点
                if ([[TWDeviceManager sharedManager] iPhone55]) {
                    margin = 4.0f;
                [view.superview addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:view.superview attribute:NSLayoutAttributeLeading multiplier:1.0 constant:margin]];
                break;
            } else if (self.type == LFBarButtonItemViewTypeRight) {
                CGFloat margin = 0.0f;
                // 5.5寸plus间距大一点
                if ([[TWDeviceManager sharedManager] iPhone55]) {
                    margin = -4.0f;
                [view.superview addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:view.superview attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:margin]];
                break;
 

在创建self.navigationItem.rightBarButtonItem的地方:

- (void)initNavigationBar {
    // rightItem
    UIImage *image = [UIImage imageNamed:@"share-icon-dark"];
    LFBarButtonItemView *rightItemCustomView = [[LFBarButtonItemView alloc] initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didClickOnShareButton)];
    [rightItemCustomView addGestureRecognizer:tap];
    rightItemCustomView.type = LFBarButtonItemViewTypeRight;
    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
    [imageView sizeToFit];
    [rightItemCustomView addSubview:imageView];
    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithCustomView:rightItemCustomView];
    self.navigationItem.rightBarButtonItem = rightItem;
 

但是这个方法会有约束冲突问题,所以我们把产生冲突的约束删除。可以用xcode查看视图层次,以方便理解。

- (void)layoutSubviews {
    [super layoutSubviews];
    if (iOSVersion < 11.0) {
        return;
    //Here is a workaround on iOS 11 UINavigationBarItem init with custom view, position issue
    UIView *view = self;
    while (![view isKindOfClass:[UINavigationBar class]] && [view superview] != nil)
        view = [view superview];
        if ([view isKindOfClass:[UIStackView class]] && [view superview] != nil)
            if (self.type == LFBarButtonItemViewTypeLeft) {
                CGFloat margin = kAppAdaptHeight(15);
                //删除原来的leading约束
                for (NSLayoutConstraint *constraint in view.superview.constraints) {
                    if ([constraint.firstItem isKindOfClass:[UILayoutGuide class]] &&
                        constraint.firstAttribute == NSLayoutAttributeTrailing) {
                        [view.superview removeConstraint:constraint];
                //添加新约束
                NSLayoutConstraint *leadingConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:view.superview attribute:NSLayoutAttributeLeading multiplier:1.0 constant:margin];
                leadingConstraint.priority = UILayoutPriorityRequired;
                [view.superview addConstraint:leadingConstraint];
                break;
            } else if (self.type == LFBarButtonItemViewTypeRight) {
                CGFloat margin = -kAppAdaptHeight(15);
                //删除原来的leading约束
                for (NSLayoutConstraint *constraint in view.superview.constraints) {
                    if ([constraint.firstItem isKindOfClass:[UILayoutGuide class]] &&
                        constraint.firstAttribute == NSLayoutAttributeTrailing) {
                        [view.superview removeConstraint:constraint];
                NSLayoutConstraint *trailingConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:view.superview attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:margin];
                trailingConstraint.priority = UILayoutPriorityRequired;
                [view.superview addConstraint:trailingConstraint];
                break;
 

现在看起来问题解决了,但是某一个界面在push一个新界面之后再返回回来之后位置就还原了 
解决方案其实很简单,只要将设置leftItem的方法写在viewWillAppear中即可,这样即可保证约束不会被系统重置。但是,这样的方案,是不是觉得不完美!

现在有一个终极解决方案:

UINavigationBarContentView平铺在导航栏中作为iOS11的各个按钮的父视图,该视图的所有的子视图都会有一个layoutMargins被占用,也就是系统调整的占位,我们只要把这个置空就行了.那样的话该视图下的所有的子视图的空间就会变成我们想要的那样,当然为了保险起见,该视图的父视图也就是bar的layoutMargins也置空,这样 整个bar就会跟一个普通视图一样了 左右的占位约束就不存在了

给UINavigationBar写一个分类:

#import "UINavigationBar+iOS11Spacing.h"
#import <objc/runtime.h>
#define kSpacerWidth kAppAdaptWidth(15)
@implementation UINavigationBar (iOS11Spacing)
+(void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleInstanceMethodWithOriginSel:@selector(layoutSubviews)
                                     swizzledSel:@selector(sx_layoutSubviews)];
-(void)sx_layoutSubviews{
    [self sx_layoutSubviews];
    if (iOS11_OR_LATER && !kSpacerWidth) {//需要调节
        self.layoutMargins = UIEdgeInsetsZero;
        CGFloat space = kSpacerWidth;
        for (UIView *subview in self.subviews) {
            if ([NSStringFromClass(subview.class) containsString:@"ContentView"]) {
                subview.layoutMargins = UIEdgeInsetsMake(0, space, 0, space);//可修正iOS11之后的偏移
                break;
+ (void)swizzleInstanceMethodWithOriginSel:(SEL)oriSel swizzledSel:(SEL)swiSel {
    Method originAddObserverMethod = class_getInstanceMethod(self, oriSel);
    Method swizzledAddObserverMethod = class_getInstanceMethod(self, swiSel);
    [self swizzleMethodWithOriginSel:oriSel oriMethod:originAddObserverMethod swizzledSel:swiSel swizzledMethod:swizzledAddObserverMethod class:self];
+ (void)swizzleMethodWithOriginSel:(SEL)oriSel
                         oriMethod:(Method)oriMethod
                       swizzledSel:(SEL)swizzledSel
                    swizzledMethod:(Method)swizzledMethod
                             class:(Class)cls {
    BOOL didAddMethod = class_addMethod(cls, oriSel, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
        class_replaceMethod(cls, swizzledSel, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
    } else {
        method_exchangeImplementations(oriMethod, swizzledMethod);
 

这样就有一个好处,在原来代码的基础上,判断iOS11,什么都不做,iOS7-iOS11之间版本使用老方法修改间距。

举一个设置LeftBarButtonItem的例子:

#import "UINavigationItem+iOS7Spacing.h"

#import <objc/runtime.h>

#define xSpacerWidth -8

@implementation UINavigationItem (iOS7Spacing)

- (UIBarButtonItem *)spacer

    UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];

    space.width = xSpacerWidth;

    return space;

- (void)mk_setLeftBarButtonItem:(UIBarButtonItem *)leftBarButtonItem

    if (iOS11_OR_LATER) {

        [self mk_setLeftBarButtonItem:leftBarButtonItem];

    } else if (iOS7_OR_LATER) {

        if (leftBarButtonItem && (leftBarButtonItem.customView !=nil || leftBarButtonItem.image !=nil)) {

            [self mk_setLeftBarButtonItem:nil];

            [self mk_setLeftBarButtonItems:@[[self spacer], leftBarButtonItem]];

        } else {

            if (iOS7_OR_LATER) {

                [self mk_setLeftBarButtonItems:nil];

            [self mk_setLeftBarButtonItem:leftBarButtonItem];

    } else {

        [self mk_setLeftBarButtonItem:leftBarButtonItem];

- (void)mk_setLeftBarButtonItems:(NSArray *)leftBarButtonItems

    if (iOS7_OR_LATER && leftBarButtonItems && leftBarButtonItems.count > 0 ) {

        NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:leftBarButtonItems.count + 1];

        [items addObject:[self spacer]];

        [items addObjectsFromArray:leftBarButtonItems];

        [self mk_setLeftBarButtonItems:items];

    } else {

        [self mk_setLeftBarButtonItems:leftBarButtonItems];

- (void)mk_setRightBarButtonItem:(UIBarButtonItem *)rightBarButtonItem

    if (iOS11_OR_LATER) {

        [self mk_setRightBarButtonItem:rightBarButtonItem];

    } else if (iOS7_OR_LATER) {

        if (rightBarButtonItem && (rightBarButtonItem.customView !=nil || rightBarButtonItem.image != nil)) {

            [self mk_setRightBarButtonItem:nil];

            [self mk_setRightBarButtonItems:@[[self spacer], rightBarButtonItem]];

        } else {

            if (iOS7_OR_LATER) {

                [self mk_setRightBarButtonItems:nil];

            [self mk_setRightBarButtonItem:rightBarButtonItem];

    } else {

        [self mk_setRightBarButtonItem:rightBarButtonItem];

- (void)mk_setRightBarButtonItems:(NSArray *)rightBarButtonItems

    if (iOS7_OR_LATER && rightBarButtonItems && rightBarButtonItems.count > 0) {

        NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity:rightBarButtonItems.count + 1];

        [items addObject:[self spacer]];

        [items addObjectsFromArray:rightBarButtonItems];

        [self mk_setRightBarButtonItems:items];

    } else {

        [self mk_setRightBarButtonItems:rightBarButtonItems];

+ (void)mk_swizzle:(SEL)aSelector

    SEL bSelector = NSSelectorFromString([NSString stringWithFormat:@"mk_%@", NSStringFromSelector(aSelector)]);

    Method m1 = class_getInstanceMethod(self, aSelector);

    Method m2 = class_getInstanceMethod(self, bSelector);

    method_exchangeImplementations(m1, m2);

+ (void)load

    [self mk_swizzle:@selector(setLeftBarButtonItem:)];

    [self mk_swizzle:@selector(setLeftBarButtonItems:)];

    [self mk_swizzle:@selector(setRightBarButtonItem:)];

    [self mk_swizzle:@selector(setRightBarButtonItems:)];

参考文章: iOS11 导航栏按钮位置问题的解决

转载于:https://www.cnblogs.com/6duxz/p/7989101.html

UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; negativeSpacer.width = -20;//这个数值可以根据情况自由变化 自定义返回箭头 UIImage *backImage = [[UIImage imageNamed:@"backicon.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; [self.navigationController.navigationBar set...
我也是一个ios的技术人员最近有时间的时候把iOS里的UI组件重新整理了一遍,简单来看一下常用的组件以及它们的实现。其实现在这些组件都可以通过Storyboard很快的生成,只是要向这些组件能够变得生动起来并且赋予它们更具生命力的事件,还是需要一番功夫的。 UIButton 这个就不多说了,对照文档可以更多的去学习。所以在学习这些组件的时候最好的方式还是通过文档,虽然已开始可能有些困难,但是硬着头皮去啃,就一定会有悟道的那一天。建议有问题先去看文档,如果实在不行再去Google啊,Stack Overfl
ios11中,navigationbar的 leftItem 都会多出来一个奇怪的白边,这个白边很不容易修改,从前的设置方法失效了 之前看到有篇文章是修改layoutMargins的方案来做的 连接 这个方案设置虽然简单,但是有一个问题,就是当你push,pop以后,位置就变了,也就是说,layoutMargins就失效了,而且需要遍历,采用ContentView的字符串 另外有一... iOS 11版本由于对于Nav层级结构的改变,导致以前的方法无法达到理想的移动效果,使顶部的按钮完全靠左,或者是靠右. 该修改思路目前对于iOS 11.2的系统部分情况已经无效. 原因是由于从上一个vc返回到当前vc的时候,可能不会触发当前vc的viewDidLayoutSubviews方法,就算触发了此时self.navigationController.navigationBar也是空的,导致取不到需要修改约束的view,所以造成了约束无法修改为0,达不到现有效果. 临时修改方法
我们发现,在设置navigationItem的leftBarButtonItem或rightBarButtonItem时,用CustomView初始化UIBarButtonItem,不论怎么设置CustomView的frame,添加到导航条上之后总是和屏幕边界有一定的间距(5pix),如何自由调整这个间距呢? 初始化一个用于控制间距的UIBarButtonItem实例negativeSpacer,并设置negativeSpacer的width属性的值,设为-5的时候,正好可以使按钮与屏幕边界值为0,以rig
今天项目有个需求,要求右侧导行栏添加三个控钮,想到用rightBarButtonItems包三个rightBarButtonItem, 结果控件之间的间距没法调整,试过很多种方法都不行,结合网上一些思路及自己拓展,最终解决方案是在两 个控件中间加个占位的Item.代码如下。