本文翻譯自:UICollectionView Tutorial Part 1: Getting Started
iOS相冊(cè)里有一種優(yōu)雅的方式來實(shí)現(xiàn)多種布局.你可以在網(wǎng)格視圖里查看你的照片:
或者你可以查看你的層疊著的相冊(cè):
你可以通過一個(gè)非常酷的捏的手勢(shì)來進(jìn)行兩種布局的轉(zhuǎn)換.你或許會(huì)想"哇哦,我想把它加進(jìn)我的應(yīng)用里".
利用UICollectionView能夠非常簡單地添加自定義布局和轉(zhuǎn)場動(dòng)畫(如相冊(cè)應(yīng)用里的).
因?yàn)閏ollection views定制化非常強(qiáng),你將不局限于層疊和網(wǎng)格.你也可以使用circle、cover-flow、Pulse news等布局,甚至是任何你能想到的.
好消息是,如果你對(duì)UITableView熟悉,那么將很容易掌握collection views的用法,因?yàn)樗蛅able view里的data source和delegate類型非常相似.
在本教程中,你將用UICollectionView創(chuàng)建屬于自己的網(wǎng)格圖片瀏覽app.在你完成本教程的時(shí)候,你將掌握collection views的基本用法并能夠?qū)⑺鼈冞\(yùn)用到你的app中.
- UICollectionView解析
我們看一下已經(jīng)完成的項(xiàng)目.如下所示,UICollectionView包含幾個(gè)內(nèi)容:
我們來一步一步看下這些內(nèi)容:
- UICollectionView:內(nèi)容呈現(xiàn)的主界面,和UITableView相似.如同table view,collection view也是UIScrollView的子類.
- UICollectionViewCell:和UITableViewCell相似.這些cells包含相關(guān)的view,繼而添加為collection view的子視圖.可以用代碼或者在Interface Builder里創(chuàng)建cell.
- Supplementary Views:如果你在cell之外有額外的內(nèi)容需要展示,你可以使用Supplementary Views,用法同headers和footers.
除了以上可以看見的內(nèi)容,collection view還需要有個(gè)布局對(duì)象來約束內(nèi)容的位置、大小等.約束對(duì)象為UICollectionViewLayout的子類.約束可以在runtime時(shí)起作用,而且collection view可以在布局之間通過動(dòng)畫自由切換.
你可以創(chuàng)建一個(gè)UICollectionViewLayout的子類來編寫自定義布局,但蘋果已經(jīng)為開發(fā)者提供了名為UICollectionViewFlowLayout的基礎(chǔ)"flow-based"布局.它能夠根據(jù)對(duì)象的大小來挨個(gè)展示,有點(diǎn)像網(wǎng)格view.你可以用它來out of the box,或者繼承它來創(chuàng)建一個(gè)有意思的動(dòng)作或可視化效果.
接下來你將通過此教程來深入了解這些元素.但現(xiàn)在,你需要來創(chuàng)建這個(gè)相關(guān)的項(xiàng)目了!
- FlickrSearch介紹
在下面的教程里,你將創(chuàng)建一個(gè)名叫FlickrSearch的圖片瀏覽app.像前面的截圖一般,這個(gè)應(yīng)用能夠搜索在Flickr上一段時(shí)期內(nèi)的流行圖片,而且能夠下載下來并在網(wǎng)格視圖根據(jù)圖片大小來展示.
準(zhǔn)備好了嗎?在Xcode里點(diǎn)擊File\New\Project....然后選擇iOS\Application\Single View Application模板.
這個(gè)模板將提供一個(gè)簡單的UIViewController和storyboard.
點(diǎn)擊Next填寫app的基本信息.設(shè)置項(xiàng)目名字為FlickrSearch,設(shè)配類型為iPad,語言為swift.點(diǎn)擊Next選擇項(xiàng)目保存位置后,選擇Create.
模板里的view controller和storyboard都沒用,你將使用UICollectionViewController(有點(diǎn)像UITableViewController),它是用來控制collection view的view controller的子類.刪除ViewController.swift和從Main.storyboard上去除空的view controller.現(xiàn)在你將有一個(gè)黑色的頁面:]
打開AppDelegate.swift添加一個(gè)我稱為Wenderlich綠的顏色.在import UIKit一行下面添加:
let themeColor = UIColor(red: 0.01, green: 0.41, blue: 0.22, alpha: 1.0)
利用Wenderlich綠作為整個(gè)應(yīng)用的主題色.在application(_:didFinishLaunchingWithOptions)增加:
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
window?.tintColor = themeColor
return true
}
- 開始你的collection
打開Main.storyboard,然后點(diǎn)擊Collection View Controller.選擇Editor\Embed in\Navigation Controller來創(chuàng)建一個(gè)navigation controller且會(huì)默認(rèn)collection view controller為root.
你的storyboard看起來會(huì)像下面一樣:
確保這個(gè)navigation controller 為初始view controller(左邊會(huì)有箭頭).
選擇collection view通過Attributes inspector設(shè)置其背景顏色為白色:
從截圖中還會(huì)看到Layout設(shè)置為Flow,表示將會(huì)使用前文提到的flow layout.
在collection上選擇一個(gè)單獨(dú)的cell通過attributes inspector設(shè)置其Reuse Identifier為FlickrCell.這和table views 非常像,data source使用這個(gè)identifier來重用或者新建cell.
在collection view之上的navigation bar的中心拖入一個(gè)text field.這將是用戶輸入搜索內(nèi)容的地方.在attributes inspector中設(shè)置Placeholder Text為Search,Return Key為Search.然后按住control鍵從text field拖動(dòng)到collection view controller后選擇delegate outlet:
UICollectionViewController能做很多東西,但你最好新建個(gè)它的子類.點(diǎn)擊File\New\File...,選擇Cocoa Touch Class并新建的類命名為FlickrPhotosViewController.它是UICollectionViewController的子類.模板中會(huì)有很多代碼,但最好要知道這個(gè)類是如何運(yùn)作的.打開FlickrPhotosViewController.swift并刪除除了描述和UIKit導(dǎo)入外的所有代碼.你的文件如下面所示:
import UIKit
class FlickrPhotosViewController : UICollectionViewController { }
在類中添加你在storyboard設(shè)置的重用標(biāo)示常量,并添加一個(gè)section 嵌套(你待會(huì)會(huì)用到):
private let reuseIdentifier = "FlickrCell"
private let sectionInsets = UIEdgeInsets(top: 50.0, left: 20.0, bottom: 50.0, right: 20.0)
隨著教程的繼續(xù),里面會(huì)添加剩余的代碼.
返回到Main.storyboard,選擇collection view controller,在identity inspector里面設(shè)置Class為FlickrPhotosViewController來匹配你的新類:
- 解析Flickr Photos
這一章節(jié)你首要做的是快速添加10次section title.非常簡單.
Flickr是一個(gè)非常好的圖片分享服務(wù),而且對(duì)開發(fā)者相當(dāng)友好,公共API很好用.通過API可以實(shí)現(xiàn)圖片的搜索、添加、評(píng)論等功能.
使用Flickr API之前,你需要個(gè)API key.如果你在做一個(gè)真正的項(xiàng)目,建議你在這里注冊(cè)下:http://www.flickr.com/services/api/keys/apply/
如果僅用測試之用,無需注冊(cè),Flickr提供了一個(gè)示例Key.進(jìn)入:http://www.flickr.com/services/api/explore/?method=flickr.photos.search復(fù)制URL最后的"&api_key="和"&"之間的API key.粘貼到你的文本編輯器里以供稍后使用.
例如,如果URL為:
http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=6593783 efea8e7f6dfc6b70bc03d2afb&format=rest&api_sig=f24f4e98063a9b8ecc8b522b238 d5e2f
那么API key就為'6593783efea8e7f6dfc6b70bc03d2afb'
鑒于本文主要講的是UICollectionView并非Flickr API,我已經(jīng)為你寫好了Flickr搜索的代碼,你可以在這里下載.
解壓后,把FlickrSearcher.swift拖進(jìn)項(xiàng)目中.確保Copy items into destination group's folder被勾選,繼而點(diǎn)擊完成.
文件中包含兩個(gè)類和一個(gè)結(jié)構(gòu)體:
- FlickrSearchResults:包含搜索條目和搜索返回結(jié)果的結(jié)構(gòu)體.
- FlickrPhoto:從Flickr取回的圖片數(shù)據(jù)信息-縮略圖、圖片、ID等等.而且還有一些建立Flickr URLs和尺寸計(jì)算的方法.FlickrSearchResults為包含這些數(shù)據(jù)對(duì)象的數(shù)組.
- Flickr:提供了一個(gè)簡單的基于block的搜索和返回搜索結(jié)果的API.
查看下里面的代碼,相當(dāng)簡單,也許能促使你將Flickr運(yùn)用到你的應(yīng)用中.
在你能搜索Flickr之前,你需要輸入一個(gè)API key.打開FlickrSearcher.swift替換apiKey為你之前獲得的API key.它將如下所示:
let apiKey = "hh7ef5ce0a54b6f5b8fbc36865eb5b32"
當(dāng)你都準(zhǔn)備好時(shí),移步下一章節(jié).是時(shí)候做連接Flickr的準(zhǔn)備工作了.
- 準(zhǔn)備數(shù)據(jù)結(jié)構(gòu)
你將做的效果是每當(dāng)你完成一次搜索時(shí),它會(huì)在collection view顯示一個(gè)新的"section"來展示結(jié)果(并非簡單地替換之前的結(jié)果).換言之,如果你搜索一個(gè)"ninjas"后再搜索"pirates",在tableview里將會(huì)有一個(gè)ninjas section和一個(gè)pirates section.Talk is cheap!
要實(shí)現(xiàn)這個(gè)功能,需要一個(gè)數(shù)據(jù)結(jié)構(gòu)來保存每次的搜索結(jié)果.FlickrSearchResults數(shù)組將擔(dān)此重任.
打開FlickrPhotosViewController.swift添加如下幾個(gè)屬性和一個(gè)工具方法:
private var searches = [FlickrSearchResults]()
private let flickr = Flickr()
func photoForIndexPath(indexPath: NSIndexPath) -> FlickrPhoto { return searches[indexPath.section].searchResults[indexPath.row]
}
searches是一個(gè)追蹤所有搜索的數(shù)組.flickr為當(dāng)你搜索時(shí)所使用的引用對(duì)象.
photoForIndexPath為在collection view里獲取最近幾次搜索結(jié)果的方法.你將用索引來獲得圖片,而并非大量重復(fù)的代碼.
- 獲得好的結(jié)果
現(xiàn)在你已經(jīng)為Flickr搜索做好了準(zhǔn)備.當(dāng)用戶點(diǎn)擊Search,輸入搜索關(guān)鍵詞后來觸發(fā)一次搜索.你已經(jīng)將text field 的delegate和collection view controller連接,現(xiàn)在你需要做些后續(xù)工作.
打開FlickrPhotosViewController.swift增加一個(gè)extension來實(shí)現(xiàn)text field的代理方法:
extension FlickrPhotosViewController : UITextFieldDelegate {
func textFieldShouldReturn(textField: UITextField) -> Bool {
// 1
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .Gray)
textField.addSubview(activityIndicator)
activityIndicator.frame = textField.bounds
activityIndicator.startAnimating()
flickr.searchFlickrForTerm(textField.text) {
results, error in
//2
activityIndicator.removeFromSuperview()
if error != nil {
println("Error searching : \(error)")
}
if results != nil {
//3
println("Found \(results!.searchResults.count) matching \(results!.searchTerm)")
self.searches.insert(results!, atIndex: 0)
//4
self.collectionView?.reloadData()
}
}
textField.text = nil
textField.resignFirstResponder()
return true
}
}
下面是代碼的釋義:
- 增加一個(gè)activity view后,通過我先前提供的類來輸入關(guān)鍵詞后進(jìn)行Flick異步搜索.當(dāng)有結(jié)果返回時(shí),完成block會(huì)被調(diào)用來設(shè)置FlickrPhoto對(duì)象,或者返回一個(gè)錯(cuò)誤信息(當(dāng)出錯(cuò)時(shí)).
- 在console里打印錯(cuò)誤信息.當(dāng)然在實(shí)際的應(yīng)用中你并不希望這些錯(cuò)誤信息被用戶看到.
- 得到結(jié)果后將其增加到搜索數(shù)組的前面.
- 當(dāng)你有新數(shù)據(jù)加入時(shí)需要刷新UI.使用insertSections方法來將你獲得的結(jié)果添加到列表的頂端.
運(yùn)行你的app.在輸入框里輸入,你將會(huì)在console里看到日志信息來表示返回的結(jié)果數(shù),如下所示:
Found 20 matching bananas
Flickr類限制每次獲得20條數(shù)據(jù).
咋回事,你并沒有在collection view里看到照片!像table view一般,collection view需要實(shí)現(xiàn)data source 和代理方法.
- 完善UICollectionView
如你所知,當(dāng)你使用table view時(shí)需要一個(gè)data source和一個(gè)代理來提供數(shù)據(jù)展示和動(dòng)作操作(如選擇row).
相似的,當(dāng)你使用collection view的時(shí)候需要設(shè)置一個(gè)數(shù)據(jù)源和一個(gè)代理.他們需要遵從: - 數(shù)據(jù)源(UICollectionViewDataSource)返回collection的元素個(gè)數(shù)和視圖個(gè)數(shù).
- 代理(UICollectionViewDelegate)當(dāng)有動(dòng)作操作時(shí)被調(diào)用,例如cell被選擇,高亮顯示或者刪除.
UICollectionViewFlowLayout也有個(gè)代理協(xié)議:UICollectionViewDelegateFlowLayout.它作用于布局的時(shí)候,設(shè)置cell的間距、滑動(dòng)方向等等.
在本章,你將實(shí)現(xiàn)你的view controller的必須方法UICollectionViewDataSource和UICollectionViewDelegateFlowLayout,這些全部在collection view中完成.UICollectionViewDelegate將會(huì)在第二章中用到.
- UICollectionViewDataSource
在FlickrPhotosViewController.swift,增加數(shù)據(jù)源的擴(kuò)展:
extension FlickrPhotosViewController : UICollectionViewDataSource {
//1
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return searches.count
}
//2
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return searches[section].searchResults.count
}
//3
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! UICollectionViewCell
cell.backgroundColor = UIColor.blackColor()
// Configure the cell
return cell
}
}
這些方法非常簡單明了:
- 每個(gè)section是一個(gè)搜索結(jié)果,所以數(shù)量為searches數(shù)組的個(gè)數(shù).
- 元素的個(gè)數(shù)為FlickrSearch里獲得的searchResults數(shù)組元素個(gè)數(shù).
- 這是個(gè)臨時(shí)方法用來返回一個(gè)空的cell,稍后會(huì)來重寫它.collection views需要你注冊(cè)個(gè)復(fù)用的cell,否則將會(huì)在runtime期間發(fā)生錯(cuò)誤.
運(yùn)行項(xiàng)目,輸入搜索.盡管現(xiàn)在看起來不怎樣,但畢竟看到了20個(gè)新的cell:
- UICollectionViewFlowLayoutDelegate
如前文所述,每個(gè)collection view都有一個(gè)布局.項(xiàng)目中,你使用flow layout,它非常好用且提供的是網(wǎng)格視圖.
仍舊在FlickrPhotosViewController.swift里,添加另一個(gè)布局相關(guān)的擴(kuò)展:
extension FlickrPhotosViewController : UICollectionViewDelegateFlowLayout {
//1
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let flickrPhoto = photoForIndexPath(indexPath)
//2
if var size = flickrPhoto.thumbnail?.size {
size.width += 10
size.height += 10
return size
}
return CGSize(width: 100, height: 100)
}
//3
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAtIndex section: Int) -> UIEdgeInsets {
return sectionInsets
}
}
- collectionView(_:layout:sizeForItemAtIndexPath:)來告訴布局cell的大小.要實(shí)現(xiàn)這個(gè),你需要知道FlickrPhoto的尺寸.
- 來返回尺寸的大小.FlickrPhoto的thumbnail屬性可選的,可能不存在.當(dāng)此屬性不存在時(shí)就返回個(gè)默認(rèn)尺寸.如果有此屬性則返回增加邊界后的尺寸.
- collectionView(_:layout:insetForSectionAtIndex:)返回cell、header和footer之間的空間.通常為一個(gè)常數(shù)值.
運(yùn)行應(yīng)用,進(jìn)行搜索.可以看見一個(gè)個(gè)不同尺寸的黑色方塊!
已經(jīng)有了圖片展示的空間,下面開始展示圖片了!
- 創(chuàng)建自定義UICollectionViewCell
UICollectionView的一個(gè)超棒的事情是和table view一樣,很容易地在Storyboard里創(chuàng)建.你可以在collection views里拖拽,添加約束.
打開Main.storyboard選擇collection view.設(shè)置cell的大小為200*200.
在cell上拖拽一個(gè)image view.它會(huì)自動(dòng)填滿cell.記得添加邊界約束.這樣圖片就會(huì)總在cell的里面,無論圖片尺寸大小是多少.選擇圖片,點(diǎn)擊pin按鈕增加距離邊界5points的約束.注意不要選中Constrain to margins.
UICollectionViewCell不允許改變背景顏色.創(chuàng)建它的子類將是解決辦法.
創(chuàng)建FlickrPhotoCell,它為UICollectionViewCell的子類.
在Main.storyboard選擇cell.設(shè)置其類為FlickrPhotoCell:
打開Assistant editor,確保打開的是FlickrPhotoCell.swift,連接image view到類中:
打開FlickrPhotosViewController.swift替換其中的collectionView(cellForItemAtIndexPath:):
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
//1
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! FlickrPhotoCell
//2
let flickrPhoto = photoForIndexPath(indexPath)
cell.backgroundColor = UIColor.blackColor()
//3
cell.imageView.image = flickrPhoto.thumbnail
return cell
}
這和之前的方法有所不同.
- cell名字現(xiàn)在為FlickrPhotoCell
- 你將使用之前的FlickrPhoto方法來呈現(xiàn)圖片.
- 傳遞給image view圖片的thumbnail屬性.
運(yùn)行程序,輸入一個(gè)搜索banana.你將得到下面的圖片:
完成.
你可以在這里下載完整項(xiàng)目.
Girl學(xué)iOS100天 第12&13天