iOS11自定義大標題的實現(xiàn)方案

自從iOS 11發(fā)布后,iPhone的界面風格發(fā)生了很大變化,主要在3個設計大方向進行了改進:1、大標題導航欄 ?2、提示文字設計的層次 3、增強對比效果

于是,APP的設計師們開始瘋狂地抄襲,硬要在自己的App設計各種大標題風格,可是到iOS程序猿的手上就頭疼了,除了單純地給導航欄設置大標題,蘋果沒有開放更多API,公有的API無法滿足設計師們的需求。怎么辦,設計師卻拿著App Store的界面給程序猿說,“為啥別的應用能做,你們卻做不出來,是不是技術不過關?”,程序猿表示很無語。

今天給大家分享自定義大標題的一個解決方案,附源碼:https://github.com/linchgit/LargeTitleStyle

iOS 11系統(tǒng)自帶大標題顯示的條件:

1、在UIViewController下,使用[self.navigationController.navigationBar setPrefersLargeTitles:YES];開啟大標題;

2、UIView的第一個SubView是UIScrollView或者UIScrollView的子類;

滿足以上條件后,通過滑動列表,可以縮放大標題。

利用iOS 11的大標題特性,可以通過監(jiān)聽UINavgationBar的高度,自定義自己的大標題。實現(xiàn)方案:

1、繼承UINavgationBar,創(chuàng)建一個可監(jiān)聽導航欄高度的LTSNavigationBar,關鍵代碼:

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

}

2、新建UIViewController的Category類:UIViewController (LTS),接收導航欄高度的變化:

//導航欄高度發(fā)生變化時調(diào)用,如果視圖控制器需要開啟自定義大標題時,實現(xiàn)下面的方法
@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, *)){
? ? ? ? //與系統(tǒng)方法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];
}
//設置高度變化監(jiān)聽
-(void)setupLTS
{
? ? [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(navigationBarHeightChange:) name:KEY_UINavigationBar_Height_Changed object:nil];
}

//監(jiān)聽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方法,實現(xiàn)監(jiān)聽代碼的入侵。

3、創(chuàng)建大標題容器視圖:LTSCustomContainerView,在導航欄高度變化時,回調(diào)方法刷新容器視圖的高度:

//自定義大標題協(xié)議
@protocol LTSViewProtocol//父界面發(fā)生變化時調(diào)用

-(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、創(chuàng)建自定義視圖類LTSMenuView,實現(xiàn)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的子類實現(xiàn)大標題風格:

@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=@"";,詳細源碼,請看源碼:

最終效果圖:

原創(chuàng)文章,轉載請注明出處。write by linch

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 前言 最近忙完項目比較閑,想寫一篇博客來分享一些自學iOS的心得體會,希望對迷茫的你有所幫助。博主非科班出身,一些...
    GitHubPorter閱讀 1,582評論 9 5
  • 直方圖主要用在數(shù)據(jù)圖表,作為對比數(shù)據(jù),用柱體高度的高低,形象直觀地表達出來,往往與折線圖配合使用,而折線圖便于從眾...
    理想是試閱讀 1,087評論 0 0
  • 風把自己藏起來 葉子不再喧嘩 蜷曲著 同誰在捉迷藏 鳥的冀翅穿透烈日 深耕天空 卻把愛昧深埋在瘋狂的草垛里 交配在...
    安若胡閱讀 294評論 0 3
  • Promise簡介 Promise是一種解決異步回調(diào)問題而發(fā)展的編程語言特性,在各種語法中都有支持,比如在Java...
    李卓立閱讀 860評論 0 2
  • 戳上面的藍字關注我們哦! 《墨麒雕刻藝術》以匠之心,造木之藝 弘揚雕刻文化︱傳承工匠精神 明楊慎《升庵外集》有云:...
    墨麒雕刻藝術閱讀 932評論 0 0

友情鏈接更多精彩內(nèi)容