在上周, 我们已经通过Ps将Lifestyle页面给设计出来了, 本周我们就可以按照设计图纸进行开工啦, 今天我们就按部就班的完成LifestyleViewController的第一个功能无限轮播图!!
参考链接:
以下内容在上述文章基础上进行, 请事先查阅.
无限轮播图, 这种简单的功能没什么技术含量, 实现的方式也各种各样, 技术含量较高的分为:
1) UIScrollView二图流
(就是两张图之间来回切换) 这个对算法的要求比较高, 一般不推荐自己写, 使用网上现成封装好的就好啦.
2) UICollectionViewLayout布局流
(使用自定义Layout布局) 这种对算法要求极高, 不过一劳永逸, 使用方便快捷低耦合.
还有其他的实现的方法我也不一一列举, 在SQExtension中也有封装好的SQInfiniteCell可以使用, 今天我推荐的方法是投机流
那什么是投机流呢, 且听我娓娓道来. 先回到我们的项目, 经过设计图分析,首先我们先将导航栏进行隐藏, 隐藏的方式各种各样, 可以直接setHidden
但是为了我们之后的功能, 先按如下设置:
隐藏导航栏
- (void)loadView {
[super loadView];
[self.navigationItem setTitleView:[UIView new]];
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageWithColor:[UIColor clearColor]] forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setShadowImage:[UIImage imageWithColor:[UIColor clearColor]]];
}
隐藏导航栏 我选择的方式是将导航栏填充透明背景色而不是将其隐藏. setTitleView
是因为之前的框架有KVO属性, 接下来我们将tableView向上偏移64
_tableView.contentInset = UIEdgeInsetsMake(-64, 0, 0, 0);
- (UITableView *)tableView {
if (!_tableView) {
_tableView = [UITableView new];
_tableView.frame = self.view.bounds;
_tableView.dataSource = self;
_tableView.delegate = self;
_tableView.contentInset = UIEdgeInsetsMake(-64, 0, 0, 0);
}
return _tableView;
}
这样我们几步简单的操作, 就将导航栏给隐藏了, 接下来我们需要自定义Cell.
自定义cell
自定义Cell 的方式也是各有不同, 写法也大不一样, xib, 代码流等等, 我是属于纯代码流的, 并不是说我不会用AutoLayout, 而是我觉得用xib写自定义控件的效率和可维护性 还是比较低下的, 但这是今后的趋势Apple的开发这种可见即可得的出发点就是让我们不用关心展示层, 而更加关心业务逻辑.
扯远了… 我们现在使用纯代码, 之前我有讲过代码库这个功能, 就不过多赘述, 我们使用代码库在.h文件和.m文件中添加:
.h
+ (instancetype)cellWithTableView:(UITableView *)tableView;
+ (CGFloat)cellHeight;
.m
+ (instancetype)cellWithTableView:(UITableView *)tableView {
NSString * identifier = NSStringFromClass([<#class#> class]);
<#class#> * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell) {
cell = [[<#class#> alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
return cell;
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self setupSubviews];
}
return self;
}
- (void)setupSubviews {
}
- (void)layoutSubviews {
[super layoutSubviews];
}
+ (CGFloat)cellHeight {
return <#cellHeight#>;
}
接着我们在tableView的cellForRow
和heightForRow
代理方法中添加:
cellForRow
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SQBannerCell * cell = [SQBannerCell cellWithTableView:tableView];
return cell;
}
heightForRow
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [SQBannerCell cellHeight];
}
这样我们的自定义Cell就算完成了, 非常简单快捷吧, 接下来我们要实现轮播图的功能!
实现无限滚动
现在我们就来说说这个投机流是怎么一回事. 我们还是使用UICollectionView 因为这样就不用我们自己来做复用缓存池啦 cell.contentView.layer.contents = (__bridge id)[UIImage imageNamed:@"banner"].CGImage;
这个是layer的寄宿图属性, 不懂得同学可以Google一下. (我只是偷懒 ^ ^)
创建CollectionView
- (UICollectionViewFlowLayout *)flowLayout {
if (!_flowLayout) {
_flowLayout = [UICollectionViewFlowLayout new];
_flowLayout.minimumInteritemSpacing = 0.0f;
_flowLayout.minimumLineSpacing = 0.0f;
_flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
}
return _flowLayout;
}
- (UICollectionView *)collectionView {
if (!_collectionView) {
_collectionView = [[UICollectionView alloc]initWithFrame:CGRectZero collectionViewLayout:self.flowLayout];
_collectionView.dataSource = self;
_collectionView.delegate = self;
_collectionView.pagingEnabled = YES;
_collectionView.bounces = NO;
_collectionView.showsHorizontalScrollIndicator = NO;
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
}
return _collectionView;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
cell.contentView.layer.contents = (__bridge id)[UIImage imageNamed:@"banner"].CGImage;
return cell;
}
布局collectionView
- (void)layoutSubviews {
[super layoutSubviews];
self.flowLayout.itemSize = self.contentView.bounds.size;
self.collectionView.frame = self.contentView.bounds;
}
到这一步我们collectionView算是完成 但怎么完成无线滚动呢? 说好的投机呢?
投机流大揭秘
使用全局来替换宏 这样的性能会好一点
static const NSInteger kMultiply = 500;
static const NSInteger kCounts = 5;
这算神马投机?? 表示看不懂啊有木有!! 不急接着往下看!!
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return kCounts * kMultiply;
}
在layoutSubviews中添加如下代码
- (void)layoutSubviews {
[super layoutSubviews];
self.flowLayout.itemSize = self.contentView.bounds.size;
self.collectionView.frame = self.contentView.bounds;
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:kCounts * kMultiply * 0.5f inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
}
这样我们就完成了无限轮播了, 不信你试试, 相信聪明的同学已经看懂了, 没错就是在collectionView创建完之后让他从中间开始走, kCounts
代表你数据源有几条数据, kMultiply
代表着你的乘积, 其实并不是真正的无限循环, 但效果已经达到了不是, 坏笑~~ (用户不可能一直翻翻翻,翻个500多下吧, 脑残除外) 接下来继续分析设计图, 发现在pageControl下面有一个浮雕层, 我们把这张图片给添加进来, (使用Ps切图工具 CMD + C)
- (UIImageView *)reliefArcImageView {
if (!_reliefArcImageView) {
_reliefArcImageView = [UIImageView new];
_reliefArcImageView.image = [UIImage imageNamed:@"RelliefArc"];
}
return _reliefArcImageView;
}
- (UIPageControl *)pageControl {
if (!_pageControl) {
_pageControl = [UIPageControl new];
_pageControl.hidesForSinglePage = YES;
_pageControl.defersCurrentPageDisplay = YES;
_pageControl.numberOfPages = kCounts;
_pageControl.pageIndicatorTintColor = KC05_dddddd;
_pageControl.currentPageIndicatorTintColor = KC01_57c2de;
}
return _pageControl;
}
- (void)setupSubviews {
[self.contentView addSubview:self.collectionView];
[self.contentView addSubview:self.reliefArcImageView];
[self.contentView addSubview:self.pageControl];
}
- (void)layoutSubviews {
[super layoutSubviews];
self.flowLayout.itemSize = self.contentView.bounds.size;
self.collectionView.frame = self.contentView.bounds;
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:kCounts * kMultiply * 0.5f inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
CGFloat reliefArcImageViewX = 0;
CGFloat reliefArcImageViewH = kScaleLength(50);
CGFloat reliefArcImageViewY = self.height - reliefArcImageViewH;
CGFloat reliefArcImageViewW = self.width;
self.reliefArcImageView.frame = CGRectMake(reliefArcImageViewX, reliefArcImageViewY, reliefArcImageViewW, reliefArcImageViewH);
CGFloat pageControlX = 0;
CGFloat pageControlH = 35;
CGFloat pageControlY = self.collectionView.bounds.size.height - pageControlH;
CGFloat pageControlW = self.collectionView.bounds.size.width;
self.pageControl.frame = CGRectMake(pageControlX, pageControlY, pageControlW, pageControlH);
}
接着我们在scrollViewDidScroll
代理方法中添加,至此无限轮播大功告成!!
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
self.pageControl.currentPage = ((NSInteger)((scrollView.contentOffset.x + self.contentView.frame.size.width) / self.contentView.frame.size.width) - 1) % kCounts;
}
添加定时器
接下来我们要做的就是添加定时器的功能了, 让其自动的轮播! 其实非常之简单!!
1) 在初始化的时候添加定时器 [self setupTimer]
;
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self setupTimer];
[self setupSubviews];
}
return self;
}
2) 配置定时器功能
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self.timer invalidate];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
[self setupTimer];
}
- (void)setupTimer {
if ([self respondsToSelector:@selector(updateTimer)]) {
self.timer = [NSTimer timerWithTimeInterval:5.0f target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
}
}
- (void)updateTimer {
[self.collectionView setContentOffset:CGPointMake(self.collectionView.contentOffset.x + self.contentView.frame.size.width, 0) animated:YES];
}
到这 我们今天的功能就全部结束了, 投机流是不是非常简单高效快捷呢 哈哈哈~~~
在Reveal中显示
最终显示
具体源码及SQExtension方法信息 请到github上进行下载!