花落√莫相思iOS 的那些事儿

64-Swift之流水布局(UICollectionView)

2017-08-29  本文已影响58人  NetWork小贱

一 、 流水布局的介绍

在App 的开发中。流水布局是一种显示形式,它类似与生产线的传送带和商品的组合图显示的用户的视野内。流水布局又有人称画廊布局或画廊效果。

二、 本简书的样本例子如下:

4.gif

三 、 如何编写流水布局呢?

我们实现流水布局的控件选择有好多。我们这里选择 UICollectionView 为实现流水布局的底层组件。要使用 UICollectionView 来实现流水布局,我们就要重新定义 UICollectionView 的布局文件。我们要继承 UICollectionViewFlowLayout ,并且要重写 UICollectionViewFlowLayout 类的几个方法。如下:

四、 流水布局重新定义和一些方法的重新

1、 override func prepare() 的重写

// MARK: 布局初始化
override func prepare() {
    super.prepare()
    // 设置Cell的位置
    let marginX = ( (self.collectionView?.frame.size.width)! - self.itemSize.width ) * 0.5
    self.collectionView?.contentInset = UIEdgeInsets.init(top: 0, left: marginX, bottom: 0, right: marginX)
}

2 、 func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) 的方法的重写

// MARK: 当CollectionView的显示范围发生变化的时候,是否需要重新布局。如果重新布局就会调用 1. func prepare()  2. override func layoutAttributesForElements(in rect: CGRect) 两个方法
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
     return true
}

3、 func layoutAttributesForElements(in rect: CGRect) 的重写

// MARK: 返回设置好的布局数组
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    // 创建系统默认的 UICollectionViewLayoutAttributes
    let DefaultAttributes = super.layoutAttributesForElements(in: rect)
    // 计算Item在UICollectionView上的 X 轴的大小
    let CollectionViewCenterX = (self.collectionView?.contentOffset.x)! + (self.collectionView?.frame.width)! * 0.5
    for item in DefaultAttributes! {
        // 计算Cell的中心点距离CollectionView的中心点的距离(如果Cell在中间,我们看到的就是不缩放的效果)
        let Delta = abs(item.center.x - CollectionViewCenterX)
        // 计算Cell 的缩放值
        let Scal = 1.0 - Delta / (self.collectionView?.frame.width)!
        // 设置Cell 滚动时的缩放比例
        item.transform = CGAffineTransform(scaleX: Scal, y: Scal)
    }
    return DefaultAttributes
}

4、 func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint 方法的重写

// MARK: 保持Cell每次滑动后停止在UICollectionView的中间
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
    // 计算展示Cell的矩形框大小
    var rect = CGRect.init()
    rect.origin.y = 0
    rect.origin.x = proposedContentOffset.x
    rect.size = (self.collectionView?.frame.size)!
    
    var ContentOffset = proposedContentOffset.x
    // 计算CollectionView 中点Cell的偏移的 X 坐标值
    let CollectionViewCenterX = proposedContentOffset.x + (self.collectionView?.frame.width)! * 0.5
    
    var minDelta = MAXFLOAT
    for item:UICollectionViewLayoutAttributes in super.layoutAttributesForElements(in: rect)!{
        if CGFloat(abs(minDelta)) > abs(item.center.x - CollectionViewCenterX) {
            minDelta = Float(item.center.x - CollectionViewCenterX)
        }
    }
    ContentOffset += CGFloat(minDelta)
    return CGPoint.init(x: ContentOffset, y: proposedContentOffset.y)
}

五、 流水布局的使用

1、 创建UICollectionView对象

// MARK: 创建UICollectionView对象
func createCollectionView() -> Void{
    // TODO: 创建布局对象
    let FlowingwaterLayout = FlowingwaterCollectionViewLayout.init()
    // TDOD: 设置方向
    FlowingwaterLayout.scrollDirection = .horizontal
    FlowingwaterLayout.itemSize = CGSize.init(width: 200, height: 380)
    
    let FlowingwaterCollectionView = UICollectionView.init(frame: CGRect.init(x: 0, y: 100, width: view.frame.width, height: 400), collectionViewLayout: FlowingwaterLayout)
    // TODO: 设置代理
    FlowingwaterCollectionView.delegate = self
    FlowingwaterCollectionView.dataSource = self
    // TODO: 渲染到视图
    self.view.addSubview(FlowingwaterCollectionView)
    
    // TODO: 注册Cell
    FlowingwaterCollectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "NetWork小贱")
    
}

2、 UICollectionViewDelegate / UICollectionViewDataSource / UICollectionViewFlowLayout 的代理实现

// MARK: UICollectionViewDelegate / UICollectionViewDataSource / UICollectionViewFlowLayout 的代理实现

// TODO: 返回Cell的个数
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return dataArray.count
}

// TODO: 创建Cell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    // 获取Cell
    let Cell = collectionView.dequeueReusableCell(withReuseIdentifier: "NetWork小贱", for: indexPath)
    Cell.contentView.backgroundColor = UIColor.white
    for item in Cell.contentView.subviews {
        item.removeFromSuperview()
    }
    // 图像
    let imageV = UIImageView.init(frame: CGRect.init(x: 0, y: 0, width: Cell.bounds.width, height: Cell.bounds.height - 60))
    imageV.contentMode = .scaleToFill
    imageV.image = UIImage.init(named:(dataArray![indexPath.row] as! NSDictionary)["image"] as! String)
    Cell.contentView.addSubview(imageV)
    
    // 标题
    let titleL = UILabel.init(frame: CGRect.init(x: 5, y: imageV.frame.maxY + 5, width: Cell.bounds.width - 10, height: 30))
    titleL.font = UIFont.systemFont(ofSize: 18)
    titleL.text = ((dataArray![indexPath.row] as! NSDictionary)["title"] as! String)
    Cell.contentView.addSubview(titleL)
    
    // 售价
    let priceL = UILabel.init(frame: CGRect.init(x: 5, y: titleL.frame.maxY - 5, width: Cell.bounds.width - 10, height: 30))
    priceL.textColor = UIColor.red
    priceL.font = UIFont.boldSystemFont(ofSize: 17)
    priceL.text = String.init(format: "售价:¥ %@", (dataArray![indexPath.row] as! NSDictionary)["price"] as! String)
    Cell.contentView.addSubview(priceL)
    
    return Cell
}
// TODO: 选择哪个Cell
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    
}

3、 数据加载

// MARK: 获取数据
func loadData() -> Void {
    let path = Bundle.main.path(forResource: "data", ofType: "plist")
    dataArray = NSArray.init(contentsOfFile: path!)
}
上一篇下一篇

猜你喜欢

热点阅读