网易新闻顶部效果

没想到有一天我竟然真的进入了我梦寐以求的网易,虽然只是个实习生~

也没想到第一个正式的任务就是做个网易新闻顶部的效果。因为之前有好多面试官都问过我类似的问题,要么就是问网易新闻顶部的这个效果,要么就是问知乎日报顶部的那个效果,没想到现在我还真要做这个了偷笑

说了半天废话,到底是啥效果?(下个iOS版的网易新闻不就知道了。。。)(和安卓版的不一样)

我做的效果如下图:



刚开始感觉好难。几乎没什么思路。后来冷静思考了下,先是想修改下边那个tableView的contentOffset,后来又想改tableView的contentSize,还想过把上面的view和下面的tableView拼接到一起,还想过修改上面的view的contentOffset。。。。。。


但是后来我发现这些想法都是错误的。。。(天,这就是我冷静思考后的结果么。。。)


最后想到,其实只要修改上面的view的frame和下面的tableView的frame就可以了。


那么怎么做呢?

利用KVO监听。

static NSString *const kContentOffset = @"contentOffset";

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.tableView addObserver:self forKeyPath:kContentOffset options:0 context:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.tableView removeObserver:self forKeyPath:kContentOffset];
}


我们监听tableView的contentOffset,划动的时候它的contentOffset会发生变化,这样我们就可以根据它的contentOffset来动态地修改它的frame和顶部topView的frame。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:kContentOffset]) { // 监听的是contentOffset的变化
        [self.tableView removeObserver:self forKeyPath:kContentOffset]; // 这里需要先移除监听,否则会导致死循环
        
        CGFloat newY = self.tableView.contentOffset.y; // 获取划动的contentOffset的y值
        CGFloat topY = CGRectGetHeight(self.topView.frame); // topView的高度
        
        if (newY <= 0) { // topView在底部,此时只有tableView动
            self.tableView.frame = CGRectMake(0, topY, ScreenWidth, ScreenHeight-topY);
        } else if (newY > 0 && newY < topY-64) { // topView在中间,两者都动
            self.tableView.frame = CGRectMake(0, topY-newY, ScreenWidth, ScreenHeight-(topY-newY));
            self.topView.frame = CGRectMake(0, -newY, ScreenWidth, topY);
        } else if (newY >= topY-64) { // topView在顶部,此时只有tableView动
            self.tableView.frame = CGRectMake(0, 64, ScreenWidth, ScreenHeight-64);
        }
        
        [self.tableView addObserver:self forKeyPath:kContentOffset options:0 context:nil]; // 结束后再添加监听
    }
}


其实只需要分成三种情况来考虑就可以了。即:topView在顶部、topView在中间、topView在底部。

注意:一开始的时候需要先移除监听,否则会导致死循环,之后再添加监听即可。


然后是关于topView的部分。怎么让topView中的控件逐渐变透明,并且在划动到一定程度后消失,同时产生一个新的名字label呢?

刚开始的时候我的想法是通过layoutSubviews方法来修改。但是通过这个栗子我发现原来并不是frame变了就会调用layoutSubviews,而是frame的size变了才会调用它。在这个栗子中,我们修改的是frame.origin.y,并没有修改frame的size,topView的宽和高始终没有发生过变化,只不过它的y值在动态的改变而已。所以通过layoutSubviews 的方法不可行。

那怎么做呢?


还是监听。只不过刚才我们监听的是tableView的contentOffset,而现在我们需要监听的是topView的frame。

static NSString *const kFrame = @"frame";
[self addObserver:self forKeyPath:kFrame options:0 context:nil];
在topView中自己监听自己的frame的变化,然后动态修改自己的控件的alpha属性。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:kFrame]) {
        CGFloat originY = -self.frame.origin.y;
        
        CGFloat alpha = originY / ((CGRectGetHeight(self.frame)-64)/2);
        self.avatar.alpha = 1 - alpha;
        if (alpha >= 1.0) { // 原本的控件逐渐变透明
            self.nameLabel.center = CGPointMake(ScreenWidth/2, CGRectGetHeight(self.frame)-CGRectGetHeight(self.nameLabel.frame)/2-10);
            self.nameLabel.alpha = alpha - 1;
        } else { // 原本的控件已经完全透明了,产生一个新的(其实用的还是旧的控件,只不过把frame改了)
            self.nameLabel.center = CGPointMake(ScreenWidth/2, CGRectGetMaxY(self.avatar.frame)+20);
            self.nameLabel.alpha = 1 - alpha;
        }
    }
}


核心代码就是这些,demo的完整源码可以去我的GitHub下载: https://github.com/963239327/NETopView












评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值