参考资料: iOS 高仿支付宝 9.x 版本首页
但是我跟参考资料中作者关于整个页面的设计逻辑不一致, 但是实现的效果一样, 也算是多一种实现方式, 希望跟大家一起共同学习.
先上效果
整个界面的结构图
关于整个页面布局逻辑, 从上往下说:
1.顶部: "类导航栏", 因为其并不是导航栏, 只是一个跟导航栏尺寸一致的View, 所以进入这个页面的时候需要将自带的导航栏隐藏掉. 此部分共三层, 从底层往上依次是: 底色View(蓝色背景色), 扫一扫View(刚开始隐藏, 向上滚动后逐渐显示), 搜索框View.
2. tableView, 紧接着就是一个带有tableHeaderView的tableView, 其tableHeaderView高度与添加到tableView上显示主要功能的view的尺寸一致.
注意细节: 在整个向上缓慢滑动的过程中, 会发现顶部图标的隐藏/显示变化过程分为三个部分: 1. 搜索框view快速消失, 2.搜索框view后紧接着另一个"类导航"view(扫一扫 + 付钱 + 搜索 + "+")逐渐显示, 3. 下面的四个主要功能模块逐渐消失, 当滑动到导航栏位置时, 彻底消失. 并且, 当四个主要功能模块完全消失的时候, 此时手离开屏幕, 其会自动向上滚动到四个主要功能模块完全消失的位置.
关键代码:
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let offentY = scrollView.contentOffset.y
// 此处之所以以24为判断依据, 主要是因为要与下面设置alpha保持一致, 1 - 24 / 95 约等于 0.75
if offentY > 0 && offentY < 24 {
tableView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
} else if offentY >= 24 && offentY < 95 {
tableView.setContentOffset(CGPoint(x: 0, y: 95), animated: true)
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offsetY = scrollView.contentOffset.y
if offsetY <= 0 {
headerView.frame = CGRect(x: 0, y: offsetY, width: SCREEN_WIDTH, height: 305)
childVC?.changeAlpha(alpha: 1)
coverNavView.alpha = 0
mainNavView.alpha = 1
} else if offsetY > 0 && offsetY < 95 {
//处理透明度
let alpha = (1 - offsetY / 95) > 0 ? (1 - offsetY / 95) : 0
childVC?.changeAlpha(alpha: alpha / 3)
if alpha > 0.9 { // 为了原显示内容可以快速消失, 增加后面有更长的显示时长
coverNavView.alpha = 0
mainNavView.alpha = alpha / 5
} else {
mainNavView.alpha = 0
coverNavView.alpha = 1 - alpha
if alpha <= 0.75 {
childVC?.changeAlpha(alpha: 0)
}
}
}
}
使用MJRefresh时注意一个点
tableView.mj_header = MJRefreshNormalHeader { [weak self] in
guard let weak = self else {return}
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1), execute: {
// Put your code which should be executed with a delay here
weak.tableView.mj_header.endRefreshing()
weak.dataSorceNumber = 10
weak.tableView.reloadData()
})
}
//重点: 由于顶部有305高度的tableHeaderView, 并且在设置的时候, 将tableView的scrollIndicatorInsets的top值设置为305, 所以需要忽略这部分高度, 才可以正常显示下拉刷新动画.
tableView.mj_header.ignoredScrollViewContentInsetTop = -305
tableView.mj_footer = MJRefreshAutoNormalFooter { [weak self] in
guard let weak = self else {return}
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2), execute: {
weak.tableView.mj_footer.endRefreshing()
weak.dataSorceNumber += 10
weak.tableView.reloadData()
})
}