自從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