前言
至从一位好友去了“see”工作之后,我就一直对see这个软件持有比较大的关注,对于它的一些UI效果也会忍不住去尝试实现,这次就尝试实现个人中心。
结构分析
通过基本的交互可以将结构分成4个部分
- 导航条
- 头部
- tabbar部分
- 列表内容部分
需求分析
- 整个页面都是可以被拖动的,包括头部
- 点击tabbar部分实现切换
- tabbar可以跟随滚动,但不超出导航条
- 导航条实现透明度变化效果
整个项目的结构基本是从“tabbar可以跟随滚动,但不超出导航条”这个需求出发去构建的。根据这个需求,我们可以想到当下面的tableview滑动时,头部的高度不断变小直到0,这个根据约束的条件tabbar就可以停在导航条下面了。
为tableview设置额外滚动区域,为了可以穿透头部
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"viewDidLoad");
_lastOffsetY = -(YZHeadViewH + YZTabBarH);
// 设置顶部额外滚动区域
self.tableView.contentInset = UIEdgeInsetsMake(YZHeadViewH + YZTabBarH , 0, 0, 0);
YZTableView *tableView = (YZTableView *)self.tableView;
tableView.tabBar = _tabBar;
}
设置多个tableview,传递头部视图的高度,为了当tableview滑动时可以改变
- (void)setUpChildControlller
{
for (YZPersonTableViewController *personChildVc in self.childViewControllers) {
// 传递tabBar,用来判断点击了哪个按钮
personChildVc.tabBar = _tabBar;
// 传递高度约束,用来移动头部视图
personChildVc.headHCons = _headViewCons;
// 传递标题控件,设置文字透明
personChildVc.titleLabel = _titleLabel;
}
}
tableview滑动的代理方法
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// 获取当前偏移量
CGFloat offsetY = scrollView.contentOffset.y;
// 获取偏移量差值
CGFloat delta = offsetY - _lastOffsetY;
CGFloat headH = YZHeadViewH - delta;
if (headH < YZHeadViewMinH) {
headH = YZHeadViewMinH;
}
_headHCons.constant = headH;
// 计算透明度,刚好拖动200 - 64 136,透明度为1
CGFloat alpha = delta / (YZHeadViewH - YZHeadViewMinH);
// 获取透明颜色
UIColor *alphaColor = [UIColor colorWithWhite:0 alpha:alpha];
[_titleLabel setTextColor:alphaColor];
// 设置导航条背景图片
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageWithColor:[UIColor colorWithWhite:1 alpha:alpha]] forBarMetrics:UIBarMetricsDefault];
}
更多细节
当头部的UserinteractionEnabled 为 no时,响应者链条就会把响应传递到tableview处,这样头部也可以实现滑动。
为了让这个框架具有良好的扩展性,即我不需要手动告诉框架有多少个tableview,它会根据childcontroller的数量自动创建tabbar中的按钮数,并且能准确实现监听。这时用touchbegin监听点击,通知方法实现传递事件就可以有很良好扩展性。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint curP = [touch locationInView:self];
for (UIView *tabBarChildView in _tabBar.subviews) {
CGPoint childP = [self convertPoint:curP toView:tabBarChildView];
if ([tabBarChildView pointInside:childP withEvent:event]) {
// 点击了按钮
// 通知个人控制器切换内容视图
[[NSNotificationCenter defaultCenter] postNotificationName:YZClickBtnNote object:nil userInfo:@{YZClickBtnObjcKey: tabBarChildView}];
// 处理完事件,结束当前事件处理
return;
}
}
// 如果没有处理事件,就调用系统自带的处理方式
[super touchesBegan:touches withEvent:event];
}
在代码结构中,具体实现的类可以在viewdidload方法中为那些图片啊,子控制器初始化什么,而在父类willdidappear方法中就可以对这些可以赋值的变量进行处理,这样代码的风格就可以得到良好维护。
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// 即将显示的时候做一次初始化操作
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self setUpNav];
// 设置子控制器
[self setUpChildControlller];
// 设置tabBar
[self setUpTabBar];
});
}
存在无法解决的问题
无论在see中还是在我这个框架中都有一个问题,当a列表被滑动了100,切换到b列表滑动200,在切换到a列表发现无端端跟着也被滑动了200。这是这种UI结构所带来无法解决的问题。