美文网首页自专题
iOS11自定义大标题的实现方案

iOS11自定义大标题的实现方案

作者: 程序源代码 | 来源:发表于2018-02-01 17:25 被阅读879次

    自从iOS 11发布后,iPhone的界面风格发生了很大变化,主要在3个设计大方向进行了改进:1、大标题导航栏  2、提示文字设计的层次 3、增强对比效果

    于是,APP的设计师们开始疯狂地抄袭,硬要在自己的App设计各种大标题风格,可是到iOS程序猿的手上就头疼了,除了单纯地给导航栏设置大标题,苹果没有开放更多API,公有的API无法满足设计师们的需求。怎么办,设计师却拿着App Store的界面给程序猿说,“为啥别的应用能做,你们却做不出来,是不是技术不过关?”,程序猿表示很无语。

    今天给大家分享自定义大标题的一个解决方案,附源码:https://github.com/linchgit/LargeTitleStyle

    iOS 11系统自带大标题显示的条件:

    1、在UIViewController下,使用[self.navigationController.navigationBar setPrefersLargeTitles:YES];开启大标题;

    2、UIView的第一个SubView是UIScrollView或者UIScrollView的子类;

    满足以上条件后,通过滑动列表,可以缩放大标题。

    利用iOS 11的大标题特性,可以通过监听UINavgationBar的高度,自定义自己的大标题。实现方案:

    1、继承UINavgationBar,创建一个可监听导航栏高度的LTSNavigationBar,关键代码:

    -(void)layoutSubviews
    {
        [super layoutSubviews];
        [[NSNotificationCenter defaultCenter] postNotificationName:KEY_UINavigationBar_Height_Changed object:self userInfo:nil];

    }

    2、新建UIViewController的Category类:UIViewController (LTS),接收导航栏高度的变化:

    //导航栏高度发生变化时调用,如果视图控制器需要开启自定义大标题时,实现下面的方法
    @protocol LTSNavigationChangedDelegate
    @optional
    -(void)LTSNavigationChanged:(LTSNavigationBar *)navigationBar;
    @end
    @interface UIViewController (LTS)
    @end

    #import "UIViewController+LTS.h"
    @implementation UIViewController (LTS)
    //交换方法
    + (void)load
    {
        if(@available(iOS 11.0, *)){
            //与系统方法viewDidLoad交换为BLSViewDidLoad方法
            [UIViewController swizzleMethod:@selector(viewDidLoad) withMethod:@selector(BLSViewDidLoad)];
        }
    }
    -(void)BLSViewDidLoad
    {
        [self BLSViewDidLoad];
        if (self.navigationController && self.navigationController.navigationBar.prefersLargeTitles == YES) {
            self.title = @"";
        }
        [self setupLTS];
    }
    //设置高度变化监听
    -(void)setupLTS
    {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(navigationBarHeightChange:) name:KEY_UINavigationBar_Height_Changed object:nil];
    }

    //监听UINavigationBar高度变化

    -(void)navigationBarHeightChange:(NSNotification *)notification
    {
        LTSNavigationBar *notifyNavigationBar = notification.object;
        if ([notifyNavigationBar isKindOfClass:[LTSNavigationBar class]]) {
           [self navigationBarUpdated:notifyNavigationBar];
        }
    }

    //通知UINavigationBar高度变化
    -(void)navigationBarUpdated:(LTSNavigationBar *)notifyNavigationBar{

     if ([self respondsToSelector:@selector(LTSNavigationChanged:)]) {
            [self LTSNavigationChanged:notifyNavigationBar];
        }
    }
    @end

    说明:利用runtime的特性用BLSViewDidLoad置换UIViewController的ViewDidLoad方法,实现监听代码的入侵。

    3、创建大标题容器视图:LTSCustomContainerView,在导航栏高度变化时,回调方法刷新容器视图的高度:

    //自定义大标题协议
    @protocol LTSViewProtocol//父界面发生变化时调用

    -(void)LTSSuperViewUpdated;
    @end
    @interface LTSCustomContainerView : UIView
    @property (nonatomic,strong) UIView* contentView;
    @property (nonatomic,assign) BOOL maxStretchMode;
    //设置最大拉伸模式后,大标题显示最多只能拉伸至96像素(默认大标题的NavigatinBar的高度),相应的UIScrollView也需要限制最大偏移量
    -(instancetype)initWithFrame:(CGRect)frame contentView:(UIView *)contentView;
    //随导航栏变化而更新
    -(void)updateUIWithNavigationBar:(CGFloat)navigationBarHeight scrollView:(UIScrollView *)scrollView;
    @end

    @implementation LTSCustomContainerView-(instancetype)initWithFrame:(CGRect)frame contentView:(UIView *)contentView
    {
      self = [super initWithFrame:frame];
        self.contentView = contentView;
        [self addSubview:self.contentView];
        return self;
    }

    -(void)layoutSubviews
    {
        [super layoutSubviews];
        [self.contentView LTSSuperViewUpdated];
    }

    -(void)updateUIWithNavigationBar:(CGFloat)navigationBarHeight scrollView:(UIScrollView *)scrollView
    {
        CGPoint contentOffset = scrollView.contentOffset;
        if (contentOffset.y>44) {
            CGRect frame = self.frame;
            frame.size.height = navigationBarHeight;
            self.frame = frame;
            [self layoutIfNeeded];
        }else
        {
            CGRect frame = self.frame;
            if (self.maxStretchMode) {
                frame.size.height = MIN(navigationBarHeight, 96);
            }else
            {
                frame.size.height = navigationBarHeight;
            }
            self.frame = frame;        
            [self layoutIfNeeded];
        }
    }
    @end

    4、创建自定义视图类LTSMenuView,实现LTSViewProtocol
    @interface LTSMenuView : UIView@property (nonatomic, weak) id menuDelegate;

    - (instancetype)initViewWithFrame:(CGRect)frame andTextArray:(NSArray *)textArray;

    @end

    LTSMenuView关键代码:

    @interface LTSMenuView()

    @property(nonatomic, strong) NSMutableArray *titleArray;

    @property(nonatomic, assign) CGFloat leftDistance;

    @property(nonatomic, assign) CGFloat curTitleWidth;

    @property(nonatomic, weak) UIButton *selectButton;

    @property(nonatomic, strong) NSMutableArray *lineArray;

    @property(nonatomic, weak) UIView *selectLineView;

    @property(nonatomic,assign) NSInteger curSelectedIndex;

    @property(nonatomic,assign) BOOL isLargeTitleMode;

    @end

    @implementation LTSMenuView

    -(void)LTSSuperViewUpdated{

        CGRect superFrame =self.superview.frame;

        CGFloat superHeight = CGRectGetHeight(superFrame);

        self.isLargeTitleMode = (superFrame.size.height>53)?YES:NO;

        if (self.isLargeTitleMode) {

            CGFloat height = superHeight - 44;

            height = (height<38)?38:height;

            height = MIN(height, 52);

            self.frame = CGRectMake(0, CGRectGetHeight(superFrame)-height, CGRectGetWidth(self.frame), height);

        }else

        {
        self.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), 44);
        }
        [self updateItemView];
    }

    - (instancetype)initViewWithFrame:(CGRect)frame andTextArray:(NSArray *)textArray{

        if(!_titleArray && textArray.count < 1)   {
         return nil;
        }
        self.leftDistance = 0.0;
        self.curTitleWidth = 0.0;
        self = [super initWithFrame:frame];
        if(self)
        {
            for(NSInteger i = 0; i < textArray.count ;i++)
            {
                NSString *titleStr = textArray[i];
                CGSize size = [self getTextSize:titleStr];
                if(size.height != 0 && size.width != 0)
                {
                    UIButton *button = [[UIButton alloc] init];
                    [button setTitle:titleStr forState:UIControlStateNormal];
                    button.tag = i;
                    [button addTarget:self action:@selector(titleButtonClick:) forControlEvents:UIControlEventTouchDown];
                    [self.titleArray addObject:button];

                    UIView *lineView = [[UIView alloc] init];
                   [lineView setBackgroundColor:[UIColor blackColor]];
                    [self.lineArray addObject:lineView];
                    if(i == 0)
                    {
                        [self selctedBtn:button];
                    }
                    else
                    {
                        lineView.hidden = YES;
                    }
                    [self addSubview:button];
                    [self addSubview:lineView];
                }
            }
            [self updateItemView];
        }
        return self;
    }

    //自定义标题分为大标题模式和非大标题模式
    -(void)updateItemView{
        for (NSInteger i =0;i<[self.titleArray count];i++) {
            UIButton *button = self.titleArray[i];
            if(i == 0)
            {
                self.leftDistance = 20.0;
            }
            else
            {
                self.leftDistance = self.curTitleWidth + 12;
            }
            UIFont *font = nil;
            if (button.tag == self.curSelectedIndex) {
                [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
                if (self.isLargeTitleMode) {
                    font = [UIFont boldSystemFontOfSize:34];
                }else
                {
                    font = [UIFont boldSystemFontOfSize:20];
                }
            }else
            {
                [button setTitleColor:RGB(170, 170, 170) forState:UIControlStateNormal];
                if (self.isLargeTitleMode) {
                    font = [UIFont boldSystemFontOfSize:20];
                }else
                {
                    font = [UIFont boldSystemFontOfSize:17];
                }
            }
            CGSize size = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName:font}];
            button.titleLabel.font = font;
            self.curTitleWidth = size.width + self.leftDistance;
            button.frame = CGRectMake(self.leftDistance, 8, size.width, 22);
            UIView *lineView = self.lineArray[i];
            lineView.frame = CGRectMake(self.leftDistance + 2 , size.height + 12, size.width - 2, 2);
            lineView.hidden = self.isLargeTitleMode || button.tag != self.curSelectedIndex;
        }
    }

    @end

    5、最后,在UIViewController的子类实现大标题风格:

    @interface MenuTableViewController : UITableViewController

    @end

    #import "UIViewController+LTS.h"
    #import "LTSCustomContainerView.h"
    #import "LTSMenuView.h"
    @interface MenuTableViewController ()
    @property(nonatomic,strong) LTSCustomContainerView *headerView;
    @end

    @implementation MenuTableViewController
    -(LTSCustomContainerView *)headerView
    {
        if(!_headerView)
        {
            CGRect frame = self.navigationController.navigationBar.bounds;
            LTSMenuView *titleView = [[LTSMenuView alloc] initViewWithFrame:frame andTextArray:@[@"时刻", @"人物", @"地点", @"动画影集"]];
            _headerView = [[LTSCustomContainerView alloc] initWithFrame:frame contentView:titleView];
            [_headerView setMaxStretchMode:NO];
        }
        return _headerView;

    }

    - (void)viewDidLoad {
        [super viewDidLoad];
        self.navigationItem.title=@"";
        self.tabBarItem.title = @"菜单大标题";
         if(@available(iOS 11.0, *)){
             [self.navigationController.navigationBar setPrefersLargeTitles:YES];
         }
        [self.navigationController.navigationBar addSubview:self.headerView];
    }
    #pragma mark LTSNavigationChangedDelegate
    -(void)LTSNavigationChanged:(UINavigationBar *)navigationBar
    {
        [self.headerView updateUIWithNavigationBar:navigationBar.frame.size.height scrollView:self.tableView];
    }
    #pragma mark - Table view data source

    ....

    @end

    MenuTableViewController使用iOS 11大标题特性,默认把self.title作为大标题,因此需要代码屏蔽掉title:self.navigationItem.title=@"";,详细源码,请看源码:

    最终效果图:

    原创文章,转载请注明出处。write by linch

    相关文章

      网友评论

      • 安仔_6e72:跳转到其他控制器,怎么取消大标题效果
        程序源代码:跳转到其他控制器时先隐藏LTSCustomContainerView,并且去掉大标题模式:[self.navigationController.navigationBar setPrefersLargeTitles:NO]
      • 码渣:在ios11以下系统就无法实现大标题导航栏

      本文标题:iOS11自定义大标题的实现方案

      本文链接:https://www.haomeiwen.com/subject/nyulzxtx.html