Swift实现"视差效果"的视图轮播

Swift实现"视差效果"的视图轮播,第1张

概述来自Leo的原创博客,转载请著名出处 我的StackOverflow 我的Github https://github.com/LeoMobileDeveloper 注意:本文的代码是用Swift 2.2写的。 视差效果 什么是视差效果?我们来看下格瓦拉的App,就知道了 格瓦拉的视差效果算是比较明显的。所谓视差效果,就是看起来在”上面”的视图滚动的速度大于”底层”的时图滚动。所以,给人的视觉体验要

来自Leo的原创博客,转载请著名出处

我的StackOverflow

我的Github
https://github.com/LeoMobileDeveloper

注意:本文的代码是用Swift 2.2写的。

视差效果

什么是视差效果?我们来看下格瓦拉的App,就知道了

格瓦拉的视差效果算是比较明显的。所谓视差效果,就是看起来在”上面”的视图滚动的速度大于”底层”的时图滚动。所以,给人的视觉体验要比”屌丝”的滚动效果好不少。

ParallexBanner

之前项目赶进度,一直用的开源的。最近刚好在复习Swift,脑袋一热,就自己写了个。

ParallexBanner

支持

循环滚动 自动滚动 本地图片和网络图片 视差效果 Storyboard和纯Code布局

效果

实现视图轮播的几种方式

视图轮播没什么难度,大致分为几种实现方式

单纯的用ScrollVIEw实现,然后一张一张图片subvIEw添加进去。 UICollectionVIEw实现 UIPageVIEwController实现

大致分析了下。

ScrollVIEw实现简单粗暴,但是有一个很大的问题,视图复用。因为是一次性addSubVIEw进去的。所以,在图片较多的时候,内存占用较多。 UIPageVIEwController实现依赖于VIEwController,而作为一个视图来说,还是轻量级比较好一点。 UICollectionVIEw帮我们实现了复用,我们只需要关注轮播本身就可以了。

So,
本文就选用CollectionVIEw实现吧。

定义接口

写一个功能或者业务的第一步,定义接口,想要整体的类分布,值传递的逻辑。(这个很重要)

用Swift写代码要注意一点:Swift是一个面相协议编程的语言

所以,Try start with protocol.

视图轮播需要数据源传递进来,同样需要把点击和滚动事件传递出去。所以,我们就采用Cocoa touch的常用设计模式:dataSource和delegate,定义如下

@objc public protocol ParallexBannerDelegate {    //点击事件    optional func banner(banner:ParallexBanner,dIDClickAtIndex index:NSInteger)    //滚动事件    optional func banner(banner:ParallexBanner,dIDScrollToIndex index:NSInteger)}@objc public protocol ParallexBannerDataSource{     //一共有几个    func numberOfBannersIn(bannner:ParallexBanner)->NSInteger    //每一个index处的图片,这里可以返回String或者UIImage类型    func banner(banner:ParallexBanner,urlOrImageAtIndex index:NSInteger)->AnyObject    //Placeholder    optional func banner(banner:ParallexBanner,placeHolderForIndex index:NSInteger)->UIImage?    //Image的ContentMode    optional func banner(banner:ParallexBanner,contentModeAtIndex index:NSInteger)->UIVIEwContentMode}

对了,我们要支持两种类型的滚动:普通滚动,和视差滚动。这里有两种方式试下,一种是用一个Bool来表示,另一种是用枚举。

考虑到以后,我可能添加更多的滚动模式,这里用枚举表示。

public enum ParallexBannerTransition{    case normal    case Parallex}

然后,我们还需要几个属性,暴露出来给用户设置。这时候的代码如下

public class ParallexBanner: UIVIEw {// MARK: - Propertys -    public  weak var dataSource:ParallexBannerDataSource?    public  weak var delegate:ParallexBannerDelegate?    public  var TransitionMode:ParallexBannerTransition = ParallexBannerTransition.Parallex    public  var autoScroll:Bool = true    public  var enableScrollForSinglePage = false    public  var parllexSpeed:CGfloat = 0.4    public  var autoScrollTimeInterval:NSTimeInterval = 3.0     public  let pageControl:UIPageControl = UIPageControl()    private var _currentIndex = 1    private var collectionVIEw:UICollectionVIEw!    private var timer:NSTimer?    private var flowLayout:UICollectionVIEwFlowLayout!// MARK: - Init -    overrIDe public init(frame: CGRect) {        super.init(frame: frame)        commonInit()    }    public required init?(coder aDecoder: NSCoder) {        super.init(coder: aDecoder)        commonInit()    }}
视图布局

在定义好接口之后,我们要考虑布局了。
对于ParallexBanner来说,布局比较简单

底层是一个UICollectionVIEw 上层是一个UIPageControl

我们再来看看CollectionVIEwCell
普通的滚动CollectionVIEwCell中只有一个UIImageVIEw,为了实现”视差效果”,我们需要Cell本身也能够控制ImageVIEw滚动。所以,我们用一个ScrollVIEw来包含ImageVIEw,通过控制ContentOffset来控制ImageVIEw的滚动。

public class BannerCell:UICollectionVIEwCell{    let imageVIEw = UIImageVIEw()    let scrollVIEw = UIScrollVIEw()    overrIDe init(frame: CGRect) {        super.init(frame: frame)        commonInit()    }    private func commonInit(){        contentVIEw.addSubvIEw(scrollVIEw)        scrollVIEw.scrollEnabled = false        //这里要设置,不然这个scrollVIEw会吃掉我们的触摸        scrollVIEw.userInteractionEnabled = false        scrollVIEw.addSubvIEw(imageVIEw)        imageVIEw.contentMode = UIVIEwContentMode.ScaleAspectFill;    }    required public init?(coder aDecoder: NSCoder) {        fatalError("init(coder:) has not been implemented")    }    overrIDe public func layoutSubvIEws() {        super.layoutSubvIEws()        scrollVIEw.contentSize = self.bounds.size;        scrollVIEw.frame = self.bounds        imageVIEw.frame = scrollVIEw.bounds    }}
循环滚动

用CollectionVIEw实现基于Timer的滚动没什么难度。
无非就是一行代码

collectionVIEw.scrollToItemAtIndexPath(nextIndx,atScrollposition: UICollectionVIEwScrollposition.None,animated: true)

那么如何实现循环滚动呢?有很多种方式实现,本文采用在前后插入两个额外的数据来实现。比如我有三张图,

然后,在前后各插入两张

当我向右滚动,滚动到如图红色虚线的临街区域的时候,就把contentOffset调整到左边的位置

同样,当我向左滚动到临界区域,就调整contentOffset到右侧区域

这样就实现了循环滚动。
对应代码

public func scrollVIEwDIDScroll(scrollVIEw: UIScrollVIEw) {        var offSetX = scrollVIEw.contentOffset.x        let wIDth = CGRectGetWIDth(scrollVIEw.bounds)        guard wIDth != 0 else{            return        }        if offSetX >= wIDth * CGfloat(self.dataSource!.numberOfBannersIn(self) + 2 - 1){            offSetX = wIDth;            scrollVIEw.contentOffset = CGPointMake(offSetX,0);        }else if(offSetX < 0 ){            offSetX = wIDth * CGfloat(self.dataSource!.numberOfBannersIn(self) + 2 - 2);            scrollVIEw.contentOffset = CGPointMake(offSetX,0);        }    }
视差效果

视差效果还是比较简单实现的。我们获取当前在屏幕上的Cell,然后计算相对移动的距离,然后,把Cell本身的ImageVIEw像相反方向按照Speed来移动。

collectionVIEw.visibleCells().forEach { (cell) in            if let bannerCell = cell as? BannerCell{               handleEffect(bannerCell)            }        }

调整Cell中的ScrollVIEw的ContentOffset

private func handleEffect(cell:BannerCell){        switch TransitionMode {        case .Parallex:            let minusX = self.collectionVIEw.contentOffset.x - cell.frame.origin.x            let imageOffsetX = -minusX * parllexSpeed;            cell.scrollVIEw.contentOffset = CGPointMake(imageOffsetX,0)        default:            break        }    }
总结

到这里,基本的原理就讲解完了。其实,所谓的视差效果,就是合理的利用ScrollVIEw。感兴趣的同学可以看看源代码,不到300行,很简单。地址:ParallexBanner

总结

以上是内存溢出为你收集整理的Swift实现"视差效果"的视图轮播全部内容,希望文章能够帮你解决Swift实现"视差效果"的视图轮播所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1073837.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存