iOS开发:一个无限滚动自动播放图片的Demo(Swift语言编码)

很久以前就想写这么一个无限滚动的Demo了,最近学习了下Swift,手中没有可以用来练手的Demo,所以才将它实现了。

Github地址(由于使用了UIView+AutoLayout第三方进行布局,所以Github判断主要语言是OC):https://github.com/wzpziyi1/DisplayingImage

使用UICollectionView来实现的,不同于UIScrollView实现的一点是,就是不需要再手动实现缓存池功能,因为UICollectionView中的cell本就是循环利用的,我只是需要处理好无限滚动以及定时器的移除与添加即可。

这里需要注意下,MJExtension框架,并不能完美支持Swift,我在编写的过程中,利用它解析一个plist文件,并未成功,然后自己写了个KVC解析的,希望MJ老师尽快将它升级为Swift版本吧。

示例图片:

在编写过程中,遇到的问题还是比较多的,是因为我不是那么熟悉Swift导致的,个人觉得Swift现在最不好的一点,就是还没有一个统一的规范。我是从OC转Swift的,所以代码中基本是使用OC的那一套规范,但是从其他语言转过来的,可能就会有很大差异了。

    1. 这是一个从plist里面读取数据,并将之存储到数组里面的代码:

      class ZYNew: NSObject {
          var icon: String!
          var title: String!
      
          init(dict: Dictionary<String, String>) {
              super.init()
              self.setValuesForKeysWithDictionary(dict)
          }
      
          class func getNews() -> Array<ZYNew>
          {
              let path = NSBundle.mainBundle().pathForResource("newses.plist", ofType: nil)
              let originArray: NSArray? = NSArray(contentsOfFile: path!)
              var news = Array<ZYNew>()
              originArray?.enumerateObjectsUsingBlock({ (obj: AnyObject, index: Int, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
                  let tmp = ZYNew(dict: obj as! Dictionary<String, String>)
                  news.append(tmp)
              })
              return news
          }
      }
      

      这里有个极为坑的地方,Swift中的Array类型竟然没有contentsOfFile方法,也就是说,使用Array是不能从一个文件路径中读取一个plist,然后将之转化为数组的。好吧,没有那就算了,反正也是可以使用NSArray,那么我用NSArray转化就好,然后就掉进坑里了。
      在Swift里面,OC中的NSArray\NSDictionary等里面装的对象它是全部将之认为是anyObject类型的,这就意味着,你在将NSArray强制转化为Array<ZYNew>的时候,是错误的。比如说,在上面的代码,我就使用如下代码强制转化过:

      let originArray: NSArray? = NSArray(contentsOfFile: path!)
               let news: Array<ZYNew> = originArray as! [ZYNew]
      这样转,在这里是错误的,因为anyObject的真正类型是字典,然后我修改成上面的代码了。

    2. 自定义UICollectionViewCell的代码:

      import UIKit
      
      class ZYNewCell: UICollectionViewCell {
      
          //Mark:- 存储属性
          var new: ZYNew? {
              didSet{
      //            print(new?.icon)
                  self.imageView.image = UIImage(named: (new?.icon)!)
                  self.titleLabel.text = new?.title
              }
          }
          //MARK:- 计算属性
      
          //MARK:- UI属性
          private weak var imageView: UIImageView!
          private weak var titleLabel: UILabel!
      
          override init(frame: CGRect) {
              super.init(frame: frame)
              self.commitInit()
          }
      
          required init?(coder aDecoder: NSCoder) {
              super.init(coder: aDecoder)
              self.commitInit()
          }
      
          private func commitInit()
          {
              let imageView = UIImageView()
              imageView.contentMode = UIViewContentMode.ScaleAspectFill
              imageView.clipsToBounds = true
              self.addSubview(imageView)
              self.imageView = imageView
      
              let titleLabel = UILabel()
              titleLabel.textAlignment = NSTextAlignment.Center
              titleLabel.textColor = UIColor.whiteColor()
              self.addSubview(titleLabel)
              self.titleLabel = titleLabel
          }
      
          override func layoutSubviews() {
              super.layoutSubviews()
      
              self.imageView.autoPinEdgesToSuperviewEdgesWithInsets(UIEdgeInsetsZero)
              self.titleLabel.autoPinEdgesToSuperviewEdgesWithInsets(UIEdgeInsetsZero, excludingEdge: ALEdge.Bottom)
              self.titleLabel.autoSetDimension(ALDimension.Height, toSize: 30)
          }
      }
      

      这里倒是编写得很顺利,由于OC的编码习惯,我喜欢将类的属性进行分层,这样方便我后期开发中快速查找问题所在,代码看过去也是一目了然。

      Swift中的属性有两种:
              存储属性:它有willSet和didSet方法
              计算属性:它有set和get方法
      感觉就是对应OC中的getter和setter方法,可以进行重写。

    3. 主要代码ZYImageDisplayingView:

      import UIKit
      
      class ZYImageDisplayingView: UIView, UICollectionViewDelegate, UICollectionViewDataSource {
      
          //MARK:- 常量
          private let identifier = "ZYNewCell"
      
          //MARK:- 存储属性
          override var frame: CGRect{
              didSet{
                  if (self.collectionView != nil) {
                      self.collectionView?.removeFromSuperview()
                  }
      
                  if (frame.width == 0.0 && frame.height == 0.0 && frame.origin.x == 0.0 && frame.origin.y == 0.0) {
                      return
                  }
      
                  let layout = UICollectionViewFlowLayout()
                  layout.itemSize = frame.size
                  layout.scrollDirection = UICollectionViewScrollDirection.Horizontal
                  layout.minimumLineSpacing = 0
                  let collectionView = UICollectionView(frame: CGRectMake(0, 0, frame.width, frame.height), collectionViewLayout: layout)
                  collectionView.registerClass(ZYNewCell.self, forCellWithReuseIdentifier: identifier)
                  collectionView.showsHorizontalScrollIndicator = false
                  self.addSubview(collectionView)
                  self.collectionView = collectionView
      
                  self.collectionView!.delegate = self
                  self.collectionView!.dataSource = self
                  self.collectionView!.backgroundColor = UIColor.whiteColor()
                  self.collectionView!.pagingEnabled = true
      
                  self.collectionView!.scrollToItemAtIndexPath(NSIndexPath(forItem: 0, inSection: ZYImageGroups / 2), atScrollPosition: UICollectionViewScrollPosition.None, animated: false)
      
                  self.bringSubviewToFront(pageControl)
                  self.addTimer()
              }
          }
      
          var news = ZYNew.getNews()
      
          var timer: NSTimer?
      
          //MARK:- 计算属性
      
          //MARK:- UI控件
          weak var collectionView: UICollectionView?
          weak var pageControl: UIPageControl!
      
          //MARK:- 初始化方法
          override init(frame: CGRect) {
              super.init(frame: frame)
              self.commitInit()
          }
      
          required init?(coder aDecoder: NSCoder) {
              super.init(coder: aDecoder)
              self.commitInit()
      
          }
      
          private func commitInit(){
              self.backgroundColor = UIColor.yellowColor()
              var pageControl = UIPageControl()
              pageControl.numberOfPages = self.news.count
              pageControl.pageIndicatorTintColor = UIColor.redColor()
              pageControl.currentPageIndicatorTintColor = UIColor.whiteColor()
              self.addSubview(pageControl)
              self.pageControl = pageControl
      
          }
      
          //MARK:- UICollectionViewDataSource
          func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
              return ZYImageGroups
          }
      
          func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
              return self.news.count
          }
      
          func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
      
              var cell:ZYNewCell? = collectionView.dequeueReusableCellWithReuseIdentifier(self.identifier, forIndexPath: indexPath) as? ZYNewCell
              if (cell == nil) {
                  cell = ZYNewCell()
              }
      //        print(self.news[indexPath.row])
              cell?.new = self.news[indexPath.row]
              return cell!
          }
      
          //MARK:- UIScrollViewDelegate
          func scrollViewWillBeginDragging(scrollView: UIScrollView) {
              self.removeTimer()
          }
      
          //当scrollView减速完毕时调用,最好是在这个时候添加定时器
          func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
              self.addTimer()
          }
      
          func scrollViewDidScroll(scrollView: UIScrollView) {
              let size = scrollView.contentOffset
      //        print(size)
              self.pageControl.currentPage = Int(size.x / (self.collectionView?.frame.width)! + 0.5) % self.news.count
          }
      
          //MARK:- 定时器处理
          func addTimer()
          {
              self.removeTimer()
              self.timer = NSTimer(timeInterval: 2, target: self, selector: Selector("updateTimer"), userInfo: nil, repeats: true)
              NSRunLoop.mainRunLoop().addTimer(self.timer!, forMode: NSRunLoopCommonModes)
          }
      
          func removeTimer()
          {
              self.timer?.invalidate()
              self.timer = nil
          }
      
          func updateTimer()
          {
              let currentIndexPath = self.resetIndexPath()
      
              var section = currentIndexPath.section
              var row = currentIndexPath.row + 1
      
              if (row == self.news.count) {
                  row = 0
                  section++
              }
              self.collectionView?.scrollToItemAtIndexPath(NSIndexPath(forItem: row, inSection: section), atScrollPosition: UICollectionViewScrollPosition.None, animated: true)
          }
      
          func resetIndexPath() -> NSIndexPath
          {
              let currentIndexPath = self.collectionView?.indexPathsForVisibleItems().first
      
              self.collectionView?.scrollToItemAtIndexPath(NSIndexPath(forItem: (currentIndexPath?.row)!, inSection: ZYImageGroups / 2), atScrollPosition: UICollectionViewScrollPosition.None, animated: false)
              return NSIndexPath(forItem: (currentIndexPath?.row)!, inSection: ZYImageGroups / 2)
          }
      
          override func layoutSubviews() {
              super.layoutSubviews()
              self.pageControl.autoPinEdgeToSuperviewEdge(ALEdge.Bottom, withInset: 8)
              self.pageControl.autoPinEdgeToSuperviewEdge(ALEdge.Right, withInset: 20)
              self.pageControl.autoSetDimensionsToSize(CGSizeMake(100, 20))
          }
      }
      

      这部分代码更多的是逻辑处理吧。plist里面只有5个具体的model,我是假设collectionViewCell有100组(也就是section等于100),每一组有5行(也就是row等于5),每一次要滚动到下一个cell的时候,我会先让它滚动到sectio等于50的地方,然后row不变,在开始滚动到下一张,也就是row+1,如果row超过plist中model的个数,那么相应的,section++,row清零。

      这里值得说的一点的是,我是重写了父类的frame属性,当ZYImageDisplayingView的frame发生改变的时候,就会触发这个属性的didSet方法,我在这个方法里面初始化了UICollectionView,并做了相应的设置。个人认为这里写的不是很好,我不应该在这个方法里面初始化UICollectionView,这要有这样几点考虑:
              如果我是用autoLayout来布局这个控件,是不会触发frame的didSet方法的。
              如果我需要更改ZYImageDisplayingView的位置,那么为了避免重复创建UICollectionView,
              我必须先把以前创建的UICollectionView移除,再
              创建新的collectionView。

      其他的,OC中的[ZYImageDisplayingView class]对应Swift中的ZYImageDisplayingView.self
      OC中的#pragma mark 对应Swift中的 //MARK:-

    4. ViewController里面的代码:

      import UIKit
      
      class ViewController: UIViewController {
      
          weak var displayingView: ZYImageDisplayingView!
          override func viewDidLoad() {
              super.viewDidLoad()
              // Do any additional setup after loading the view, typically from a nib.
      
              self.view.backgroundColor = UIColor.whiteColor()
      
              let displayingView = ZYImageDisplayingView()
              self.view.addSubview(displayingView)
              self.displayingView = displayingView
              self.displayingView.frame = CGRectMake(50, 100, 300, 130)
      //        self.displayingView.autoPinEdgeToSuperviewEdge(ALEdge.Left, withInset: 50)
      //        self.displayingView.autoPinEdgeToSuperviewEdge(ALEdge.Top, withInset: 100)
      //        self.displayingView.autoSetDimensionsToSize(CGSizeMake(300, 130))
          }
      
      }
      

      这里需要注意的是,不要使用autoLayout对displayingView进行布局,否则会导致collectionView的frame为CGRectZero。

      当然,这是我的Swift还不怎么熟练所导致的,后续会进行一定的更改。

时间: 2024-08-13 22:58:12

iOS开发:一个无限滚动自动播放图片的Demo(Swift语言编码)的相关文章

【iOS开发每日小笔记(六)】Swift语言学习的入门随想

这篇文章是我的[iOS开发每日小笔记]系列中的一片,记录的是今天在开发工作中遇到的,可以用很短的文章或很小的demo演示解释出来的小心得小技巧.该分类的文章,内容涉及的知识点可能是很简单的.或是用很短代码片段就能实现的,但在我看来它们可能会给用户体验.代码效率得到一些提升,或是之前自己没有接触过的技术,很开心的学到了,放在这里得瑟一下.其实,90%的作用是帮助自己回顾.记忆.复习.如果看官觉得太easy,太碎片,则可以有两个选择:1,移步[iOS探究]分类,对那里的文章进行斧正:2,在本文的评论

IOS开发系列--无限循环的图片浏览器

--UIKit之UIScrollView 概述 UIKit框架中有大量的控件供开发者使用,在iOS开发中不仅可以直接使用这些控件还可以在这些控件的基础上进行扩展打造自己的控件.在这个系列中如果每个控件都介绍一遍确实没有必要,所谓授人以鱼不如授人以渔,这里会尽可能让大家明白其中的原理,找一些典型的控件进行说明,这样一来大家就可以触类旁通.今天我们主要来看一下UIScrollView的内容: UIView UIScrollView 实战--图片浏览器 UIView 在熟悉UIScrollView之前

用原生的javascript 实现一个无限滚动的轮播图

说一下思路:和我上一篇博客中用JQ去写的轮播图有相同点和不同点 相同点: 首先页面布局是一样的 同样是改变.inner盒子的位置去显示不同的图片 不同点: 为了实现无限滚动需要多添加两张重复的图片 左右切换和前面的方法有所不同,前面是获取当前的索引值乘以-600px当做位移距离,现在是需要获取当前.inner的位置来加上或者减去-600来实现 下面来一步步的去实现轮播图: 首先是html <!DOCTYPE html> <html lang="en"> <

iOS开发之视差滚动视图

首先声明一点,由于自己iOS开发经验有限,这里给下面将要实现的效果起名叫视差滚动视图,自己也不知道是否严谨,等以后有经验了,再来更新吧. 一.需求 有的时候我们可能会有这样一种需求,在一个UITableView的上方放置一个View(为了下面实现方便,这里就叫TopView吧),想要实现的效果是,当滚动UITableView时,让TopView也一起向上滚动:当TopView滚动到一定位置时,不再继续滚动TopView,而只是滚动UITableView. 二.思路 1.开始时的思路是这样的,因为

iOS开发-- 一个苹果证书如何多次使用

苹果的开发者账号限制开发者证书只能有5个,我们开发过程中遇到超过5个人需要真机调试的情况,如何解决这个问题呢? 有两种方式可以解决问题: 1. Revoke原来的证书----不推荐 将以前的证书“revoke”掉,然后重新生成一个新的证书. 这种方法是可以的,但是会造成相应的Provisioning Profiles失效,这是小问题.但是又要重新申请证书甚至描述文件很浪费时间,所以不提倡这种做法. 2. p12----推荐 我们的每一个证书都可以生成一个.p12文件,这个文件是一个加密的文件,只

iOS 开发之头部滚动展示视图(转)

// //  RootViewController.m //  头部滚动展示视图 //  头部滚动广告视图 #define SCREEN_SIZE [UIScreen mainScreen].bounds.size #define KImageCnt 5 #define KImage_H  250 #import "RootViewController.h" @interface RootViewController ()<UIScrollViewDelegate> @pr

iOS 开发之头部滚动展示视图

效果: // //  RootViewController.m //  头部滚动展示视图 // //  Created by 寒竹子 on 15/4/1. //  Copyright (c) 2015年 摩天居士. All rights reserved. //  头部滚动广告视图 #define SCREEN_SIZE [UIScreen mainScreen].bounds.size #define KImageCnt 5 #define KImage_H  250 #import "Roo

ios开发--一个苹果证书怎么多次使用——导出p12文件

为什么要导出.p12文件 当我们用大于三个mac设备开发应用时,想要申请新的证书,如果在我们的证书里,包含了3个发布证书,2个开发证书,可以发现再也申请不了开发证书和发布证书了(一般在我们的证书界面中应该只有一个开发证书,一个发布证书,没必要生成那么多的证书,证书一般在过期之后才会重新添加.) 如图: 这时候,再点击“+”时,就会发现点击不了开发和发布证书,也就是添加不了开发证书和发布证书了: 有两个解决不能添加证书的办法. 第一种方法——“revoke”(不推荐): 将以前的证书“revoke

IOS开发--一个控件添加后看不见 有哪些可能。

一个控件用肉眼看不见,有哪些可能 1.根本没有创建实例化这个控件 2.没有设置尺寸 3.控件的颜色跟父控件的背景色一样(实际上已经显示了,只不过用肉眼看不见) 4.透明度alpha <= 0.01 5.hidden = YES 6.没有添加到父控件中 7.被其他控件挡住了 8.位置不对 9.父控件发生了以上情况 10.特殊情况 * UIImageView没有设置image属性,或者设置的图片名不对 * UILabel没有设置文字,或者文字颜色和跟父控件的背景色一样 * UITextField没有