实现导航栏动态变化交互效果的套路

 

| 导语 套路,在武术中是指一连串含有技击和攻防含义的动作组合,在本文中,指的就是一套实现特定功能的代码组合。

一、交互效果

如图所示,导航栏随列表滑动而动态改变背景色和标题显隐,这种交互设计比较常见,那该怎么去实现这样的交互效果呢?想必大体的思路大家都能想得到,首先是让VC的view的布局从屏幕上边缘开始,在UITableView滚动回调方法中根据偏移量改变导航栏的背景透明度。思路简单,但是具体到实现却是有不少技术细节要注意。下面我将分享我所在iOS项目实现导航栏动态变化交互效果的套路。

二、代码套路

第一步,如何让view的布局从屏幕顶部边缘开始,到TabBar的顶部截止?

iOS 7 之后,UINavigationBar和UITabBar默认都变成了半透明,系统也调整了view的布局,默认使用全屏布局(full-screen layout),即铺满整个屏幕。为了让开发者可以自主控制view的布局,UIViewController新增了edgesForExtendedLayout属性,通过设置此属性,你可以指定view的边(上、下、左、右)延伸到整屏幕。

@property(nonatomic,assign) UIRectEdge edgesForExtendedLayout NS_AVAILABLE_IOS(7_0);

typedef NS_OPTIONS(NSUInteger, UIRectEdge) {

UIRectEdgeNone = 0,

UIRectEdgeTop = 1 << 0,

UIRectEdgeLeft = 1 << 1,

UIRectEdgeBottom = 1 << 2,

UIRectEdgeRight = 1 << 3,

UIRectEdgeAll = UIRectEdgeTop | UIRectEdgeLeft | UIRectEdgeBottom | UIRectEdgeRight

} NS_ENUM_AVAILABLE_IOS(7_0);

刚刚说到UINavigationBar和UITabBar默认是半透明,但如果是不透明,你会发现设置edgesForExtendedLayout没有效果,如何在不透明的情况下也能延伸到整屏幕呢。UIViewController同时新增了一个extendedLayoutIncludesOpaqueBarssh属性,默认是NO,如果设置为YES,则在Bar不透明的情况下,edgesForExtendedLayout也能生效。所以套路的第一步是:

self.edgesForExtendedLayout = UIRectEdgeTop;
self.extendedLayoutIncludesOpaqueBars = YES;

第二步,如何动态改变导航栏的透明度和标题?

UINavigationBar的translucent、barTintColor、backgroundImage三者共同决定了UINavigationBar的背景效果,而translucent在中间起非常关键的作用,它决定了系统如何使用barTintColor和backgroundImage去设置UINavigationBar的背景效果。如果不了解其中细节的,强烈推荐我的另一篇文章《UINavigationBar的translucent、barTintColor、backgroundImage》下面直接分享动态改变导航栏的透明度和标题的最佳实践写法:

1. 不主动去设置UINavigationBar的translucent为YES,因为如果主动设置为YES,则导航栏无法做到完全不透明;

2. 通过setBackgroundImage:forBarMetrics: 的方式来设置导航栏背景,以达到控制背景效果的目的,为什么要怎样操作,在我刚刚推荐的文章里有详细的分析;

3. backgroundImage通过颜色生成,通过改变颜色alpha值,来动态控制图片的透明度,最终控制导航栏透明度;

4. 如果导航栏已经是不透明的背景了,就不需要再重复设置,减少颜色转图片的资源消耗,可以通过translucent的值是否为NO来判断是否已经设为不透明背景;

5. 通过设置self.navigationItem.titlel来控制标题的变化,而不是self.title, 因为self.title会同时改变tabBar是的title;

所以套路的第二步是:

// 根据scrollView的偏移量来动态计算alpha值
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGPoint offset = scrollView.contentOffset;
    CGFloat alpha = 1.0;
    if (offset.y <= 0) {
        alpha = 0;
    } else if (offset.y < TOP_SAFE_OFFSET) {
        alpha = 1.0 - (TOP_SAFE_OFFSET - offset.y)/TOP_SAFE_OFFSET;
    } else {
        alpha = 1.0;
    }
    if (!(alpha == 1 && !self.navigationController.navigationBar.translucent)) {
        //如果导航栏背景已经是不透明,那就不再需要重复设置了,减少颜色转图片的开销
        [self.navigationController.navigationBar ig_setBackgroundImageByColor:HEXACOLOR(IGAME_NAVIBAR_COLOUR_VALUE, alpha) forBarMetrics:UIBarMetricsDefault];
    }
    self.navigationItem.title = alpha > 0.2 ? @"赛事":@"";
}

项目中导航栏一般是纯色背景,所以提供一个分类方法,快速通过颜色来设置背景图

@interface UINavigationBar (IGAppearance)

- (void)ig_setBackgroundImageByColor:(UIColor *)imageColor forBarMetrics:(UIBarMetrics)barMetrics;

@end

通过颜色来创建图片的方法

@interface UIImage (IGUIKit)

+ (nullable UIImage *)imageWithColor:(UIColor *)color;

@end

第三步,如何让tableView不自动偏移?

如图,当我们设置了view从顶部边缘开始布局之后,发现tableView会自动偏移,使内容从导航栏底部开始,为什么会发生偏移呢? 因为tableview从屏幕顶部开始布局,为了不让导航栏遮住tableView的内容,系统默认会自动调整tableview的contentInset,我们可以打印一下tableView,可以看到一个adjustedContentInset: {64, 0, 0, 0}。adjustedContentInset是iOS 11之后新增的属性。如果是iOS 11之前,那就是调整contentInset。

; layer = ;

contentOffset: {0, -62}; contentSize: {375, 2068}; adjustedContentInset: {64, 0, 0, 0}>

(lldb) po NSStringFromUIEdgeInsets(self.tableView.contentInset)

{64, 0, 0, 0}

那如何禁止系统自动调整tableView的contentInset呢?下面就是我们套路的最后一步:

if (@available(iOS 11.0, *)) {
    self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} else {
    self.automaticallyAdjustsScrollViewInsets = NO;
}

 

通过以上三个步骤,我们便能轻轻松松实现导航栏动态变化交互效果。感谢大家的阅读,欢迎大家提出问题和建议!

你可能感兴趣的:(iOS开发)