iOS简易轮播图的实现

前言

见过很多的iOS应用,都有无限轮播图,之前也看到过很多相关实现的文章,可是仅仅就是一看而过罢了。当自己在项目中用到这个,真正去写的时候才发现不是很容易,遇到了一些问题,这篇文章就记录下自己最后成功实现轮播图的历程。

原理

原理其实就是一种假象。不过在我这里,目前有过两种原理的实现。一种利用UICollectionView实现轮播,另一种利用UIScrollView实现轮播。
今天我们要来打造一款这样的轮播图(图画的不好,请勿见怪)。

举例:三张图的无限轮播

原理一:利用UIScrollView实现轮播图。

要想实现这样的效果,我们使用UIScrollView包含image0,image1,image2的三个UIImageView都是放在UIScrollView里面的,但是这样的话是不可以实现轮播的。要想实现轮播就要再在该UIScrollView的左右两侧各加一张UIImageView。在最后一张放image0, 第一张放image2。这样的话当我们滚动到最后或者最前面的的时候,我们就把列表切换到相应的位置。

实际UISrollView图

原理二:利用UICollectionView实现轮播图。

你当然可以利用原理一再用UICollectionView实现一次。但是我这里采用一种偷懒的方式,利用UICollectionView的cell重用机制。创建个无数个cell,比如3x20000。当用户第一次进来的我们就将UICollectionView滑到3x20000x0.5的位置。然后利用cell.indexPath.item % 3来对cell上的UIImageView进行image显示。这种方式比较偷懒,在后来添加NSTimer自动轮播和点击事件的时候也比较简单,不过有点大炮打小鸟的感觉。所以我在这里也只是介绍下原理,如果你有兴趣,可以自己尝试去实现下。而且UICollectionView自带一个方法- scrollToItemAtIndexPath:atScrollPosition:animated:还比较厉害。

具体实现

针对原理一,我就来简单实现下。(我这里使用代码创建,你也可以使用Storyboard创建,原理一样)

创建工程,创建BannerView.Swift,添加初始化代码如下:

/// 初始化scrollView
lazy var scrollView: UIScrollView = {
       let scrollView = UIScrollView()
       scrollView.delegate = self
       scrollView.alwaysBounceVertical = false
       scrollView.bounces = true
       scrollView.pagingEnabled = true
       scrollView.scrollEnabled = true
       scrollView.showsVerticalScrollIndicator = false
       scrollView.showsHorizontalScrollIndicator = false
       return scrollView
   }()
/// 初始化pageControl
   lazy var pageControl: UIPageControl = {
       let pageControl = UIPageControl()
       pageControl.currentPageIndicatorTintColor = UIColor.redColor()
       pageControl.pageIndicatorTintColor = UIColor.whiteColor()
       return pageControl
   }()

   /// 在init里调用setupSubviews即可
   func setupSubviews() {  
       self.addSubview(scrollView)
       self.addSubview(pageControl)

       scrollView.translatesAutoresizingMaskIntoConstraints = false
       pageControl.translatesAutoresizingMaskIntoConstraints = false

       self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[v]|", options: [], metrics: nil, views: ["v": scrollView]))
       self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[v]|", options: [], metrics: nil, views: ["v": scrollView]))

       self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[p(==20)]-10-|", options: [], metrics: nil, views: ["p": pageControl]))
       self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[p(==100)]-10-|", options: [], metrics: nil, views: ["p": pageControl]))
   }

初始化UIScrollView:

func setupScrollView() {
        /// 创建 (imageNames.count + 2)个UIImageView
        for index in 0 ... (imageNmaes.count + 1) {
            let imageView = UIImageView(frame: CGRect(x: self.width*CGFloat(index), y: 0, width: self.width, height: self.height))
            var picName = String()
            switch index {
            case 0:
                picName = imageNames[imageNames.count-1]
                break
            case imageNames.count + 1:
                picName = imageNames[0]
                break
            default:
                picName = imageNames[index-1]
                break
            }

//          imageView.af_setImageWithURL(NSURL(string: picName)!)
            imageView.image = UIImage(named: picName)
            imageView.contentMode = .ScaleToFill
            imageView.clipsToBounds = true
            imageView.userInteractionEnabled = true
            scrollView.addSubview(imageView)
        }
        scrollView.contentSize = CGSize(width: self.width * CGFloat(imageNames.count + 2), height: self.height)
        scrollView.contentOffset = CGPoint(x: self.width, y: 0)
    }

无限轮番的实现。(重点)使用UIScrollViewDelegate协议。

func scrollViewDidScroll(scrollView: UIScrollView) {
       if scrollView == self.scrollView {
           let contentOffsetX = scrollView.contentOffset.x
          ///向前滑动到第0张图的时候,滚到第三张图
           if contentOffsetX == 0 {
               scrollView.contentOffset = CGPoint(x: self.width * CGFloat(self.imageNames.count), y: 0)
           }
           /// 跑到第三张图的时候,滚到第0张图
           if contentOffsetX == CGFloat(self.imageNames.count + 1)*self.width {
               scrollView.contentOffset = CGPoint(x: self.width, y: 0)
           }
           /// 改变pageControl,这里要注意,一定要一定要一定要 - 0.5, 因为最前面的第0张图一直不显示。
           let index = scrollView.contentOffset.x / self.width - 0.5
           self.pageControl.currentPage = Int(index)
       }
   }

添加计时器

好了,做到这里,你已经成功实现了轮播图,可是这个轮播图是手动的,下面添加自动滚动。也很简单,设置一个NSTimer计时器即可。代码如下:

// MARK: - Timer
   var timer: NSTimer?
   var i = 1
   /// 在scrollView上的图片成功设置后调用startTimer即可
   private func startTimer() {
       if timer != nil {
           self.stopTimer()
       }
       /// 这里设置的滚动间隔为2秒
       timer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: #selector(autoScroll), userInfo: nil, repeats: true)
       NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSDefaultRunLoopMode)
   }
   private func stopTimer() {
       timer?.invalidate()
       timer = nil
   }
   func autoScroll() {
       if i == imageNames.count + 1 {
           i = 1
       }
       i += 1
       /// 在处理无限轮番的时候设置contentOffset没有添加animated,这里要添加上来,不然有种很突兀的感觉。
       scrollView.setContentOffset(CGPoint(x: self.width * CGFloat(i), y: 0), animated: true)
   }
    /// 手动滚动结束后恢复计时
   func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        self.startTimer()
    }
    /// 当手动滚动开始时停止计时
    func scrollViewWillBeginDragging(scrollView: UIScrollView) {
        self.stopTimer()
    }

添加具体点击事件

具体到点击事件的话,我是放在初始化imageView那里处理的,给imageVIew添加一个UITapGestureRecognizer,在这里最好再写一个代理方法,将imageView的点击事件传递出去。然后根据当前contentOffet.x / self.width来将具体哪张图片的事件传递出去。我就不用代码展示了。

结尾

还是比较简单的。但是我这个复用性比较差,封装的也不好,可是具体要看思路😂。代码实现放在了Github Wiki上了BannerView.Swift,由于工程量太小,就没将工程文件上传了。

关于作者

Qiuncheng,一位正在iOS路上前行的初学者,正在努力成为一名合格的iOS开发者。

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • 只有电影里才会等你四五年,感情,就是不联系就会没有的东西
    周曌阅读 79评论 0 0
  • 曾经不知道多少次,我们违背自己的初心,去做那件令自己窒息的事;曾经不知道多少回,我们为了迎合大众的口味,去不断...
    梅骨赏阅读 202评论 0 1