简单粗暴的轮播图(Banner)开发

久别重逢,继续完成功能模块的介绍.
今天要介绍的功能模块可以说覆盖了绝大多数APP,不信你试试,现在你就谁便打开你的手机应用,然后进入首页,是否看到了一个广告牌,在不停地循环播放,就是它..你没有看错.....轮播图,我喜欢叫他Banner.
闲话不多说😆,好像说的也不少了哈!进入主题.

"三思而后行"----意:做事谨慎,小心稳妥

开发亦是如此,首先我们要思考一下,做这个轮播图的思路:
1.首先,不难发现它是一个滚动视图,那么就要考虑能够提供滚动视图的有哪些类,是使用UITableView呢?,还是UICollectionView呢?其实选择哪个都可以实现轮播图控件实现,今天我要给大家分享的实现方法就是上面提到的这两个类的父类UIScrollView.
2.当然,只有滚动视图还不够还要能够展示图片,所以我们在UIScrollView上还要创建UIImageView用来展示图片,那么问题来了应用要展示的Banner图片数量不定,是不是每个图片都要创建一个UIImageView来展示Banner呢?,这里的处理是本将的"核心",如果每张图片都要创建一个UIImageView内存开销会很大,所以我们要尽可能少的去创建UIImageView,本讲用的三个UIImageView,依次展示最后一张图片,第一张图片,第二张图片具体实现方法下面代码区会有讲解.
3.轮播图...要实现轮播,必须要有一个定时器,每隔几秒去切换下一张图片,因此我们必须要使用Timer这个类来实现轮播效果.
4.除了图片轮播以外每个轮播图的实现还要用到一个控件就是UIPageControl,用来标记当前显示图片是第几张.
5.最后我们要实现的就是点击轮播图时,会有一个响应事件来处理我们点的那张图片.
6.还有一些细节比如:分页标签的位置等等,下面会一一讲到

到这里其实开发思路已经OK了

上面开发思路已经有了,下面👇就是具体的开发
github:下载地址

代码实现及介绍:
创建一个SirSlideshowView类继承UIView
import UIKit
//先导入一个第三方苦,用于加载网络图片
import Kingfisher
创建一个枚举用来设置分页标签的位置
///
/// - center: 中间
/// - left: 左侧
/// - right: 右侧
enum PagePosition {
    case center
    case left
    case right
}
设置协议和协议中的方法

为了实现Banner点击事件

protocol SirSlideshowViewDelegate {
    func ClickBanner(index:Int)
}
下面为SirSlideshowView类中所有的设置的属性和方法,代码注释比较多,相信你一定能看懂!
class SirSlideshowView: UIView {
//设置代理
var delegate : SirSlideshowViewDelegate?

fileprivate var scrollView : UIScrollView?

fileprivate var pageControl : UIPageControl?
//Banner的title
fileprivate var imagetitleLabel : UILabel?

fileprivate var imageTitleView : UIView?

fileprivate var isShow : Bool? = false

fileprivate var timer : Timer?

fileprivate var imageViews = [UIImageView]()

fileprivate var positionPage : PagePosition

/// 是否添加Banner图片标题:默认为false,不添加
var isShowImageTitle : Bool? = false {
    didSet{
        isShow = isShowImageTitle
        setUIForImageTitle()
    }
}

/// Banner图片数组
var images : [Any] = []{
    didSet{
        pageControl?.numberOfPages = images.count
        reloadImage()
    }
}

/// Banner标题数组
var imageTitles : [String] = []{
    didSet{
        reloadImage()
    }
}

/// Banner标题颜色
var imageTitleColor : UIColor = UIColor.black{
    didSet{
        imagetitleLabel?.tintColor = imageTitleColor
    }
}

/// 分页指示器非当前页小点颜色
var  pageIndicatorColor : UIColor = UIColor.white{
    didSet{
        pageControl?.pageIndicatorTintColor = pageIndicatorColor
    }
}

/// 分页指示器当前页小点颜色
var currentPageIndicatorColor : UIColor = UIColor.black{
    didSet{
        pageControl?.currentPageIndicatorTintColor = currentPageIndicatorColor
    }
}


/// 初始化方法
///
/// - Parameters:
///   - frame: Frame
///   - images: Banner图片数组
///   - imageTitles: Banner标题数组
///   - pagePosition: 分页指示器的位置
init(frame: CGRect,images:[Any]? = [],imageTitles:[String]? = [],pagePosition:PagePosition? = .center) {
    if images == nil {
        self.images = [""]
    }else{
        self.images = images ?? []
    }
    self.imageTitles = imageTitles ?? []
    self.positionPage = pagePosition!
    super.init(frame: frame)
    setUpUI()
    addTimer()
}

required init?(coder aDecoder: NSCoder) {
    
    fatalError("init(coder:) has not been implemented")
}

fileprivate func setUpUI() {
    //创建scrollview
    scrollView = UIScrollView(frame: self.bounds)
    scrollView?.delegate = self
    scrollView?.isPagingEnabled = true
    scrollView?.showsHorizontalScrollIndicator = false
    scrollView?.showsVerticalScrollIndicator = false
    self.addSubview(scrollView!)
    //创建3个imageView 用于显示轮播图片,图片依次设置好最后一个,第一个,第二个图片
    for a in 0 ..< 3 {
        let imageView = UIImageView()
        if images.count > 0 {
             self.imageString(imageView:imageView, imageS: "\(images[(a + images.count-1)%images.count])")
        }
        imageView.isUserInteractionEnabled = true
        let tap = UITapGestureRecognizer(target: self, action: #selector(actionClick))
        imageView.addGestureRecognizer(tap)
        scrollView?.addSubview(imageView)
        imageViews.append(imageView)
    }
    //创建分页控制标签
    pageControl = UIPageControl()
    self.addSubview(pageControl!)
    pageControl?.currentPage = 0
    pageControl?.pageIndicatorTintColor = pageIndicatorColor
    pageControl?.currentPageIndicatorTintColor = currentPageIndicatorColor
}


/// 添加timer
fileprivate func addTimer() {
    timer = Timer(timeInterval: 2, repeats: true, block: { [weak self] _ in
        self?.nextImage()
    })
    guard let timer = timer else {
        return
    }
    RunLoop.current.add(timer, forMode: .commonModes)
}

///停止timer,将timer设置为nil才会销毁
fileprivate func removeTimer() {
    timer?.invalidate()
    timer = nil
}


/// 下一个图片
fileprivate func nextImage() {
    if pageControl?.currentPage == images.count - 1 {
        pageControl?.currentPage = 0
    } else {
        pageControl?.currentPage += 1
    }
    let contentOffset = CGPoint(x: self.frame.width*2 , y: 0)
    scrollView?.setContentOffset(contentOffset, animated: true)
}

/// 上一个图片
fileprivate func preImage() {
    if pageControl?.currentPage == 0 {
        pageControl?.currentPage = images.count - 1
    } else {
        pageControl?.currentPage -= 1
    }
    let contentOffset = CGPoint(x: 0, y: 0)
    scrollView?.setContentOffset(contentOffset, animated: true)
}

/// 重新加载图片,设置3个imageView的图片
fileprivate func reloadImage() {
    //通过pageControl当前选中的位置,获取当前轮播到哪一张图片
    let currentIndex = pageControl?.currentPage
    let nextIndex = (currentIndex! + 1) % images.count
    let preIndex = (currentIndex! + images.count-1) % images.count
    
    self.imageString(imageView: (scrollView?.subviews[0] as! UIImageView), imageS: "\(images[preIndex])")
    self.imageString(imageView:(scrollView?.subviews[1] as! UIImageView), imageS: "\(images[currentIndex!])")
    self.imageString(imageView: (scrollView?.subviews[2] as! UIImageView), imageS: "\(images[nextIndex])")
    
    if self.isShow! {
        if currentIndex! > imageTitles.count - 1 {
            imagetitleLabel?.text = ""
        }else{
            imagetitleLabel?.text = imageTitles[currentIndex!]
        }
    }
}

/// 代理方法
@objc fileprivate func actionClick() {
    
    self.delegate?.ClickBanner(index: (pageControl?.currentPage)!)
    
}


/// 设置图片
///
/// - Parameters:
///   - imageView: 要添加图片的ImageView
///   - imageS: image(本地图片或者url)
fileprivate func imageString(imageView:UIImageView, imageS:String){
    
    var header : String?
    
    if  imageS.characters.count >= 4 {
        header = (imageS as NSString).substring(to: 4)
    }
    
    if header == "http" {

        imageView.kf.setImage(with: URL(string: "\(imageS)"), placeholder: UIImage(named: "placeholder")!, options: nil, progressBlock: nil, completionHandler: nil)
    }else{
        
        imageView.image = UIImage(named: "\(imageS )") ?? UIImage(named: "placeholder")!
        
    }
}


fileprivate func setUIForImageTitle(){
    
    if self.isShow! {
        imageTitleView = UIView()
        imageTitleView?.frame = CGRect(x: 0, y: self.frame.height - 30, width: self.frame.width, height: 30)
        imageTitleView?.backgroundColor = .gray
        imageTitleView?.alpha = 0.6
        self.addSubview(imageTitleView!)
        imagetitleLabel = UILabel()
        imagetitleLabel?.tintColor = imageTitleColor
        self.addSubview(imagetitleLabel!)
    }
    
}


override func layoutSubviews() {
    scrollView?.contentSize = CGSize(width: CGFloat(Int(self.frame.width) * images.count), height: self.frame.height)
    scrollView?.contentOffset = CGPoint(x: self.frame.width, y: 0)
    var i = 0
    for imageView in imageViews {
        imageView.frame = CGRect(x: CGFloat(i*Int(self.frame.width)), y: 0, width: self.frame.width, height: self.frame.height)
        i = i+1
    }
    switch self.positionPage {
    case .center:
        pageControl?.frame = CGRect(x: 0, y: self.frame.height - 30, width: self.frame.width/3, height: 30)
        pageControl?.center.x = self.center.x
    case .left:
        pageControl?.frame = CGRect(x: 0, y: self.frame.height - 30, width: self.frame.width/3, height: 30)
         imagetitleLabel?.frame = CGRect(x: self.frame.width/3, y: self.frame.height - 30, width: self.frame.width*2/3, height: 30)
    case .right:
        pageControl?.frame = CGRect(x: self.frame.width/3*2, y: self.frame.height - 30, width: self.frame.width/3, height: 30)
        imagetitleLabel?.frame = CGRect(x: 0, y: self.frame.height - 30, width: self.frame.width*2/3, height: 30)
        
    }
   
}



}

extension SirSlideshowView :UIScrollViewDelegate{

/// 开始滑动的时候,停止timer,将timer设置为nil才会销毁
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    removeTimer()
}
/// 当停止滚动的时候重新设置三个ImageView的内容,然后显示中间那个imageView
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
    reloadImage()
    scrollView.setContentOffset(CGPoint(x: self.frame.width, y: 0), animated: false)
}
/// 停止拖拽,开始timer, 并且判断是显示上一个图片还是下一个图片
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    addTimer()
    if scrollView.contentOffset.x < self.frame.width {
        preImage()
    } else {
        nextImage()
    }
}
}

简单使用如下:

class ViewController: UIViewController,SirSlideshowViewDelegate {

 override func viewDidLoad() {
    super.viewDidLoad()
    self.navigationController?.navigationBar.isTranslucent = false
    let bannerView = SirSlideshowView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 200), images: nil, pagePosition: .right)
    bannerView.isShowImageTitle = true
    bannerView.delegate = self
    bannerView.images = ["test1","test2","http://img.ph.126.net/ocT0cPlMSiTs2BgbZ8bHFw==/631348372762626203.jpg"]
    bannerView.imageTitles = ["起舞飞扬","圣诞快乐","Love"]
    bannerView.imageTitleColor = .red
    bannerView.pageIndicatorColor = UIColor.red
    self.view.addSubview(bannerView)
}

func ClickBanner(index:Int) {
    let test = TestViewController()
    test.title = "Banner\(index)详情"
    self.navigationController?.pushViewController(test, animated: true)
}
}

2oo行代码,搞定轮播图(Banner),简单粗暴的轮播图(Banner)开发大功告成,相信本讲,对读者朋友会有所帮助,如果觉得文章还可以,点击下方👇喜欢鼓励一下,没关注的朋友可以点击一下关注,专题系列讲解持续更新中!

推荐阅读更多精彩内容