1. 程式人生 > >[Swift通天遁地]九、拔劍吧-(11)創建強大的Pinterest風格的瀑布流界面

[Swift通天遁地]九、拔劍吧-(11)創建強大的Pinterest風格的瀑布流界面

idl nsstring 項目文件 warn port 狀態 打開 參數 pear

本文將演示如何創建強大的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

模擬器啟動後,由下往上拖動,可以瀏覽瀑布流底部的內容。

在詳情頁面的頂部往下方拖動,

通過下拉動作,返回瀑布流頁面。

[Swift通天遁地]九、拔劍吧-(11)創建強大的Pinterest風格的瀑布流界面