Swift实现"视差效果"的视图轮播
来自Leo的原创博客,转载请著名出处 我的StackOverflow 我的Github 注意:本文的代码是用Swift 2.2写的。 视差效果什么是视差效果?我们来看下格瓦拉的App,就知道了
ParallexBanner之前项目赶进度,一直用的开源的。最近刚好在复习Swift,脑袋一热,就自己写了个。
支持
效果 实现视图轮播的几种方式视图轮播没什么难度,大致分为几种实现方式
大致分析了下。
So, 定义接口写一个功能或者业务的第一步,定义接口,想要整体的类分布,值传递的逻辑。(这个很重要) 用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()
}
}
视图布局在定义好接口之后,我们要考虑布局了。
我们再来看看CollectionViewCell 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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |