[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 ,NTHorizontalPageViewControllerProtocol10 { 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風格的瀑布流界面