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