[Swift通天遁地]九、拔剑吧-(11)创建强大的Pinterest风格的瀑布流界面

本文将演示如何创建强大的Pinterest风格的瀑布流界面

Github项目:【demonnico/PinterestSwift】,下载并解压文件。

【PinterestSwift】文件夹->【CHTCollectionViewWatrfallLayout.swift】文件

->按下【Command】,继续选择【Extension.swift】->继续选择【Macro.swift

->继续选择【NTHorizontalPagViewCell.swift

->继续选择【NTTransition.swift

->继续选择【NTTransitionProtocol.swift

->继续选择【NTWaterfallViewCell.swift

将上面选择的文件,拖入到自己的项目中。

在弹出的文件导入确认窗口中,点击【Finish】完成按钮,确认文件的导入。

在项目文件夹上点击鼠标右键,弹出右键菜单。

【New File->【Cocoa Touch->【Next】->

【Class】:HorizontalPageViewController

【Subclass of:UICollectionViewController

【Language】:Swift

->Next->【Create】

点击打开【HorizontalPageViewController.swift】,

现在开始编写代码,创建一个自定义的集合视图。

 1 import Foundation
 2 import UIKit
 3 
 4 //初始化一个字符串常量,作为集合单元格的复用标识。
 5 let horizontalPageViewCellIdentify = "horizontalPageViewCellIdentify"
 6 
 7 //给当前的类添加两个协议,使用第一个协议中的方法,返回页面切换所需的集合视图,
 8 //使用第二个协议的方法,设置集合视图的单元格的偏移距离。
 9 class HorizontalPageViewController : UICollectionViewController, NTTransitionProtocol ,NTHorizontalPageViewControllerProtocol
10 {
11     //添加一个字符串数组属性,作为集合视图每个单元格显示的图片内容。
12     var imageNameList : Array <NSString> = []
13     //添加一个属性,作为集合视图单元格的偏移距离。
14     var pullOffset = CGPoint.zero
15     
16     //添加一个初始化方法,用来设置集合视图的布局。
17     init(collectionViewLayout layout: UICollectionViewLayout!, currentIndexPath indexPath: IndexPath)
18     {
19         super.init(collectionViewLayout:layout)
20         //获得集合视图控制器的集合视图。
21         let collectionView :UICollectionView = self.collectionView!;
22         //设置集合视图在页面跳转时自动停止滚动
23         collectionView.isPagingEnabled = true
24         //使用第三方类库提供的集合单元格类,注册集合视图的单元格,并设置单元格的复用标识。
25         collectionView.register(NTHorizontalPageViewCell.self, forCellWithReuseIdentifier: horizontalPageViewCellIdentify)
26         //通过扩展方法,把一个对象与另外一个对象进行关联。
27         //这需要使用到运行时函数,包含四个参数:
28         //源对象、关键字、关联的对象、一个关联策略。
29         collectionView.setToIndexPath(indexPath)
30         
31         //调用集合视图对象的序列刷新方法,该方法可以执行多个插入、删除、重新加载、移动等操作。
32         collectionView.performBatchUpdates({collectionView.reloadData()}, completion: { finished in
33             if finished
34             {
35                 //调用集合视图对象的滑动到指定项目方法,在垂直方向上,滑动到指定索引的单元格。
36                 collectionView.scrollToItem(at: indexPath,at:.centeredHorizontally, animated: false)
37             }});
38     }
39     
40     override func viewDidLoad()
41     {
42         super.viewDidLoad()
43     }
44     
45     //添加一个代理方法,用来初始化或复用集合视图中的单元格。
46     override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
47     {
48         //根据复用标识,从集合视图中获取可以复用的单元格。
49         let collectionCell: NTHorizontalPageViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: horizontalPageViewCellIdentify, for: indexPath) as! NTHorizontalPageViewCell
50         
51         //设置集合视图的扩展设置,从而往集合视图中添加一个图像视图。
52         collectionCell.imageName = self.imageNameList[(indexPath as NSIndexPath).row] as String
53         //初始化集合视图的扩展属性。
54         collectionCell.tappedAction = {}
55         //给集合视图添加一个下拉动作。
56         collectionCell.pullAction = { offset in
57             //当接收到下拉事件时,
58             self.pullOffset = offset
59             //在导航控制器的堆栈中,返回上一个页面。
60             self.navigationController!.popViewController(animated: true)
61         }
62         //在绘制周期开始前,首先对集合视图进行布局,
63         collectionCell.setNeedsLayout()
64         
65         //最后返回设置好的集合视图单元格。
66         return collectionCell
67     }
68     
69     //添加一个代理方法,用来设置集合视图的单元格的数据。
70     override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
71     {
72         return imageNameList.count
73     }
74     
75      //添加一个代理方法,用来返回页面切换所需的集合视图
76     func transitionCollectionView() -> UICollectionView!
77     {
78         return collectionView
79     }
80     
81      //添加一个代理方法,设置集合视图的单元格的偏移距离。
82     func pageViewCellScrollViewContentOffset() -> CGPoint
83     {
84         return self.pullOffset
85     }
86     
87     //添加一个必须实现的初始化方法
88     required init?(coder aDecoder: NSCoder)
89     {
90         fatalError("init(coder:) has not been implemented")
91     }
92 }

在左侧的项目导航区,打开视图控制器的代码文件【ViewController.swift】

现在开始编写代码,将系统默认的视图控制器,修改为另一个集合视图控制器,

作为瀑布流的载体

  1 import UIKit
  2 
  3 let waterfallViewCellIdentify = "waterfallViewCellIdentify"
  4 
  5 //创建一个遵循导航控制器代理协议的类
  6 class NavigationControllerDelegate: NSObject, UINavigationControllerDelegate
  7 {
  8     //添加一个方法,用来处理导航控制器页面之间的跳转事件,
  9     //并返回页面跳转的动画样式。
 10     func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?{
 11         
 12         //初始化三个对象,作为导航控制器跳转前的页面。
 13         let fromVCConfromA = (fromVC as? NTTransitionProtocol)
 14         let fromVCConfromB = (fromVC as? NTWaterFallViewControllerProtocol)
 15         let fromVCConfromC = (fromVC as? NTHorizontalPageViewControllerProtocol)
 16         
 17         //初始化三个对象,作为导航控制器跳转后的页面。
 18         let toVCConfromA = (toVC as? NTTransitionProtocol)
 19         let toVCConfromB = (toVC as? NTWaterFallViewControllerProtocol)
 20         let toVCConfromC = (toVC as? NTHorizontalPageViewControllerProtocol)
 21         
 22         //判断当从瀑布流页面,跳转到详情页面,或者从详情页面,返回瀑布流页面时的跳转样式。
 23         if((fromVCConfromA != nil)&&(toVCConfromA != nil)&&(
 24             (fromVCConfromB != nil && toVCConfromC != nil)||(fromVCConfromC != nil && toVCConfromB != nil))){
 25             //初始化一个动画跳转对象
 26             let transition = NTTransition()
 27             //根据导航控制器的页面跳转的类型是否为出栈操作,
 28             //来设置跳转对象的布尔属性。
 29             transition.presenting = operation == .pop
 30             //最后返回设置好的切换对象。
 31             return  transition
 32         }
 33         else
 34         {
 35             return nil
 36         }
 37     }
 38 }
 39 
 40 //修改当前视图控制器的父类,将父类修改为集合视图控制器,
 41 //并遵循瀑布流布局协议、动画切换协议、以及瀑布流视图控制器协议。
 42 class ViewController:UICollectionViewController, CHTCollectionViewDelegateWaterfallLayout, NTTransitionProtocol, NTWaterFallViewControllerProtocol{
 43     
 44     //初始化一个字符串数组,作为集合视图所有图像的名称。
 45     var imageNameList : Array <NSString> = []
 46     //初始化一个导航控制器代理对象。
 47     let delegateHolder = NavigationControllerDelegate()
 48     override func viewDidLoad()
 49     {
 50         super.viewDidLoad()
 51         // Do any additional setup after loading the view, typically from a nib.
 52         //设置当前导航控制器的代理对象。
 53         self.navigationController!.delegate = delegateHolder
 54         
 55         var index = 1
 56         //添加一个循环语句,用来往数组中添加图片的名称。
 57         while(index<14)
 58         {
 59             //根据循环的索引,初始化一个由图片名称组成的字符串数组。
 60             let imageName = NSString(format: "Pic%d.png", index)
 61             //将图片名称添加到数组中。
 62             imageNameList.append(imageName)
 63             index += 1
 64         }
 65         
 66         //获得当前集合视图控制器中的集合视图。
 67         let collection :UICollectionView = collectionView!
 68         //设置集合视图的显示区域与屏幕相同,在此使用了宏定义常量。
 69         collection.frame = screenBounds
 70         //设置集合视图的布局样式
 71         collection.setCollectionViewLayout(CHTCollectionViewWaterfallLayout(), animated: false)
 72         //设置集合视图的背景颜色为黑色
 73         collection.backgroundColor = UIColor.black
 74         //给集合视图注册复用标识符
 75         collection.register(NTWaterfallViewCell.self, forCellWithReuseIdentifier: waterfallViewCellIdentify)
 76         //重新加载集合视图中的数据
 77         collection.reloadData()
 78     }
 79     
 80     //添加一个方法,用来设置单元格的尺寸
 81     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize
 82     {
 83         //加载数组中的指定名称的图片
 84         let image:UIImage! = UIImage(named: self.imageNameList[(indexPath as NSIndexPath).row] as String)
 85         //单元格的宽度时固定的,在此根据单元格的高度和图片的宽度,获得等比例的图片高度。
 86         let imageHeight = image.size.height*gridWidth/image.size.width
 87         
 88         //返回计算好的图片尺寸
 89         return CGSize(width: gridWidth, height: imageHeight)
 90     }
 91     
 92     //添加一个代理方法,用来初始化或复用集合视图中的单元格。
 93     override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
 94     {
 95         //根据复用标识,从集合视图中获取可以复用的单元格。
 96         let collectionCell: NTWaterfallViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: waterfallViewCellIdentify, for: indexPath) as! NTWaterfallViewCell
 97         //设置集合视图的扩展方法,从而往集合视图中添加一个图像视图。
 98         collectionCell.imageName = self.imageNameList[(indexPath as NSIndexPath).row] as String
 99         //对单元格在绘制之前进行重新布局。并返回设置好的单元格。
100         collectionCell.setNeedsLayout()
101         return collectionCell
102     }
103     
104      //添加一个代理方法,用来设置单元格的数量
105     override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
106     {
107         return imageNameList.count
108     }
109     
110      //添加一个代理方法,用来处理单元格的触摸事件。
111     override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
112     {
113         //初始化一个视图控制器,作为即将显示的细节页面。
114         let pageViewController =
115             HorizontalPageViewController(collectionViewLayout: pageViewControllerLayout(), currentIndexPath:indexPath)
116         //设置控制器的图片名称列表属性
117         pageViewController.imageNameList = imageNameList
118         //在集合视图中,跳转到指定位置的单元格
119         collectionView.setToIndexPath(indexPath)
120         //在导航控制器的堆栈中,压入新的集合控制器。
121         navigationController!.pushViewController(pageViewController, animated: true)
122     }
123     
124     //添加一个方法,用来设置集合视图的布局方式
125     func pageViewControllerLayout () -> UICollectionViewFlowLayout
126     {
127         //初始化一个集合视图流布局对象。
128         let flowLayout = UICollectionViewFlowLayout()
129         //根据导航栏的显示状态,创建集合视图的尺寸。
130         let itemSize  = self.navigationController!.isNavigationBarHidden ?
131             CGSize(width: screenWidth, height: screenHeight+20) : CGSize(width: screenWidth, height: screenHeight-navigationHeaderAndStatusbarHeight)
132         //设置集合视图的单元格的尺寸。
133         flowLayout.itemSize = itemSize
134         //设置单元格之间的最小行距
135         flowLayout.minimumLineSpacing = 0
136         //设置同一行的单元格之间的最小间距。
137         flowLayout.minimumInteritemSpacing = 0
138         //设置布局对象的滚动方向为水平方向。
139         flowLayout.scrollDirection = .horizontal
140         
141         //返回设置好的布局对象
142         return flowLayout
143     }
144     
145     //添加一个方法,用来返回进行动态切换的集合视图
146     func transitionCollectionView() -> UICollectionView!
147     {
148         return collectionView
149     }
150     
151     func viewWillAppearWithPageIndex(_ pageIndex : NSInteger)
152     {
153         
154     }
155     
156     override func didReceiveMemoryWarning() {
157         super.didReceiveMemoryWarning()
158         // Dispose of any resources that can be recreated.
159     }
160 }

在项目导航区,打开故事板文件。

在故事板中添加一个集合视图控制器,首先选择并删除默认的视图控制器。

选择默认的视图控制器,【Command】+【Delete】删除选择的视图控制器。

点击控件库图标,打开控件库的列表窗口。双击集合视图控制器,往故事板中插入一个控制器。

依次点击:【Editor】编辑器->【Embed In】植入->【Navigation Controller】导航控制器

将集合视图控制器植入导航控制器。植入导航控制器。

打开检查器设置面板,点击属性检查器图标,进入属性设置面板。

勾选【Is Initial View Controller】是否初始视图控制器。

将导航控制器修改为项目的初始控制器。

选择集合视图控制器,点击身份检查器图标,打开身份设置面板。

在类名输入框内,输入所绑定的自定义集合视图类。

【Class】:ViewController

模拟器启动后,由下往上拖动,可以浏览瀑布流底部的内容。

在详情页面的顶部往下方拖动,

通过下拉动作,返回瀑布流页面。

猜你喜欢

转载自www.cnblogs.com/strengthen/p/10357522.html