仿半糖首页效果 - JSDBanTangHomeDemo

2016-12-22      211      Objective-C
项目简介 前往项目首页

真正的仿半糖首页效果,半糖首页核心技术解析。

实现上滑的的效果

这一步是首页效果的基础,实现这一步后,才有继续其它步的必要,这里面难度不是很大,关键是要想到一个好方法不容易。下面就具体讲讲是如何实现的。

有一点可以确定的是,使用的肯定是KVO的做法。通过监听contentOffset的变化来进行相应的处理。但是具体怎么做,怎么来划分层次,真的是一个让人脑壳痛的问题。

怎么下手呢,开始真的毫无思绪,然后想到了一个利器,Reveal。不管别的,先用Reveal看看图层结构再说。关于Reveal的使用,在我的另外一篇文章里面有。使用Reveal查看任意iOS App的图层结构。通过图层查看后,下面是一个ScrollView,上面是几个TableView。于是这个立刻把我带入了坑,很多网上的Demo都是这样尝试,把TableView放到ScrollView上面,然后对它们的contentOffset都添加监听。通过判断偏移量来禁用TableView或是Scrollew的手势。经过无数次的尝试最后还是放弃了,手势冲突这个问题不可能这样很好的解决。

然后我搜了资料,有人说可以使用contentInset这个属性,这是用来设定ScrollView及其子类的内容显示区域,通过改变这个属性的值,达到类似的滑动的效果。也就是在KVO的实现方法里面不断的改变contentInset的值,然后模拟上推的效果。没有试过这个属性的可以尝试一下。我使用之后,出现的问题就是卡顿,特别卡,而且很难控制值,这就造成了完全没有流畅性可言。间接说明了使用这个根本没法达到这个效果。

然后我实在是想不到什么好办法,打算用UISwipGesturer,不过想想这就算了吧。太愚蠢了,而且也会很麻烦。像我这样懒的,总想少写几行代码。怎么办呢,再想想。有一天在地铁上拿出半糖的APP来研究,突然灵光一闪,想到了。因为既然这么流畅,那一定是使用了原生的UITableView,然后再使用scrollIndicatorInsets这个属性就可以伪装出tableView是从下面开始的效果,然后我的tableView从坐标(0,0)的位置开始。上面添加一个空白的View把内容往下面撑即可实现类似的效果。如图(为了便于看清楚布局,我给每个视图留了一个边距)


能想到这一步其实完成了很大的工作了。接下来就是给上面的搜索框和轮播页面添加坐标变化的事件了。

头部三个View的坐标改变

给TableView添加监听,然后在如下方法里面根据contentOffset的值改变轮播和分类选择控价的坐标。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    UITableView *tableView = (UITableView *)object;

    if (![keyPath isEqualToString:@"contentOffset"]) {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        return;
    }

    CGFloat tableViewoffsetY = tableView.contentOffset.y;
    self.lastTableViewOffsetY = tableViewoffsetY;

    if ( tableViewoffsetY>=0 && tableViewoffsetY<=136) {
        self.segmentScrollView.frame = CGRectMake(0, 200-tableViewoffsetY, SCREEN_WIDTH, 40);
        self.cycleScrollView.frame = CGRectMake(0, 0-tableViewoffsetY, SCREEN_WIDTH, 200); 
    }else if( tableViewoffsetY < 0){
        self.segmentScrollView.frame = CGRectMake(0, 200, SCREEN_WIDTH, 40);
        self.cycleScrollView.frame = CGRectMake(0, 0, SCREEN_WIDTH, 200);

    }else if (tableViewoffsetY > 136){
        self.segmentScrollView.frame = CGRectMake(0, 64, SCREEN_WIDTH, 40);
        self.cycleScrollView.frame = CGRectMake(0, -136, SCREEN_WIDTH, 200);
    }
}


我们需要添加一个坐标的限制,因为偏移量有时候会无限大或者是无限小。而我们的轮播和分类选择器的区间是固定不变的。所以需要找对坐标进行限制,一旦偏移量超过了这个坐标就不进行改变,而是保持固定的值不变。

为了模块的划分清晰一些,我把上面的搜素框单独的划分到了JQHeaderView里面。所以需要把外面的tableView传到里面去。然后在里面同样进行了监听然后事件处理。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{

    if (![keyPath isEqualToString:@"contentOffset"]) {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        return;
    }
    UITableView *tableView = (UITableView *)object;
    CGFloat tableViewoffsetY = tableView.contentOffset.y;

    UIColor * color = [UIColor whiteColor];
    CGFloat alpha = MIN(1, tableViewoffsetY/136);

    self.backgroundColor = [color colorWithAlphaComponent:alpha];

    if (tableViewoffsetY < 125){

        [UIView animateWithDuration:0.25 animations:^{
            self.searchButton.hidden = NO;
            [self.emailButton setBackgroundImage:[UIImage imageNamed:@"home_email_black"] forState:UIControlStateNormal];
            self.searchBar.frame = CGRectMake(-(self.width-60), 30, self.width-80, 30);
            self.emailButton.alpha = 1-alpha;
            self.searchButton.alpha = 1-alpha;


        }];
    } else if (tableViewoffsetY >= 125){

        [UIView animateWithDuration:0.25 animations:^{
            self.searchBar.frame = CGRectMake(20, 30, self.width-80, 30);
            self.searchButton.hidden = YES;
            self.emailButton.alpha = 1;
            [self.emailButton setBackgroundImage:[UIImage imageNamed:@"home_email_red"] forState:UIControlStateNormal];
        }];
    }

}


更多中文文档