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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容