支付宝首页仿写记录

花了两天时间写了一个支付宝的首页。
首先关于最终的首页架构:

  1. 整体使用的一个UITableView和2个HeadView(MenuGroudView(包扣HomeNavi、AdView、MiniNavi)、GridView)来布局。未采用网上2个ScrollView的方式 ;
  2. 单独写了一个VC_A来处理UITableView的滑动事件,另外写一个VC_A1继承VC_A来处理UITableView的DataSource和delegate。目的保证功能独立;
  3. 2个HeadView直接添加到UITableView上,未设置为tableView.tableHeaderView,保证这2个HeadView的位置可控。未采用网上的添加到VC.view上,目的是:HeadView已超出UITableView上边距,手势无法接受。
  4. HeadView在tableView上的显示空间有tableView.contentInset预留出来。
  5. 对iPhone X的适配采用对UITableView进行更多的偏移量来适配;
  6. 对导航栏的处理,选择隐藏掉,对系统对tableView的默认偏移特性也进行关闭;
  7. 对页面上下滑动处理采用KVO监听,参考,在监听中控制MenuGroudView的高度以及2个HeadView的位置。

最终效果:

支付宝首页仿写记录_第1张图片
6s
iPhone X

核心代码:

VC部分:

//
//  JYALPHomeSimulateVC.m
//  MYCCBRegisterSubAccount
//
//  Created by  JackYing on 2018/7/31.
//  Copyright © 2018年 JackYing. All rights reserved.
//

#import "JYALPHomeSimulateVC.h"

#import "JYHGridView.h"
#import "JYHMenuGroudView.h"

@interface JYALPHomeSimulateVC () {
    
    /// 表格最顶部到顶部的间隙
    CGFloat _topOffset;
    /// 表格顶部内容顶部到表格顶部的间隙
    CGFloat _topMargin;
    /// 快捷菜单,会被隐藏到导航栏
    JYHMenuGroudView *groudView;
    /// 常用菜单,直接跟随tableview进行滑动
    JYHGridView *gridView;
}

@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) UIScrollView *scrollView;

@end

@implementation JYALPHomeSimulateVC
@synthesize tableView = _tableView;

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Do any additional setup after loading the view.
    self.title = @"支付宝首页";
    [self.navigationController setNavigationBarHidden:YES animated:NO];
    
    // 关闭系统默认偏移
    if (@available(iOS 11.0, *)) {
        self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    } else {
        self.automaticallyAdjustsScrollViewInsets = NO;
    }
    
    [self.tableView addObserver:self forKeyPath:@"contentOffset" options:(NSKeyValueObservingOptionNew) context:nil];
    
    [self addHeadSubViews];
}

- (void)dealloc {
    [self.tableView removeObserver:self forKeyPath:@"contentOffset"];
}

- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:(UITableViewStyleGrouped)];
        _tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
        _tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
        _tableView.pagingEnabled = YES;
        
        _topMargin = 16;
        // 对iPhone X进行适配
        _topOffset = 300 + kJYALPHomeStatueHeight;
        _tableView.contentInset = UIEdgeInsetsMake(_topOffset + _topMargin, 0, 0, 0); // +16目的是顶部到菜单底部有空隙
        _tableView.scrollIndicatorInsets = _tableView.contentInset;
    }
    return _tableView;
}

- (void)addHeadSubViews {
    
    // 直接将gridView、groudView放在table上可避免上半部分手势的处理,
    // 不需要控制gridView.frame
    gridView = [[JYHGridView alloc] init];
    //gridView.backgroundColor = [UIColor redColor];
    [self.tableView addSubview:gridView];

    groudView = [[JYHMenuGroudView alloc] init];
    groudView.delegate = self;
    [self.tableView addSubview:groudView]; // groudView在上层显示,形成groudView.navi在最上层视觉  
    [self layoutHeadSubViews];
}

/// 原始状态
- (void)layoutHeadSubViews {
    
    CGFloat y = -(_topOffset + _topMargin);
    CGFloat height = _topOffset / 2;
    groudView.frame = CGRectMake(0, y, CGRectGetWidth(self.tableView.frame), height);
    gridView.frame = CGRectMake(0, CGRectGetMaxY(groudView.frame), CGRectGetWidth(self.tableView.frame), height);
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    
    CGFloat offsetY = self.tableView.contentOffset.y;
    // 恢复初始位置
    if (offsetY == -(_topOffset + _topMargin)) {
        
        [self layoutHeadSubViews];
        [groudView setSubViewAlpha:1];
        groudView.adView.hidden = NO;
    }
    else if (offsetY < -(_topOffset + _topMargin)) {// 向下滑动
        
        [groudView setSubViewAlpha:1];

        CGRect groudViewFrameNew = groudView.frame;
        groudViewFrameNew.size.height = _topOffset/2;
        groudViewFrameNew.origin.y = offsetY; // table偏移多少就相向移动多少
        groudView.frame = groudViewFrameNew;
        
        CGRect gridViewFrameNew = gridView.frame;
        gridViewFrameNew.origin.y = CGRectGetMaxY(groudViewFrameNew);
        gridView.frame = gridViewFrameNew;
    }
    else { // 向上滑动
        // 直接将gridView、groudView放在table上不需要控制gridView.frame
        CGFloat groudViewHeight = (ABS(offsetY) - _topOffset/2 - _topMargin);
        CGRect groudViewFrameNew = groudView.frame;
        if (groudViewHeight >= 64 && groudViewHeight <= _topOffset / 2) { // 减少视图面积
            groudViewFrameNew.size.height = groudViewHeight;
        }
    
        groudViewFrameNew.origin.y = offsetY; // table偏移多少就相向移动多少
        groudView.frame = groudViewFrameNew;
        
        // 控制alpha值以及隐藏显示
        CGFloat alpha = (-offsetY / (_topOffset + _topMargin));
        [groudView setSubViewAlpha:alpha];
        
    }
}

- (void)menuView:(UIView *)menuView didSeletedMenu:(id)menu {
    NSAssert(NO, @"叶子子类实现!");
}

@end

设置alpha值及显示隐藏:

- (void)setSubViewAlpha:(CGFloat)alpha {
    //NSLog(@"1. ---->%lf", alpha);
    if (alpha == 1) { // 回到原始位置
        self.adView.alpha = self.homeNavi.alpha = 1;
        self.adView.hidden = self.homeNavi.hidden = NO;
        self.miniMenu.hidden = YES;
    } else {
        
        if (alpha > 0.8) {
            self.adView.hidden = self.homeNavi.hidden = NO;
        }
        if (alpha < 0.95) {
            self.miniMenu.hidden = NO;
        }
        
        alpha = alpha - (1 - alpha) * 3; // 加速减小
        //NSLog(@"adView: ---->%lf", alpha);
        self.adView.alpha = alpha;
        
        alpha -= alpha * 0.5;
        //NSLog(@"homeNavi: ---->%lf", alpha);
        self.homeNavi.alpha = alpha; // homeNavi比adView先消失
        
        alpha = (1 - alpha + alpha * 0.2); // 加速增大
        //NSLog(@"miniMenu: ---->%lf", alpha);
        self.miniMenu.alpha = alpha;
        
        if (alpha > 0.9) {
            alpha = (alpha + alpha * 0.1);
            alpha = alpha < 1 ? alpha : 1;
            //NSLog(@"2. ---->%lf", alpha);
            self.adView.hidden = self.homeNavi.hidden = YES;
            self.miniMenu.hidden = NO;
            self.miniMenu.alpha = alpha;
        }
    }
}

对状态栏进行适配:

CGFloat naviMaxY = 44 + kJYALPHomeStatueHeight;
    self.homeNavi.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), naviMaxY);
    self.adView.frame = CGRectMake(0, naviMaxY, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame) - naviMaxY);
    self.miniMenu.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), naviMaxY);

趟过的坑:

  1. miniNavi和AdView的alpha值和显示隐藏的控制;
  2. 计算gridView和groudView在滑动过程中相对于TableView的偏移量;
  3. iPhone X的适配在VC中将statueBar高度进行预留,在groudView中控制各子视图的位置;
  4. 在滑动向下过程中到达下半部分时,groudView、gridView的高度应该是固定的,不需要在进行计算;
    a. 所以处理这里时错以为是因为runloop的mode==tracking时,frame进行计算,但不对UI进行更新,等track转为default时在更新,进而有延迟感。查看offsetY的具体值后发现,在sc进行快速tracking时offsetY的变化跨度很大,所以在计算groudViewHeight时产生的跨度也很大,导致最终tracking结束时直接将groudView设置为初始值而产生的闪屏现象:如从143-->150,有跳跃感。
  5. 关于偏移,因为tableView向上滑动时offset在增大,而groudView.y也需要从最初的-topOffset到到0再到正数,一直保持到tableview的顶端,所以, table偏移多少就相向移动多少,即groudView.y = offsetY。
  6. 为了避免处理手势,所以将2个HeadView直接添加到UITableView上,方便在HeadView上滑动时,tableview也会相应正常滑动,而未将其设置为tableHeaderView,是保证这2个HeadView的位置方便控制。

你可能感兴趣的:(支付宝首页仿写记录)