实现无限轮播
生活杂谈
- 最近很多童鞋私信我,Swift项目有些语法跟不上。希望我出OC版的(OC版我会在后续推出),但是以我的考虑来说,Swift在语言排行版上面其实已经超过OC,另外国内现在新项目一般都会用Swift开发了。所有其实不能总是对于Swift是一种拒绝的态度,待在舒适区是会被淘汰的噢
- 但是对于刚从OC转Swift的人来说,确实Swift语法和OC差异太大,又没有比较好的教程。后续我会看看能不能出一个OC快速转向Swift的教程,不需要的童鞋可以先研究我们的项目,需要的童鞋可以抓紧时间学习一下
实现效果
- 轮播效果
思路分析
- 实现无限轮播常用的方案有三个:
- 第三方框架:(不推荐,该功能并不难,而且三方框架中会有很多冗余代码)
- UIScrollView:上面放三个View,自己实现三个View的循环利用
- UICollectionView:利用系统会对UICollectionViewCell的循环利用机制实现
- 方案选择:
- 方案三:简单好用,循环利用的问题交给系统自己处理即可
界面搭建
- 自定义一个UIView
- 由于该View上面内容比较固定,因此可以直接通过Xib进行描述
- 添加UICollectionView,占据整个View,右下角添加UIPageControl
- 设置UICollectionView的布局,设置数据源以及实现数据源方法(见代码)
- 切记:设置自定义View的autoresizingMask = .None,否则控件将不能显示
- 部分代码解释
// MARK:- 定义RecommendCycleView类
class RecommendCycleView: UIView {
// MARK: 控件属性
@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var pageControl: UIPageControl!
// MARK: 系统回调
override func awakeFromNib() {
super.awakeFromNib()
// 设置不自动拉伸
autoresizingMask = .None
// 注册cell
collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: kCycleCellID)
}
override func layoutSubviews() {
super.layoutSubviews()
// 设置collectionView的布局
let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = collectionView.bounds.size
}
}
// MARK:- 通过Xib快速创建的类方法
extension RecommendCycleView {
class func recommendCycleView() -> RecommendCycleView {
return NSBundle.mainBundle().loadNibNamed("RecommendCycleView", owner: nil, options: nil).first as! RecommendCycleView
}
}
// MARK:- 实现UICollectionView的数据源&代理
extension RecommendCycleView : UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(kCycleCellID, forIndexPath: indexPath)
cell.backgroundColor = indexPath.item % 2 == 0 ? UIColor.redColor() : UIColor.blueColor()
return cell
}
}
extension RecommendCycleView : UICollectionViewDelegate {
}
-
将自定义View添加到UICollectionView中
- 懒加载RecommendCycleView对象
- 将cycleView添加到UICollectionView中
- 设置UICollectionView的内边距
- 代码如下:
-
懒加载RecommendCycleView
private lazy var cycleView : RecommendCycleView = {
let cycleView = RecommendCycleView.recommendCycleView()
cycleView.frame = CGRect(x: 0, y: -kCycleViewH, width: kScreenW, height: kCycleViewH)
return cycleView
}()
- 添加到collectionView中
// 添加cycleView
collectionView.addSubview(cycleView)
collectionView.contentInset = UIEdgeInsets(top: kCycleViewH, left: 0, bottom: 0, right: 0)
请求数据&展示数据
请求数据
- 接口描述
- 接口名称:顶部轮播数据
- 接口地址:http://www.douyutv.com/api/v1/slide/6
- 请求参数:</br>
参数名称 | 参数说明 |
---|---|
version | 当前版本号:2.300 |
- 在RecommendViewMode中发送网络请求
- 根据接口发送请求
- 定义CycleModel模型
- 将请求到的数据转成模型对象
- 模型定义
class CycleModel: NSObject {
/// 轮播标题
var title : String = ""
/// 轮播图片
var pic_url : String = ""
/// 轮播对应主播信息
var anchor : AnchorModel?
/// 主播信息
var room : [String : NSObject]? {
didSet {
guard let room = room else { return }
anchor = AnchorModel(dict: room)
}
}
// MARK: 构造函数
init(dict : [String : NSObject]) {
super.init()
setValuesForKeysWithDictionary(dict)
}
override func setValue(value: AnyObject?, forUndefinedKey key: String) {}
}
- 数据请求
func requestCycleData(finishedCallback : () -> ()) {
NetworkTools.requestData(.GET, URLString: "http://www.douyutv.com/api/v1/slide/6?version=2.300") { (result) in
// 1.将结果转成字典
guard let resultDict = result as? [String : NSObject] else { return }
guard let dataArray = resultDict["data"] as? [[String : NSObject]] else { return }
// 2.将字典转成模型对象
for dict in dataArray {
self.cycleModels.append(CycleModel(dict: dict))
}
finishedCallback()
}
}
展示数据
- 将数据传递给RecommendCycleView对象
- 刷新UICollectionView
- 设置UIPageControl的个数
- 自定义Cell,用于展示数据
- 通过xib直接描述Cell
- 通过xib直接描述Cell
- 根据模型展示数据
- 代码如下:
class CollectionCycleCell: UICollectionViewCell {
// MARK: 控件属性
@IBOutlet weak var iconImageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
// MARK: 定义模型属性
var cycleModel : CycleModel? {
didSet {
titleLabel.text = cycleModel?.title
let iconURL = NSURL(string: cycleModel?.pic_url ?? "")!
iconImageView.kf_setImageWithURL(iconURL)
}
}
}
实现无限轮播功能
- 在返回Cell个数地方,返回无限个数
- 例如:(cycleModels?.count ?? 0) * 10000
- 原因:无论用户怎么滚动,滚动几天可能才能滚完
- 另外:因为Cell有循环利用,是不会常见那么多Cell的。不会造成内存很大
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return (cycleModels?.count ?? 0) * 10000
}
- 随着用户的滚动,改变pageControl的显示
- 监听UICollectionView的滚动即可
func scrollViewDidScroll(scrollView: UIScrollView) {
let offset = scrollView.contentOffset.x + scrollView.bounds.width * 0.5
pageControl.currentPage = Int(offset / scrollView.bounds.width) % (cycleModels?.count ?? 1)
}
-
让用户向前滚动也可以
- 默认滚动到60处,那么用户向前滚动也有内容
- 注意:不需要太多,因为用户习惯来讲是很少向前滚动的
let indexPath = NSIndexPath(forItem: (cycleModels?.count ?? 0) * 10, inSection: 0)
collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .Left, animated: false)
-
自动滚动功能
- 添加定时器
- 每隔3秒钟自动滚动到下一个
// MARK:- 对定时器操作方法
extension RecommendCycleView {
private func addCycleTimer() {
cycleTimer = NSTimer(timeInterval: 3, target: self, selector: #selector(self.scrollToNext), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(cycleTimer!, forMode: NSRunLoopCommonModes)
}
private func removeCycleTimer() {
cycleTimer?.invalidate()
cycleTimer = nil
}
@objc private func scrollToNext() {
// 滚动collectionView
let currentOffSet = collectionView.contentOffset.x + collectionView.bounds.width
collectionView.setContentOffset(CGPoint(x: currentOffSet, y: 0), animated: true)
}
}
- 监听用户拖拽
- 用户拖拽过程中,定时器不更新
- 监听用户拖拽&结束拖拽即可
func scrollViewWillBeginDragging(scrollView: UIScrollView) {
removeCycleTimer()
}
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
addCycleTimer()
}