UICollectionView在Swift3.0中的用法

UICollectionView的初始化跟OC中是相似的,创建 GameView 集成自 UICollectionView 。注意不同于UITableView的用法,他需要用 UICollectionViewFlowLayout 来指定一些需要设置的属性,或者可以通过遵守

UICollectionViewDelegateFlowLayout 这个代理来实现。下面我用设置属性的方式来实现的,比较方便。

        //布局
        layout.scrollDirection = .vertical//滑动方向  这个就是默认是纵向滑动
        layout.itemSize = CGSize(width: width, height: width)//item的size
        layout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)//section距上下左右的数据
        layout.minimumLineSpacing = CGFloat(integerLiteral: 5)//如果是纵向滑动的话即行间距,如果是横向滑动则为列间距
        layout.minimumInteritemSpacing = CGFloat(integerLiteral: 5)//如果是纵向滑动的话即列间距,如果是横向滑动则为行间距
        layout.headerReferenceSize = CGSize(width: maiSrc.width, height: 30)//设置headerView的size
        layout.footerReferenceSize = CGSize(width: maiSrc.width, height: 30)//设置footerView的size

下边直接贴出整个的源码 ,调用就异常简单了 :

   override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.view.backgroundColor = UIColor.white()
        
        let game = GameView.init(frame: CGRect(x: 0, y: 20, width: maiSrc.width, height: maiSrc.height - 20))
        self.view.addSubview(game)
        
        
    }

源码 :

//
//  GameView.swift
//  CharacterGame
//
//  Created by 江南花印孓 on 2016/6/28.
//  Copyright © 2016年 恒江. All rights reserved.
//

import UIKit

let identifier = "identifier"
let headerIdentifier = "headerIdentifier"
let footerIdentifier = "footerIdentifier"

class GameView: UICollectionView ,UICollectionViewDelegate ,UICollectionViewDataSource ,UICollectionViewDataSourcePrefetching{
    
    internal var num = 6
    internal var items = 196
    var source = [Int]()
    
    //MARK:---------这里是一个 init 初始化标记
    init(frame:CGRect) {
        //TODO: -----int 初始化方法 里边要做的事情----
        let layout = UICollectionViewFlowLayout.init()//通过Layout的一些属性把对应数据设置好,则不用再实现对应函数
        let leftWidth = maiSrc.width - CGFloat(integerLiteral: ( num + 1 ) * 5)
        let width = leftWidth / CGFloat(integerLiteral: num)
        for i in 1...items {
            source.append(i)
        }
        //FIXME: 问题一 :  除了使用layout属性设置还可以用什么方法来实现?
        //布局
        layout.scrollDirection = .vertical//滑动方向  这个就是默认是纵向滑动
        layout.itemSize = CGSize(width: width, height: width)//item的size
        layout.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)//section距上下左右的数据
        layout.minimumLineSpacing = CGFloat(integerLiteral: 5)//如果是纵向滑动的话即行间距,如果是横向滑动则为列间距
        layout.minimumInteritemSpacing = CGFloat(integerLiteral: 5)//如果是纵向滑动的话即列间距,如果是横向滑动则为行间距
        layout.headerReferenceSize = CGSize(width: maiSrc.width, height: 30)//设置headerView的size
        layout.footerReferenceSize = CGSize(width: maiSrc.width, height: 30)//设置footerView的size
        
        //FIXME: 注意这个父类 init 方法写在这里了
        super.init(frame: frame, collectionViewLayout: layout)
        
        //初始化
        self.backgroundColor = UIColor.white()
        self.delegate = self
        self.dataSource = self
        self.prefetchDataSource = self;//这个协议对应 UICollectionViewDataSourcePrefetching 是 10.0 以后新加的两个方法,下面介绍
        self.isPrefetchingEnabled = true//允许实现 UICollectionViewDataSourcePrefetching 这个协议 10.0 后新加的
        self.allowsMultipleSelection = true//允许多选
        self.showsVerticalScrollIndicator = false //右侧那条上下滑动的线
        
        //这三个方法可以看底层说明,讲的很详细
        self.register(singleCell.self, forCellWithReuseIdentifier: identifier)
        self.register(headerView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: headerIdentifier)
        self.register(footerView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: footerIdentifier)
        
        //FIXME: 一个很实用的系统定义宏 , 用来打印对应object的所有属性信息
        dump(self)

        /*
         *  重排手势 UICollectionViewController 里有一个默认为 true 的属性用来设置开启重排手势功能,实现对应的代理函数即可实现对应功能。
         *  collectionView(_ : , moveItemAt : , to : )
         *  这里集成自 UICollectionView ,没有手势,所以需要添加一个 长按手势 。
         */
        let longGesture = UILongPressGestureRecognizer.init(target: self, action: #selector(handlelongGesture))
        self.addGestureRecognizer(longGesture)
    }
    
   //MARK: ---------------     my  action       ------------
    func handlelongGesture(longGesture:UILongPressGestureRecognizer) -> Void {
        switch longGesture.state {
        case .began:
            //判断手势落点所在 item 的 indexPath 是否在 collectionView 内
            let index = self.indexPathForItem(at: longGesture.location(in: self))
            if index == nil {
                break
            }
            //如果在,那么就开始移动这个位置上的cell
            self.beginInteractiveMovementForItem(at: index!)
            break
        case .changed:
            //当位置发生偏移时调用该函数,判断新的位置是否在 collectionView 以内来决定是放回原位置还是移动到新的位置
            self.updateInteractiveMovementTargetPosition(longGesture.location(in: self))
            print("on the way")
            break
        case .ended:
            /*
             *  接受后调用下边两个函数中的一个通知 collectionView 轨迹交互移动已经结束,把item永久的移动到新位置,同时响应
             *  collectionView(_ : , moveItemAt : , to : ) 函数  来处理数据源  确保数据源的数据结构没问题
             */
            self.endInteractiveMovement()
            break
        default:
            self.cancelInteractiveMovement()
            break
        }
    }
    
    //MARK: --------       UICollection View Delegate      -------
    func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
        return true
    }
    
    /*  注意:cell重置需要在 UICollectionViewController 里开启一个手势 installsStandardGestureForInteractiveMovement 开关
     *  之后由控制器给 collectionView 添加长按手势 LongPressGesture 之后才会走下边的函数
     *  单纯的 collectionView 并没有这个手势,所以无法触发这个代理
     */
    func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        let str = source[sourceIndexPath.item!]
        source.remove(at: sourceIndexPath.item!)
        source.insert(str, at: destinationIndexPath.item!)
        print("this is a test String")
    }
    
    
    override func numberOfSections() -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return items
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! singleCell
        let index = source[indexPath.item!]
        cell.age.text = String.init(index)
        cell.backgroundColor = UIColor.red()
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        
        /**
         *   注意要点
         *   1.supplementaryView 的返回类型为 UICollectionReusableView ,不能混淆,Swift里边对于返回值的类型要去很严格。
         *   2.UICollectionReusableView 继承自 UIView ,是 UIView 的子类
         *   3.kind 分为 footer 和 header 两种,区分开来
         *   4.kind 和 identifier 要对应起来,因为是两个不同的 reuse identifier
         */
        if kind == UICollectionElementKindSectionHeader {
            let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerIdentifier, for: indexPath) as! headerView
            header.name.text = "测试"
            
            return header
        }
        let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerIdentifier, for: indexPath)
        return footer
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        
        let index = indexPath.item! as Int
        print("\(index)")
    }
    
    //MARK: -----------  UICollectionViewDataSourcePrefetching  预取  缓存 -----
    /*  获取 ‘预取’ 地址集合  item出现之前的预处理,iOS 10.0以后新加的,不明觉厉。。不知道有什么用
     *  目前来看 只能是单纯的提升运行效率   做一下预处理。。。
     *  当界面显示不完 item 时 , 类似复用队列一样可以缓存一部分尚未显示出来的 item ,具体缓存多少目前还没搞清楚,
     *  貌似跟几何面积有关,单个item越大,缓存的行数越少;相反,item的size越小,缓存的行数越多
     */
    func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
        //dump(indexPaths)
    }
    /*
     *  optional public func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath])
     *  这个函数的作用看文档说明,个人理解为 已经缓存,事实上却又没有用到,就是没有显示的 item 的子集。
     */
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class headerView: UICollectionReusableView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        self.addSubview(name)
        //单纯的个体案例   用不到三方框架   用 VFL 适配
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[name]-0-|",
                                                           options: [],
                                                           metrics: nil,
                                                           views: ["name":name]))
        
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[name]-0-|",
                                                           options: [],
                                                           metrics: nil,
                                                           views: ["name" : name ]))
        self.backgroundColor = UIColor.orange()
    }
    
    //懒加载
    lazy var name: UILabel = {
        let name = UILabel.init()//2098
        name.translatesAutoresizingMaskIntoConstraints = false
        name.backgroundColor = UIColor.red()//
        name.textAlignment = .center
        name.textColor = UIColor.white()
        return name
    }()
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class footerView: UICollectionReusableView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        self.backgroundColor = UIColor.green()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
class singleCell: UICollectionViewCell {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.addSubview(age)
        
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat:  "H:|-[age]-|",
                                                           options: [],
                                                           metrics: nil,
                                                           views: ["age":age]))
        
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[age]-|",
                                                           options: [],
                                                           metrics: nil,
                                                           views: ["age" : age ]))
    }
    
    lazy var age: UILabel = {
        let age = UILabel.init()
        age.translatesAutoresizingMaskIntoConstraints = false
        age.backgroundColor = UIColor.blue()
        age.textAlignment = .center
        age.textColor = UIColor.white()
        age.text = "0"
        return age
    }()
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}