Castie!

正态分布, 优劣伴生

北冥有鱼,其名为鲲(kūn)。鲲之大,不知其几千里也;化而为鸟,其名为鹏。鹏之背,不知其几千里也;怒而飞,其翼若垂天之云。是鸟也,海运则将徙于南冥。南冥者,天池也。


北海若曰:“井鼃不可以语于海者,拘于虚也;夏虫不可以语于冰者,笃于时也;曲士不可以语于道者,束于教也。今尔出于崖涘,观于大海,乃知尔丑,尔将可与语大理矣。

iOS 投机流实现 无限轮播图

在上周, 我们已经通过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的cellForRowheightForRow代理方法中添加:

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上进行下载!

最近的文章

iOS 会跳舞的TabbarController

上周的 收到大家一致的好评和转载, 小弟心中感到很是欣慰, 所以今天再此决定分享一个干货给各位小伙伴们!!参考链接: iOS 做好开工前的准备 iOS 集成Reveal UI调试利器 UI/UX 产品原型 从Axure开始 UI/UX 使用Ps 进行视觉设计 iOS 投机流实现 无限轮播图以下内容在上述文章基础上进行, 请事先查阅.具体是什么干货呢, 今天要分享的是会跳舞的TabbarController, 什么是会跳舞的TabbarController? 很简单,就是使用自...…

移动开发继续阅读
更早的文章

UI/UX 使用Ps 进行视觉设计

Tools Photoshop CS6上周我们已经将产品原型基本完成了, 本周我们就按照原型图绘制出设计图, 工具当然是大家最熟悉不过的PS(因为 不太会用Sketch) 作为一名开发者, 花个一周时间来学习入门一下PS还是很有必要的, 好了 话不多说, 进入到我们今天的工作中来.当我们拿到原型图的时候 需要分析原型图中的所有元素, 定下色彩标准和字体的标准. 当然, 不同的项目, 也需要根据不同的情况来制定标准. 由于整套原型图的内容较多, 所以今天就把首页来绘制出来.PS 中有很多的...…

UI设计继续阅读