Swift - 使用网格(UICollectionView)的自定义布局实现复杂页面
在之前的文章中:Swift - 使用网格(UICollectionView)进行流布局,我简单了介绍了UICollectionView 的使用。
网格UICollectionView除了使用流布局,还可以使用自定义布局。实现自定义布局需要继承UICollectionViewLayout,同时还要重载下面的三个方法:
// 内容区域总大小,不是可见区域 override var collectionViewContentSize: CGSize { } // 所有单元格位置属性 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { } // 这个方法返回每个单元格的位置和大小 override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { }
下面实现一个自定义布局的例子,单元格有大小两种。网格从上到下,先是左边一个大单元格右边两个小单元格,接着左边两个小单元格右边一个大单元格,依次同上循环排列。
效果图如下:
--- 自定义布局 CustomLayout.swift ---
(本文样例代码已升级到Swift3)
import UIKit /** * 这个类只简单定义了一个section的布局 */ class CustomLayout : UICollectionViewLayout { // 内容区域总大小,不是可见区域 override var collectionViewContentSize: CGSize { let width = collectionView!.bounds.size.width - collectionView!.contentInset.left - collectionView!.contentInset.right let height = CGFloat((collectionView!.numberOfItems(inSection: 0) + 1) / 3) * (width / 3 * 2) return CGSize(width: width, height: height) } // 所有单元格位置属性 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var attributesArray = [UICollectionViewLayoutAttributes]() let cellCount = self.collectionView!.numberOfItems(inSection: 0) for i in 0..<cellCount { let indexPath = IndexPath(item:i, section:0) let attributes = self.layoutAttributesForItem(at: indexPath) attributesArray.append(attributes!) } return attributesArray } // 这个方法返回每个单元格的位置和大小 override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { //当前单元格布局属性 let attribute = UICollectionViewLayoutAttributes(forCellWith:indexPath) //单元格边长 let largeCellSide = collectionViewContentSize.width / 3 * 2 let smallCellSide = collectionViewContentSize.width / 3 //当前行数,每行显示3个图片,1大2小 let line:Int = indexPath.item / 3 //当前行的Y坐标 let lineOriginY = largeCellSide * CGFloat(line) //右侧单元格X坐标,这里按左右对齐,所以中间空隙大 let rightLargeX = collectionViewContentSize.width - largeCellSide let rightSmallX = collectionViewContentSize.width - smallCellSide // 每行2个图片,2行循环一次,一共6种位置 if (indexPath.item % 6 == 0) { attribute.frame = CGRect(x:0, y:lineOriginY, width:largeCellSide, height:largeCellSide) } else if (indexPath.item % 6 == 1) { attribute.frame = CGRect(x:rightSmallX, y:lineOriginY, width:smallCellSide, height:smallCellSide) } else if (indexPath.item % 6 == 2) { attribute.frame = CGRect(x:rightSmallX, y:lineOriginY + smallCellSide, width:smallCellSide, height:smallCellSide) } else if (indexPath.item % 6 == 3) { attribute.frame = CGRect(x:0, y:lineOriginY, width:smallCellSide, height:smallCellSide ) } else if (indexPath.item % 6 == 4) { attribute.frame = CGRect(x:0, y:lineOriginY + smallCellSide, width:smallCellSide, height:smallCellSide) } else if (indexPath.item % 6 == 5) { attribute.frame = CGRect(x:rightLargeX, y:lineOriginY, width:largeCellSide, height:largeCellSide) } return attribute } /* //如果有页眉、页脚或者背景,可以用下面的方法实现更多效果 func layoutAttributesForSupplementaryViewOfKind(elementKind: String!, atIndexPath indexPath: NSIndexPath!) -> UICollectionViewLayoutAttributes! func layoutAttributesForDecorationViewOfKind(elementKind: String!, atIndexPath indexPath: NSIndexPath!) -> UICollectionViewLayoutAttributes! */ }
--- 主页面 ViewController.swift ---
import UIKit class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { var collectionView:UICollectionView! //课程名称和图片,每一门课程用字典来表示 let courses = [ ["name":"Swift","pic":"swift.png"], ["name":"Xcode","pic":"xcode.png"], ["name":"Java","pic":"java.png"], ["name":"PHP","pic":"php.png"], ["name":"JS","pic":"js.png"], ["name":"React","pic":"react.png"], ["name":"Ruby","pic":"ruby.png"], ["name":"HTML","pic":"html.png"], ["name":"C#","pic":"c#.png"] ] override func viewDidLoad() { super.viewDidLoad() let layout = CustomLayout() //let layout = UICollectionViewFlowLayout() let frame = CGRect(x:0, y:20, width: view.bounds.size.width, height:view.bounds.height-20) self.collectionView = UICollectionView(frame: frame, collectionViewLayout:layout) self.collectionView.delegate = self self.collectionView.dataSource = self // 注册CollectionViewCell self.collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "ViewCell") //默认背景是黑色和label一致 self.collectionView.backgroundColor = UIColor.white //设置collectionView的内边距 self.collectionView.contentInset = UIEdgeInsetsMake(0, 5, 0, 5) self.view.addSubview(collectionView) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } // CollectionView行数 func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return courses.count; } // 获取单元格 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { // storyboard里设计的单元格 let identify:String = "ViewCell" // 获取设计的单元格,不需要再动态添加界面元素 let cell = self.collectionView.dequeueReusableCell( withReuseIdentifier: identify, for: indexPath) as UICollectionViewCell //先清空内部原有的元素 for subview in cell.subviews { subview.removeFromSuperview() } // 添加图片 let img = UIImageView(image: UIImage(named: courses[indexPath.item]["pic"]!)) img.frame = cell.bounds img.contentMode = .scaleAspectFit // 图片上面显示课程名称,居中显示 let lbl = UILabel(frame:CGRect(x:0, y:0, width:cell.bounds.size.width, height:20)) lbl.textColor = UIColor.white lbl.textAlignment = .center lbl.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.2) lbl.text = courses[indexPath.item]["name"] cell.addSubview(img) cell.addSubview(lbl) return cell } /* 自定义布局不需要调用 //单元格大小 func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize { let size:Float = indexPath.item % 3 == 0 ? 200 : 100 return CGSize(width:size, height:size) } */ }源码下载:hangge_591.zip
看了很多篇,收益匪浅!
想了解一下,网上看到的基本都是通过代码来实现页面布局的,能否出一个在storyboard上设计collectionView并关联代码来实现的案例?新手不太会~~~
有没有纯代码的实现方式?
航哥好:信读书,书架--编辑--归档,主要是想实现归档功能。
航哥好:我想实现微信读书,书架的布局方式,想在最后一个cell里再放一个九宫格。
航哥好:如果collectionview下有两个cell:DesignViewCellA,DesignViewCellB,现在怎样获取DesignViewCellB为它赋值,并返回;
站长,8 楼的删除方式,有没有更优化的删除方式呢?请问下
楼主,请问下是不是写错了,为什么collectionlayout中会就指直接写UIcollectionView。两部分代码基本是一样的啊
航哥 来回滑动的时候出现了复用错乱问题。cell加载的图片的位置发生了改变然后cell的内容也发生了改变
您好,如何解决复用导致的错乱问题?
一进去就报错,是因为我的图片大小不对吗
如果 cell 里的图片自适应大小怎么实现啊
航哥给力,非常感谢
受益不少 谢谢
给力,谢航哥