黑马程序员技术交流社区

标题: iOS开发实用技能-循环滚动的UITableView [打印本页]

作者: iOS资源工匠哥    时间: 2016-12-13 16:13
标题: iOS开发实用技能-循环滚动的UITableView
   在iOS开发中,UITableView是使用很频繁的控件之一,之前在某个app中的一个电台选择播放功能中,通过循环滚动UITableViewCell以及设置Cell显示的不同大小等方式来实现该功能,原图如图1所示:


图1.PNG


于是我也尝试实现了一番,效果图如下所示:





构建UIScrollViewBar
    继承UIView,实现自定义可滚动的UIScrollViewBar,由于滚动条里面的文字长度不一致,因此要根据文字的长度向UIScrollViewBar中添加按钮。为了能保证用户每次在点击一个按钮时,该按钮都需要居中显示,可以通过按钮center.x减去UIScrollViewBar的宽度对UIScrollView上x的偏移量进行相应的移动,并且当用户点击最左边或最右边的按钮时需要固定UIScrollView上x的偏移量,保持UIScrollView不移动。

CGPoint offset = self.scrollBackView.contentOffset;
offset.x = btn.center.x - ScreenW / 2;
if (offset.x + ScreenW >= self.scrollBackView.contentSize.width) {//处理scrollView右边部分
      offset.x = self.scrollBackView.contentSize.width - ScreenW;
      [self.scrollBackView setContentOffset:offset animated:YES];
    }else if(offset.x <= 0){//处理scrollView左边部分
      offset.x = 0;
[self.scrollBackView setContentOffset:offset animated:YES];
    }else{
      [self.scrollBackView setContentOffset:offset animated:YES];
    }
当用户点击一个按钮时,按钮下方的线条会根据按钮长度的不同而缩放,并且线条的长度变化是在移动过程中变化的,如下图所示,因此可以通过CGAffineTransformMakeScale来实现。




CGSize bottomLabSize = self.bottomLab.bounds.size;
CGFloat scaleW = btn.bounds.size.width / bottomLabSize.width;
self.bottomLab.transform = CGAffineTransformMakeScale(scaleW, 1.0);
最后通过代理的方式将UIScrollViewBar与主控制器中的UISrcrollView关联起来,即通过点击不同的按钮主控制器中UISrcrollView做相应的移动,除此之外,在滑动主控制器中的UISrcrollView的时候UIScrollViewBar中的按钮也要随之变化,主要通过UISrcrollView中的两个代理方法实现,大致如下:
//通过代码setContentOffset:animated:让scrollView滚动完毕后,就会调用这个方法
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView )scrollView;
//人为拖曳就会调用这个方法
- (void)scrollViewDidEndDecelerating:(UIScrollView
)scrollView;构建循环滚动的UITableView以及不同大小的Cell1.实现UITableView的循环滚动继承UITableView,实现自定义的UPTableViewDisplay,为了能达到循环滚动的效果,因此在代理方法(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section中返回原来3倍的cell个数,并且当UPTableViewDisplay处于最顶部的时候,用户此时准备向下滚动时,将UPTableViewDisplay的contentOffset.y调整到自身contentSize的三分之一,同理,当UPTableViewDisplay处于最底部的时候,用户此时准备向上滚动时,将UPTableViewDisplay的contentOffset.y调整到自身contentSize的三分之二位置即可,大致代码如下:
CGPoint contentOffset  = self.contentOffset;
    //当UITableView滑到最顶部,并准备向下滑动
    if (contentOffset.y < 0.0) {
        contentOffset.y = self.contentSize.height / 3.0;
    //当UITableView滑到最底部,并准备向上滑动
    }else if (contentOffset.y >= (self.contentSize.height - self.bounds.size.height)) {
        contentOffset.y = self.contentSize.height / 3.0 - self.bounds.size.height;
    }
    [self setContentOffset: contentOffset];2.实现UITableViewCell的不同样式以屏幕中间的cell作为参照物,在它上面和下面的cell依次缩放等比例的大小可以通过计算每个cell的center.y与屏幕一半的差值来获取scale,由于UPTableViewDisplay中的cell在滚出和滚入屏幕的时候,自身的contentOffset.y也会有变化,因此只是获取center.y与屏幕一半的差值来计算scale并不是准确的,还必须减去相应的contentOffset.y才行,最后通过差值除以UPTableViewDisplay高度的一半可以得到缩放比例,如下图所示:





        同理,在设置cell的颜色时也是基于这种思路,以中间的cell最为参照物,以每个cell的高度作为判断标准,即中间的cell是完全滚上去抑或完全滚下去来改变它的颜色,并且使用(UIColor *)colorWithAlphaComponent:(CGFloat)alpha方法来设置每个cell.contenview的透明度,该方法可以使在contentView上面的控件不会受contentView透明度的影响而变化,大致代码如下:


NSArray cells = [self visibleCells];
    CGFloat centerY = self.contentOffset.y + self.bounds.size.height / 2;
    for (UPDisplayViewCell
cell in cells) {
        //设置cell的缩放大小
        //CGFloat scale = (1 - ABS(cell.center.y - centerY) / self.bounds.size.height) 1.05;
        CGFloat scale = 1 - ((ABS(cell.center.y - centerY) / self.bounds.size.height / showCellCount))
3.0;
        cell.backView.transform = CGAffineTransformMakeScale(scale, scale);
        //设置cell的颜色
        CGFloat distance = ABS(cell.center.y - centerY);
        if (distance > self.rowHeight) {
            cell.contentView.backgroundColor = [[UIColor purpleColor] colorWithAlphaComponent:0.0];
        }else{
            CGFloat alpha = ABS(1.0 - distance / self.rowHeight);
            cell.contentView.backgroundColor = [[UIColor purpleColor] colorWithAlphaComponent:alpha];
        }
    }

3.实现UITableViewCell的加载动画每一次在加载UITableViewCell的时候都是以展开的形式呈现的,并且该形式只会实现一次,因此通过可以通过一个成员变量来记录是否是第一次加载,并且在代理方法- (void)tableView:(UITableView )tableView willDisplayCell:(UITableViewCell )cell forRowAtIndexPath:(NSIndexPath *)indexPath中实现此动画。由于中间的cell是在最顶层的,其他的cell是以对称的形式展开的,因此我们首先需要在该代理方法中把每个cell的位置做修改,在通过transform.translation.y来移动每一个cell,大致代码如下:


if (self.firstLoad) {
        UPDisplayViewCell displayCell = (UPDisplayViewCell )cell;
        int displayCellCenterY = displayCell.center.y;
        if (displayCellCenterY == (int)(self.frame.size.height / 2)) {//表示为中间的cell
            [self insertSubview:displayCell atIndex:self.subviews.count - 1];
        }else if (displayCellCenterY == (int)(self.frame.size.height / 2 - self.rowHeight)){//表示是第二个cell
            [self insertSubview:displayCell atIndex:self.subviews.count - 2];
        }
        //添加展开动画
        int moveDis = displayCell.center.y - self.bounds.size.height / 2;
        if (moveDis != 0) {
            CABasicAnimation *moveCellAnima = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
            moveCellAnima.fromValue = [NSNumber numberWithFloat:-moveDis];
            moveCellAnima.toValue = [NSNumber numberWithFloat:0.0];
            moveCellAnima.duration = 0.7;
            moveCellAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
            [displayCell.layer addAnimation:moveCellAnima forKey:@"moveCellAnima"];
        }
    }

    最后,为了保证UPTableViewDisplay的连贯性以及消除UPTableViewDisplay滚动时带来的惯性,还需要实现UIScrollViewDelegate代理里面的两个方法,即用户通过两种方式使UPTableViewDisplay滚动停止时触发的方法,一种是scrollViewDidEndDragging,一种是scrollViewDidEndDecelerating,在两种方法中做相应的处理即可。       文/小时候De_我(简书作者) 原文链接:http://www.jianshu.com/p/f53777a146db






欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2