zoukankan      html  css  js  c++  java
  • iOS11UINavigationBar的item左右间距调整

    相信很多同学都知道在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;
    
    @end
    @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 导航栏按钮位置问题的解决

  • 相关阅读:
    Centos安装步骤
    Charles抓包工具的使用
    Charles抓包问题
    关于Windows系统下端口被占用的问题和task命令
    Selenium3+python自动化016-Selenium Grid
    Selenium3+python自动化016-多线程
    JDBC使用案例
    JDBC基础和使用
    文件下载案例
    ServletContext
  • 原文地址:https://www.cnblogs.com/6duxz/p/7989101.html
Copyright © 2011-2022 走看看