|
//
// LLCycleScrollView.swift
// LLCycleScrollView
//
// Created by LvJianfeng on 2016/11/22.
// Copyright © 2016年 LvJianfeng. All rights reserved.
//
import UIKit
public enum PageControlStyle {
case none
case system
case fill
case pill
case snake
}
public typealias LLdidSelectItemAtIndexClosure = (NSInteger) -> Void
@IBDesignable open class LLCycleScrollView: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UIScrollViewDelegate {
// MARK: 控制参数
// 是否自动滚动,默认true
open var autoScroll: Bool? = true {
didSet {
invalidateTimer()
if autoScroll! {
setupTimer()
}
}
}
// 无限循环,默认true 此属性修改了就不存在轮播的意义了
open var infiniteLoop: Bool? = true {
didSet {
if imagePaths.count > 0 {
let temp = imagePaths
imagePaths = temp
}
}
}
// 滚动方向,默认horizontal
open var scrollDirection: UICollectionViewScrollDirection? = .horizontal {
didSet {
flowLayout?.scrollDirection = scrollDirection!
if scrollDirection == .horizontal {
position = .centeredHorizontally
} else {
position = .centeredVertically
}
}
}
// 滚动间隔时间,默认2s
@IBInspectable open var autoScrollTimeInterval: Double = 2.0 {
didSet {
autoScroll = true
}
}
// 加载状态图 -- 这个是有数据,等待加载的占位图
@IBInspectable open var placeHolderImage: UIImage? = nil {
didSet {
if placeHolderImage != nil {
placeHolderViewImage = placeHolderImage
}
}
}
// 空数据页面显示占位图 -- 这个是没有数据,整个轮播器的占位图
@IBInspectable open var coverImage: UIImage? = nil {
didSet {
if coverImage != nil {
coverViewImage = coverImage
}
}
}
// 图片显示Mode
open var imageViewContentMode: UIViewContentMode? {
didSet {
collectionView.reloadData()
}
}
// PageControlStyle
// MARK: PageControl
open var pageControlTintColor: UIColor = UIColor.lightGray {
didSet {
setupPageControl()
}
}
// 当前显示颜色
open var pageControlCurrentPageColor: UIColor = UIColor.white {
didSet {
setupPageControl()
}
}
// MARK: CustomPageControl
// 注意: 由于属性较多,所以请使用style对应的属性,如果没有标明则通用
open var customPageControlStyle: PageControlStyle = .system {
didSet {
setupPageControl()
}
}
// 颜色
open var customPageControlTintColor: UIColor = UIColor.white {
didSet {
setupPageControl()
}
}
// 间距
open var customPageControlIndicatorPadding: CGFloat = 8 {
didSet {
setupPageControl()
}
}
// PageControlStyle == .fill
// 圆大小
open var FillPageControlIndicatorRadius: CGFloat = 4 {
didSet {
setupPageControl()
}
}
// PageControlStyle == .pill || PageControlStyle == .snake
// 当前的颜色
open var customPageControlInActiveTintColor: UIColor = UIColor(white: 1, alpha: 0.3) {
didSet {
setupPageControl()
}
}
// 背景色
@IBInspectable open var collectionViewBackgroundColor: UIColor! = UIColor.clear
// ImagePaths
open var imagePaths: Array<String> = [] {
didSet {
totalItemsCount = infiniteLoop! ? imagePaths.count * 100 : imagePaths.count
if imagePaths.count != 1 {
collectionView.isScrollEnabled = true
autoScroll = true
} else {
collectionView.isScrollEnabled = false
}
setupPageControl()
collectionView.reloadData()
}
}
// 标题
open var titles: Array<String> = []
// MARK: Private
// Identifier
fileprivate let identifier = "LLCycleScrollViewCell"
// 数量
fileprivate var totalItemsCount: NSInteger! = 1
// 显示图片(CollectionView)
fileprivate var collectionView: UICollectionView!
// 方向(swift后没有none,只能指定了)
fileprivate var position: UICollectionViewScrollPosition! = .centeredHorizontally
// FlowLayout
lazy fileprivate var flowLayout: UICollectionViewFlowLayout? = {
let tempFlowLayout = UICollectionViewFlowLayout()
tempFlowLayout.minimumLineSpacing = 0
tempFlowLayout.scrollDirection = .horizontal
return tempFlowLayout
}()
// 计时器
fileprivate var timer: Timer?
// PageControl
fileprivate var pageControl: UIPageControl?
fileprivate var customPageControl: UIView?
// 加载状态图
fileprivate var placeHolderViewImage: UIImage! = UIImage(named: "LLCycleScrollView.bundle/llplaceholder.png")
// 空数据页面显示占位图
fileprivate var coverViewImage: UIImage! = UIImage.init(named: "LLCycleScrollView.bundle/llplaceholder.png")
// 回调
open var lldidSelectItemAtIndex: LLdidSelectItemAtIndexClosure?
override public init(frame: CGRect) {
super.init(frame: frame)
// setupMainView
setupMainView()
}
// Class func
open class func llCycleScrollViewWithFrame(_ frame: CGRect, imageURLPaths: Array<String>? = [], titles: Array<String>? = [], didSelectItemAtIndex: LLdidSelectItemAtIndexClosure? = nil) -> LLCycleScrollView {
let llcycleScrollView: LLCycleScrollView = LLCycleScrollView.init(frame: frame)
if (imageURLPaths?.count)! > 0 {
llcycleScrollView.imagePaths = imageURLPaths!
}
if (titles?.count)! > 0 {
llcycleScrollView.titles = titles!
}
if didSelectItemAtIndex != nil {
llcycleScrollView.lldidSelectItemAtIndex = didSelectItemAtIndex
}
return llcycleScrollView
}
// MARK: -
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupMainView()
}
// MARK: setupMainView
private func setupMainView() {
collectionView = UICollectionView.init(frame: self.bounds, collectionViewLayout: flowLayout!)
collectionView.register(LLCycleScrollViewCell.self, forCellWithReuseIdentifier: identifier)
collectionView.backgroundColor = collectionViewBackgroundColor
collectionView.isPagingEnabled = true
collectionView.showsHorizontalScrollIndicator = false
collectionView.showsVerticalScrollIndicator = false
collectionView.dataSource = self
collectionView.delegate = self
collectionView.scrollsToTop = false
self.addSubview(collectionView)
}
// MARK: Timer
func setupTimer() {
timer = Timer.scheduledTimer(timeInterval: autoScrollTimeInterval as TimeInterval, target: self, selector: #selector(automaticScroll), userInfo: nil, repeats: true)
RunLoop.main.add(timer!, forMode: .commonModes)
}
func invalidateTimer() {
if timer != nil {
timer?.invalidate()
timer = nil
}
}
func setupPageControl() {
// 重新添加
if pageControl != nil {
pageControl?.removeFromSuperview()
}
if customPageControl != nil {
customPageControl?.removeFromSuperview()
}
if customPageControlStyle == .none {
pageControl = UIPageControl.init()
pageControl?.numberOfPages = self.imagePaths.count
}
if customPageControlStyle == .system {
pageControl = UIPageControl.init()
pageControl?.pageIndicatorTintColor = pageControlTintColor
pageControl?.currentPageIndicatorTintColor = pageControlCurrentPageColor
pageControl?.numberOfPages = self.imagePaths.count
self.addSubview(pageControl!)
pageControl?.isHidden = false
}
if customPageControlStyle == .fill {
customPageControl = LLFilledPageControl.init(frame: CGRect.zero)
customPageControl?.tintColor = customPageControlTintColor
(customPageControl as! LLFilledPageControl).indicatorPadding = customPageControlIndicatorPadding
(customPageControl as! LLFilledPageControl).indicatorRadius = FillPageControlIndicatorRadius
(customPageControl as! LLFilledPageControl).pageCount = self.imagePaths.count
self.addSubview(customPageControl!)
}
if customPageControlStyle == .pill {
customPageControl = LLPillPageControl.init(frame: CGRect.zero)
(customPageControl as! LLPillPageControl).indicatorPadding = customPageControlIndicatorPadding
(customPageControl as! LLPillPageControl).activeTint = customPageControlTintColor
(customPageControl as! LLPillPageControl).inactiveTint = customPageControlInActiveTintColor
(customPageControl as! LLPillPageControl).pageCount = self.imagePaths.count
self.addSubview(customPageControl!)
}
if customPageControlStyle == .snake {
customPageControl = LLSnakePageControl.init(frame: CGRect.zero)
(customPageControl as! LLSnakePageControl).activeTint = customPageControlTintColor
(customPageControl as! LLSnakePageControl).indicatorPadding = customPageControlIndicatorPadding
(customPageControl as! LLSnakePageControl).indicatorRadius = FillPageControlIndicatorRadius
(customPageControl as! LLSnakePageControl).inactiveTint = customPageControlInActiveTintColor
(customPageControl as! LLSnakePageControl).pageCount = self.imagePaths.count
self.addSubview(customPageControl!)
}
}
// MARK: layoutSubviews
override open func layoutSubviews() {
super.layoutSubviews()
// Cell Size
flowLayout?.itemSize = self.frame.size
// Page Frame
if customPageControlStyle == .none || customPageControlStyle == .system {
pageControl?.frame = CGRect.init(x: 0, y: self.ll_h-11, width: UIScreen.main.bounds.width, height: 10)
} else {
var y = self.ll_h-10-1
// pill
if customPageControlStyle == .pill {
y+=5
}
let oldFrame = customPageControl?.frame
customPageControl?.frame = CGRect.init(x: (oldFrame?.origin.x)!, y: y, width: (oldFrame?.size.width)!, height: 10)
}
if collectionView.contentOffset.x == 0 && totalItemsCount > 0 {
var targetIndex = 0
if infiniteLoop! {
targetIndex = totalItemsCount/2
}
collectionView.scrollToItem(at: IndexPath.init(item: targetIndex, section: 0), at: position, animated: false)
}
}
// MARK: Actions
@objc func automaticScroll() {
if totalItemsCount == 0 {return}
let targetIndex = currentIndex() + 1
scollToIndex(targetIndex: targetIndex)
}
func scollToIndex(targetIndex: Int) {
if targetIndex >= totalItemsCount {
if infiniteLoop! {
collectionView.scrollToItem(at: IndexPath.init(item: Int(totalItemsCount/2), section: 0), at: position, animated: false)
}
return
}
collectionView.scrollToItem(at: IndexPath.init(item: targetIndex, section: 0), at: position, animated: true)
}
func currentIndex() -> NSInteger {
if collectionView.ll_w == 0 || collectionView.ll_h == 0 {
return 0
}
var index = 0
if flowLayout?.scrollDirection == UICollectionViewScrollDirection.horizontal {
index = NSInteger(collectionView.contentOffset.x + (flowLayout?.itemSize.width)! * 0.5)/NSInteger((flowLayout?.itemSize.width)!)
} else {
index = NSInteger(collectionView.contentOffset.y + (flowLayout?.itemSize.height)! * 0.5)/NSInteger((flowLayout?.itemSize.height)!)
}
return index
}
func pageControlIndexWithCurrentCellIndex(index: NSInteger) -> (Int) {
return Int(index % imagePaths.count)
}
// MARK: UICollectionViewDataSource
open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return totalItemsCount
}
open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: LLCycleScrollViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! LLCycleScrollViewCell
// 0==count 占位图
if imagePaths.count == 0 {
cell.imageView.image = coverViewImage
} else {
let itemIndex = pageControlIndexWithCurrentCellIndex(index: indexPath.item)
let imagePath = imagePaths[itemIndex]
// Mode
if let imageViewContentMode = imageViewContentMode {
cell.imageView.contentMode = imageViewContentMode
}
// 根据imagePath,来判断是网络图片还是本地图
if imagePath.hasPrefix("http") {
cell.imageView.setImageWithNullableURL(imagePath, placeholderImage: placeHolderImage)
} else {
if let image = UIImage(named: imagePath) {
cell.imageView.image = image
} else {
cell.imageView.image = UIImage(contentsOfFile: imagePath)
}
}
// 对冲数据判断
if itemIndex <= titles.count-1 {
cell.title = titles[itemIndex]
} else {
cell.title = ""
}
}
return cell
}
open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let didSelectItemAtIndexPath = lldidSelectItemAtIndex {
didSelectItemAtIndexPath(pageControlIndexWithCurrentCellIndex(index: indexPath.item))
}
}
// MARK: UIScrollViewDelegate
open func scrollViewDidScroll(_ scrollView: UIScrollView) {
if imagePaths.count == 0 { return }
let indexOnPageControl = pageControlIndexWithCurrentCellIndex(index: currentIndex())
if customPageControlStyle == .none || customPageControlStyle == .system {
pageControl?.currentPage = indexOnPageControl
} else {
var progress: CGFloat = 999
// 方向
if scrollDirection == .horizontal {
let currentOffsetX = scrollView.contentOffset.x - (CGFloat(totalItemsCount) * scrollView.frame.size.width) / 2
if currentOffsetX == CGFloat(self.imagePaths.count) * scrollView.frame.size.width && infiniteLoop! {
collectionView.scrollToItem(at: IndexPath.init(item: Int(totalItemsCount/2), section: 0), at: position, animated: false)
}
progress = currentOffsetX / scrollView.frame.size.width
} else if scrollDirection == .vertical {
let currentOffsetY = scrollView.contentOffset.y - (CGFloat(totalItemsCount) * scrollView.frame.size.height) / 2
if currentOffsetY == CGFloat(self.imagePaths.count) * scrollView.frame.size.height && infiniteLoop! {
collectionView.scrollToItem(at: IndexPath.init(item: Int(totalItemsCount/2), section: 0), at: position, animated: false)
}
progress = currentOffsetY / scrollView.frame.size.height
}
if progress == 999 {
progress = CGFloat(indexOnPageControl)
}
// progress
if customPageControlStyle == .fill {
(customPageControl as! LLFilledPageControl).progress = progress
} else if customPageControlStyle == .pill {
(customPageControl as! LLPillPageControl).progress = progress
} else if customPageControlStyle == .snake {
(customPageControl as! LLSnakePageControl).progress = progress
}
}
}
open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
if autoScroll! {
invalidateTimer()
}
}
open func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if autoScroll! {
setupTimer()
}
}
}
|