来自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
来说,布局比较简单
我们再来看看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实现"视差效果"的视图轮播所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)