黑马程序员技术交流社区
标题:
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以及不同大小的Cell
1.实现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