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

自从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=@"";,详细源码,请看源码:

最终效果图:

iOS11自定义大标题的实现方案_第1张图片
iOS11自定义大标题的实现方案_第2张图片
iOS11自定义大标题的实现方案_第3张图片

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

你可能感兴趣的:(iOS11自定义大标题的实现方案)