UITableView 教程:動態(tài) Table View Cell 高

原文地址: RAYWENDERLICH

說明:英文水平有限,主要是為了鞏固學(xué)到的知識,也能幫別人快速上手,節(jié)約時間,有任何破綻,尤其是技術(shù)上的,請您一定要告訴我。


學(xué)習怎樣在iOS8上使用Swift創(chuàng)建動態(tài)Cell高

旁白:其實這張圖幾乎說明了所有問題,設(shè)置好constraints,其它都不是問題。補充一下:這篇教程實際上就是 auto layout教程。)

你應(yīng)該在使用 table view cells 的時候經(jīng)常會寫很多代碼,來手動計算label,image view,text field 和 cell 相關(guān)的每一個控件的高。
坦白講,這種方法很容易出錯且容易讓人迷糊看不懂。
在這篇教程里,你將學(xué)會怎樣創(chuàng)建自定義 cell 并且根據(jù)內(nèi)容動態(tài)調(diào)整 cell 的高度,你可能會想,"這得需要多少工作量...!"
Nope! * (旁白:you are right ~*)
你很幸運,Apple 可以讓你在 iOS8 里很容易的做到這些。你將從寫適配代碼中解脫出來。但是還是要實現(xiàn) table view 的數(shù)據(jù)源和代理方法。

讓我們開始

iOS6出來幾天之后,Apple 介紹了一個超贊的技術(shù):auto layout。程序猿們開始慶祝,街頭聚會,寫贊歌...(旁白:要不要這么夸張啊

好吧,也許有些問題,但它畢竟是一次大飛躍(旁白:是這個意思吧,哈哈

在給了很多開發(fā)者希望的同時,auto layout 還是很難用的。尤其是手寫 auto layout 代碼。Interface Builder 在設(shè)置 constraints 時也并不理想。

很快到現(xiàn)在,伴隨著對 Interface Builder 的所有改進和 iOS8 的到來,我們現(xiàn)在可以很容易的動態(tài)設(shè)置 table view cells 的高度了。

你需要不得不做的事情有:
1,在創(chuàng)建 table view cells 時使用 auto layout。
2,設(shè)置 table view 的** rowHeight 等于 UITableViewAutomaticDimension
3,設(shè)置
estimatedRowHeight ** 的值或者實現(xiàn)預(yù)估高度的代理方法。

這是你需要知道的幾個要點,現(xiàn)在開始下載代碼,搞起項目了。
旁白:確實很重要,雖然說了不少廢話,但是并不是浪費時間的。

教程 App 概覽

設(shè)想一下你的老大來到你面前,對你說:“我們的用戶在為看** Deviant Artists 的方法而大聲抗議”。
我會問:"什么是
Deviant Artists "。
你的頭解釋說:“那是一個藝術(shù)家們用來分享自己作品的社交平臺。你可以通過 Deviant Art websiteMedia RSS endpoint 來了解藝術(shù)家的公告和動態(tài)?!?br> 老大:“我們開始做這個 App 吧,但是要怎么樣把內(nèi)容顯示到表格上呢?你能做到吧?”
你突然受到了感召,走進最近的電話亭,換上了披風成了super Dev..


但你不需要弄騙人的把戲做你老大的英雄,用你的編程技術(shù)就可以做到了。
旁白:你怎么不去做導(dǎo)演啊...

首先,現(xiàn)在客戶端代碼(項目的起始程序)這里。
旁白:語法有一些過時,打開項目會自動讓你轉(zhuǎn)換到最新的語法,轉(zhuǎn)換完之后會報一個錯,將 ** FeedViewController.swift 里的 deselectAllRows **方法替換成如下代碼:

func deselectAllRows() {
    if let selectedRows = tableView.indexPathsForSelectedRows{
      for indexPath in selectedRows {
        tableView.deselectRowAtIndexPath(indexPath, animated: false)
      }
    }
  }


這個項目使用的** CocoaPods ,因此打開 DeviantArtBrowser.xcworkspace ** (不是** xcodeproj 這個文件),pods 已經(jīng)包含到 zip 包里了,所以不用在重新 pod install **。
注意:如果你不清楚什么是 CocoaPods ,可以看下這個教程

打開** Main.storyboard (在 DeviantArtBrowser ** project 下的** DeviantArtBrowser 文件夾 Views **分組里),你將會看到下面四個場景:


從左到右,它們是:

  • 頂級的導(dǎo)航控制器。
  • **FeedViewController **,標題是 ** Deviant Browser **。
  • 還有兩個都是** DetailViewController 的場景,標題分別是Deviant Article** 和 Deviant Media ,一個只用來顯示文本,另一個文本和圖片一起顯示。
    編譯并運行,你將看到一些控制臺上的輸出日志和一個短暫出現(xiàn)的活動指示器,但是在 app 里并沒有什么內(nèi)容顯示出來。
    日志輸出像下面一樣:

2014-11-08 14:30:02.746 DeviantArtBrowser[70847:829282] GET 'http://backend.deviantart.com/rss.xml?q=boost%3Apopular'

2014-11-08 14:30:03.297 DeviantArtBrowser[70847:829282] 200 'http://backend.deviantart.com/rss.xml?q=boost%3Apopular' [0.5506 s]

app 發(fā)起一個網(wǎng)絡(luò)請求并獲得返回,但是并沒有做任何事。
旁白:如果請求失敗,很可能是iOS9系統(tǒng)下不可用http協(xié)議,打開info.plist的源碼,粘貼如下內(nèi)容。

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

現(xiàn)在,打開** FeedViewController.swift(在 Controllers **文件夾的下面)。
看下 ** parseForQuery **這段代碼:

func parseForQuery(query: String?) {
  showProgressHUD()
 
  parser.parseRSSFeed(deviantArtBaseUrlString,
    parameters: parametersForQuery(query),
    success: {(let channel: RSSChannel!) -> Void  in
 
      self.convertItemPropertiesToPlainText(channel.items as! [RSSItem])
      self.items = (channel.items as! [RSSItem])
 
      self.hideProgressHUD()
      self.reloadTableViewContent()
 
    }, failure: {(let error:NSError!) -> Void in
 
      self.hideProgressHUD()
      println("Error: \(error)")
  })
}

** parser 是一個 RSSParser 的實例,屬于 MediaRSSParser 的一部分。
這是一個得到
Deviant Art ** RSS feed 的網(wǎng)絡(luò)請求,它會在成功的 block 里返回一個** RSSChannel 實例。然后解析數(shù)據(jù)將 HTML 轉(zhuǎn)成普通文本, channel.items 和控制器里的 items **屬性都是數(shù)組。

** channel.items 數(shù)組包含 RSSItem **對象,每一個對象元素都是一個 RSS feed。(現(xiàn)在你該知道要將什么顯示到表格里了吧,正是 ** items **數(shù)組!)

最后,項目里會有一些** //TODO:Write this... **的注釋,是為了告訴我們需要實現(xiàn)些什么。

開始創(chuàng)建自定義 Cell

查看源代碼之后,你現(xiàn)在知道這個 app 有了不錯的數(shù)據(jù),但是什么都沒有顯示出來,要顯示它們,你需要創(chuàng)建一個自定義的 table view cell。

  • 添加一個新類到** DeviantArtBrowser**項目里。
  • 名字是 ** BasicCell 并且繼承自 UITableViewCell **。
  • 確信** Also create xib file **沒有被勾選。
  • 語言選擇** Swift 。
    打開
    BasicCell.swift ** 并添加如下屬性:
@IBOutlet var titleLabel: UILabel!
@IBOutlet var subtitleLabel: UILabel!

接下來,打開** Main.storyboard ,拖拽一個UITableViewCell到** FeedViewController 的 table view上。
設(shè)置
BasicCell Custom Class


設(shè)置
BasicCell Identifier (Reuse Identifier)

設(shè)置 cell 的
Row Height 83 。

拖拽一個 UILabel 到 cell 上,設(shè)置 text為
Title

設(shè)置 label 的
Lines 為 0,就是沒有上限。(行數(shù)可以設(shè)置很多)

接下來像下面截圖一樣設(shè)置 label 的尺寸和位置。

連接 cell 的 label 到
BasicCell titleLabel ** outlet 上。

接下來,拖拽第二個 UILabel 到 cell 上,像第一個 label 一樣,并且設(shè)置 text 為** Subtitle 。

像第一個 label 一樣,按照下面的截圖來設(shè)置 Subtitle 的尺寸和位置。

設(shè)置 subtitle label 的
Color ** 為 * Light Gray Color ;字體大小為 * 15.0 ;并且 Lines **為 0 ;

將 cell 的 subtitle label 連接到 BasicCell 上的 subtitleLabel outlet上。

接下來,你將給** BasicCell **添加 auto layout ** constraints ** ,來布局 cell 。
注意:如果你對 auto layout 還不太熟悉,不清楚怎么設(shè)置 auto layout constraints ,可以看下 這個教程

選擇 title label 且設(shè)置它的 toptrailing,leading 距離父視圖(也就是content view)20個點。確信你沒有勾選Constrain to margins。(旁白:這個屬性就是系統(tǒng)會為你默認兩邊留白)

圖片顯示的很清楚

確信 cell 的 title label 一直是:

  • 向上距離20個點。
  • 相對于 content view 的整體寬度,左右兩邊空出20個點。

現(xiàn)在,選擇 subtitle label 設(shè)置它的 leading,trailing,和** bottom 距離父視圖20個點。再次確認,沒有勾選Constrain to margins。


像 title label 一樣,確定在 subtitle label 上的
constraints ,是按照底部距離 content view 20個點,左右距離 content view 也是20個點。
旁白
:還是看圖更直觀一些)

技巧: 使用 auto layout 布局 UITableViewCell 時,要確定這些約束都布局到了每一個 subview 的四邊,也就是說,每一個 subview 都應(yīng)該有 leading,top,trailing 和 bottom 約束。
除此之外,** contentView 的頂部到底部都要有清晰的約束條件, 你要能確定,這些子視圖的約束可以正確的指定出 contentView **的高度。
另一部分技巧是,interface Builder 經(jīng)常在你缺失一些約束的情況下,沒有警告提示。在運行項目時,auto layout 沒有返回正確的高度,比如會返回 0 的高度,遇到這些問題需要你重新調(diào)整約束條件 直到滿足條件為止。

現(xiàn)在, 選擇 subtitle label, 按住 Control 并拖拽到 title label。選擇 Vertical Spacing 連接** subtitle label 的頂部和 title label **的底部。

在 title label 上,設(shè)置 ** Horizontal** 和 ** Vertical** 的 Content Hugging PriorityContent Compression Resistance Priority751


在 subtitle label 上,設(shè)置 ** Horizontal** 和 ** Vertical** 的 Content Hugging PriorityContent Compression Resistance Priority750。

旁白:解釋一下,這兩個優(yōu)先級的意思,**Content Hugging Priority **就是級別越高,越不會被拉開,抻開。 **Content Compression Resistance Priority **就是級別越高,越不會被壓縮,擠掉。這個還是要看具體的例子來理解的,有一點繞。)
這就是告訴 auto layout 怎樣去適配 labels 的文本-區(qū)分 title label 和 subtitle label 之間約束的優(yōu)先級。在這個例子里,這些約束基本滿足了條件。

檢查: 上面的約束條件滿足情況了嗎?
1,每一個子視圖的所有側(cè)邊都有約束嗎?Yes。
2,**contentView 從上到下都有約束嗎?Yes(旁白:只有這樣 contentView 才能確定出自己的高度,像 UIScrollView 一樣,才能知道自己的 contentSize,否則運行起來都不能滑動。)
**titleLabel **距頂部有20個點,它和 **subtitleLabel **之間的距離是4個點,并且 ** subtitleLabel **和底部有19.5個點。
所以,現(xiàn)在 auto layout 已經(jīng)可以動態(tài)設(shè)置 cell 的高度了。

接下來,你要創(chuàng)建一個 ** BasiceCell ** 跳轉(zhuǎn)到 **Deviant Article **場景的鏈接(segue)。

選擇你的 BasiceCell,按住 control 拖拽到 **Deviant Article 場景,從 Selection Segue **選項中選擇 **Push **。

Interface Builder 將自動更改 Accessory 屬性為 Disclosure Indicator,這是為了指示你從 cell 導(dǎo)航到詳情里去。然而,這并不符合程序的設(shè)計,選擇 **BasiceCell ** ,更改 ** Accessory **為 None。


現(xiàn)在,用戶在任何時候點擊 BasicCell 都會跳轉(zhuǎn)到 **DetailViewController **里了。

哇塞,你的** BasicCell 已經(jīng)設(shè)置完了!如果你編譯運行 app 的話,還是毛都看不到,為啥呢...(旁白:自言自語..)


還記得哪些** TODO **的注釋嗎?,對了,這就是問題所在了。你需要到哪些 TODOs 里寫一些代碼。

配置 Table View

首先,你需要配置下這個 table view。
打開** FeedViewController.swift ** ,用下面的代碼替換**configureTableView() **這個方法。

func configureTableView() {
  tableView.rowHeight = UITableViewAutomaticDimension
  tableView.estimatedRowHeight = 160.0
}

確信 table view 是用 auto layout 來動態(tài)設(shè)置高的時候,將 **rowHeight 設(shè)置為 UITableViewAutomaticDimension 。
確定這些之后,你還要提供一個 ** estimatedRowHeight 的值。小意思,160.0
是一個隨意的值也可以使用。在你的項目里,你可能也想根據(jù)數(shù)據(jù)類型設(shè)置一個更好的值。

實現(xiàn) UITableView 的 Data Source

接下來,你需要實現(xiàn)** UITableViewDataSource 的協(xié)議方法。
首先,在
FeedViewController **里加上這個常量:

let basicCellIdentifier = "BasicCell"

這可以讓你用這個標識在 storyboard 里取得 ** BasicCell 。
接下來,用
tableView(_:numberOfRowsInSection:) **返回從 Deviant Art 獲取到數(shù)據(jù)個數(shù):

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return items.count
}

然后用,下面的代碼替換** tableView(_:cellForRowAtIndexPath:) **這個方法。

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  return basicCellAtIndexPath(indexPath)
}
 
func basicCellAtIndexPath(indexPath:NSIndexPath) -> BasicCell {
  let cell = tableView.dequeueReusableCellWithIdentifier(basicCellIdentifier) as! BasicCell
  setTitleForCell(cell, indexPath: indexPath)
  setSubtitleForCell(cell, indexPath: indexPath)
  return cell
}
 
func setTitleForCell(cell:BasicCell, indexPath:NSIndexPath) {
  let item = items[indexPath.row] as RSSItem
  cell.titleLabel.text = item.title ?? "[No Title]"
}
 
func setSubtitleForCell(cell:BasicCell, indexPath:NSIndexPath) {
  let item = items[indexPath.row] as RSSItem
  var subtitle: NSString? = item.mediaText ?? item.mediaDescription
 
  if let subtitle = subtitle {
 
    // Some subtitles are really long, so only display the first 200 characters
    if subtitle.length > 200 {
      cell.subtitleLabel.text = "\(subtitle.substringToIndex(200))..."
 
    } else {
      cell.subtitleLabel.text = subtitle as String
    }
 
  } else {
    cell.subtitleLabel.text = ""
  }
}

這邊發(fā)生什么:

  • 在 tableView(:cellForRowAtIndexPath:) 里,調(diào)用basicCellAtIndexPath(:) 方法獲取一個** BasicCell **。
  • 在basicCellAtIndexPath(:)里獲取一個** BasicCell **,使用setTitleForCell(:indexPath:) 方法設(shè)置 title label 的text,使用** setSubtitleForCell(_:indexPath:) **方法設(shè)置 subtitle label 的text,然后return 這個 cell。

現(xiàn)在,你需要實現(xiàn)... 等等,搞定了!就這么簡單?
編譯運行,你將看到這個表格:


圖片在哪呢?

這個app看起來還不錯,但是好像感覺缺了點什么?
噢,藝術(shù)在哪呢?

Deviant Art 上都是圖片,但這個 app 沒有顯示它們,你需要去修復(fù)下這個問題,不然,你的老大會讓你失去理智!

有個方法是在你的** BasicCell **上加一個 image view。

但是在 Deviant Art 上帶圖片的信息和純文字的信息都有,所以更好的做法是新建一個自定義 cell。

增加一個繼承自** BasicCell **的新類到項目里,名字叫 ImageCell,原因是你的新 cell 也需要 titleLabelsubtitleLabel。因此,有必要在基類里已經(jīng)存在一些方法的時候再做所有的事嗎?

打開** ImageCell.swift ** 并且增加下面的屬性:

@IBOutlet var customImageView: UIImageView!

這個屬性的名字是** customImageView,因為在 UITableViewCell 里已經(jīng)有了一個叫 ImageView **的屬性了。

打開** Main.storyboard ,選擇 basic cell 使用 ?C ,或者從菜單里選擇 Edit > Copy **。

選擇這個 table view 并且 按下 **?V ,或者 Edit > Paste **,去創(chuàng)建一個 cell 的新 copy 。

注意:如果你操作有誤,沒有得到想要的結(jié)果,記得使用** ?Z 或者 Edit > Undo **來撤銷操作。

選擇新 cell ,更改它的Custom Class為** ImageCell **。同樣的,更改它的 Reuse Identifier ** 為 ImageCell **。

在** ImageCell 上選擇 title label,更改它的位置 x 為128**,且寬度為 172。subtitle label 也是一樣。

Interface Builder 將會有一些警告,因為這些 labels 擺放的位置和約束設(shè)置的不一樣。

正確的做法是,選擇** ImageCell 上的 title label 刪掉 leading 這條約束,subtitle label 也是一樣刪掉 leading **約束。

現(xiàn)在選擇** ImageCell 的 title label,按照下面的截圖改變它的 Intrinsic Size Placeholder 的值。同樣的,改變 subtitle label 的 Intrinsic Size
(這是用來告訴 Interface Builder 去更新當前 view 的 frame 的占位符,Interface Builder 將不會顯示警告了。)


你需要在 cell 上增加一個 image view。但是現(xiàn)在的高度有一點小,所以選擇
ImageCell 修改它的 Row Height 141**。

現(xiàn)在,拖拽一個 image view 到** ImageCell **上,按照下面的截圖設(shè)置這個新 view 的位置和尺寸。


接下來,選擇這個 image view 做如下布局:

  • 設(shè)置 leading,topbottom20
  • 設(shè)置** width height 100**。
  • 確認 **Constrain to margins **沒有被勾選
  • 最后點擊** Add 5 constrain **的按鈕。


選擇** image view ** 顯示它的所有約束,然后選擇它的 bottom 約束來編輯它。在屬性編輯器里,改變** Relation Greater Than or Equal 它的 Priority 999


同樣的,選擇 subtitle label 去顯示它的所有約束條件,然后選擇它的
bottom ,在屬性編輯器里,改變 Relation Greater Than or Equal 它的 Priority**為 1000。

這是告訴 auto layout 在** imageView subtitleLabel 向下的約束都為20個點的時候,打破 imageView 的,遵循 subtitleLabel 的。(旁白:因為它的優(yōu)先級高,這樣避免約束沖突。)

然后將 image view 的** height width 約束的優(yōu)先級 Priority 設(shè)置成 999 。
這是因為自定義的約束有時候會和系統(tǒng)定義的約束之間會產(chǎn)生沖突,這時就是告訴 auto layout ,“如果一定要這么做,就打破這些自定義的約束吧”
旁白
:系統(tǒng)定義的優(yōu)先級高,1000。)

在大部分情況下,auto layout 都會滿足這些約束。在極少數(shù)的情況下它會打破這些約束,比如在改變設(shè)備方向時,但通常都是1-2的像素偏差,不明顯。

提示: 尤其在 table view cell上, auto layout 不總是很明顯的提示這些約束。
如果你在控制臺上看到這些 auto layout 不得不打破一個約束的警告信息,你就要試著去調(diào)整下你的約束條件的** priorities **了。

最后,選擇 ImageCell上的 title label 使用 Pin Button 來設(shè)置下 leading 約束為 8。subtitle label 也是一樣的。

現(xiàn)在你的** ImageCell **上的約束看起來是這個樣子的:


你需要選擇** ImageCell 和 image view 的 customImageView ** outlet 進行鏈接。

你需要一個** ImageCell 跳轉(zhuǎn)到 Deviant Media 場景的鏈接(segue),
這樣用戶點擊
ImageCell **就可以看到詳情了。

像之前設(shè)置 basic cell 一樣, 選擇** ImageCell ,按住 control 并拖拽到 Deviant Media 場景,然后在 Selection Segue 選項中選擇 Push **。

確認你的** Accessory 改回為 None **。

非常好,你的** ImageCell **已經(jīng)設(shè)置完成了!現(xiàn)在你可以加一些代碼讓它顯示出來了。

顯示這些圖片!

打開** FeedViewController.swift ** 在上面增加一個常量:

let imageCellIdentifier = "ImageCell"

接下來替換** tableView(_:cellForRowAtIndexPath:) **的代碼:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  if hasImageAtIndexPath(indexPath) {
    return imageCellAtIndexPath(indexPath)
 
  } else {
    return basicCellAtIndexPath(indexPath)
  }
}
 
func hasImageAtIndexPath(indexPath:NSIndexPath) -> Bool {
  let item = items[indexPath.row]
  let mediaThumbnailArray = item.mediaThumbnails as! [RSSMediaThumbnail]
 
  for mediaThumbnail in mediaThumbnailArray {
    if mediaThumbnail.url != nil {
      return true
    }
  }
 
  return false
}
 
func imageCellAtIndexPath(indexPath:NSIndexPath) -> ImageCell {
  let cell = self.tableView.dequeueReusableCellWithIdentifier(imageCellIdentifier) as! ImageCell
  setImageForCell(cell, indexPath: indexPath)
  setTitleForCell(cell, indexPath: indexPath)
  setSubtitleForCell(cell, indexPath: indexPath)
  return cell
}
 
func setImageForCell(cell:ImageCell, indexPath:NSIndexPath) {
  let item: RSSItem = items[indexPath.row]
 
  // mediaThumbnails are generally ordered by size,
  // so get the second mediaThumbnail, which is a
  // "medium" sized image
 
  var mediaThumbnail: RSSMediaThumbnail?
 
  if item.mediaThumbnails.count >= 2 {
    mediaThumbnail = item.mediaThumbnails[1] as? RSSMediaThumbnail
 
  } else {
    mediaThumbnail = (item.mediaThumbnails as NSArray).firstObject as? RSSMediaThumbnail
  }
 
  cell.customImageView.image = nil
 
  if let url = mediaThumbnail?.url {
    cell.customImageView.setImageWithURL(url)
  }
}

像上面創(chuàng)建** BasicCell **一樣,但是有一點不同,有一些新的代碼:

  • hasImageAtIndexPath(_:) 檢查IndexPath下的item的** mediaThumbnail的 url 不為空。如果不為空的要使用 ImageCell **來展示數(shù)據(jù)。
  • **imageCellAtIndexPath(:) basicCellAtIndexPath(:) 一樣,但是它要用 setImageForCell(_:indexPath:) **來設(shè)置下圖片。
  • ** setImageForCell(:indexPath:) ** 嘗試獲取第二個 media thumbnail。使用 AFNetworking提供的** setImageWithURL(:) **方法來獲取圖片。

編譯運行,你將看到漂亮的藝術(shù)圖片!默認,app 檢索 “popular“ 分類的數(shù)據(jù),但你也能按照藝術(shù)家搜索。

試著輸入** by:CheshireCatAr t** 并搜索。這是我的一個朋友,Devin Kraft,他是一個杰出的畫家。(查看他的website)。

這是我很喜歡的一位,他在 Deviant Art 上很活躍,發(fā)過作品和博客。因此,他的賬號是個很好的測試賬號,可以測試帶圖片的 cell 和不帶圖片的 cell。


這個 app 已經(jīng)看起來很漂亮了,但是你還可以讓你的老大對你的技能信心提升一到兩個級別。

優(yōu)化表格

還記得很早之前設(shè)置** estimatedRowHeight 160.0 的時候嗎?這個屬性是在 BasicCell **的豎屏方向上工作的。但是這個值非常的不準。

你可以使用** UITableViewDelegate **提供的,在運行時給 cell 估算高度的方法替換這個值。

改之前,你需要刪除** configureTableView(): **里的一行代碼:

tableView.estimatedRowHeight = 160.0

現(xiàn)在,在** // MARK: UITextFieldDelegate **組下增加下面的方法。(實際上,你可以在這個類的任何地方增加它們,但這樣結(jié)構(gòu)比較清楚易讀。)

// MARK: UITableViewDelegate
 
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
  if isLandscapeOrientation() {
    return hasImageAtIndexPath(indexPath) ? 140.0 : 120.0
  } else {
    return hasImageAtIndexPath(indexPath) ? 235.0 : 155.0
  }
}
 
func isLandscapeOrientation() -> Bool {
  return UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation)
}

這個預(yù)估 cell 高的方法很簡單。檢查當前方向是否是橫屏并且如果當前 index path 有圖片的話,返回一個預(yù)先裁定的值。

提示:不論你是實現(xiàn)的代理方法,還是簡單的設(shè)置了一個** estimatedRowHeight 固定的值。
這個 table view 會在代理方法和
estimatedRowHeight 的值之中選擇一個。它會影響到滑動的指示條和滑動的性能。(是否卡頓)
如果你的 cell 預(yù)估的高度不太正確,那么在滑動的時候就會卡頓,滑動指示條不太準,內(nèi)容也會混亂。
如果你的預(yù)估是準確的,計算就會慢,table view 滾動也會變慢。
這個成功的關(guān)鍵就在于在準確和不準確之間找一個平衡,減少不必要的計算成本。
旁白
:難道自己慢慢調(diào)試這個數(shù)值嗎?)

你還是使用固定的值來估算的高度,但是現(xiàn)在可以根據(jù) cell 的類型和設(shè)備的方向來設(shè)置更合理的高度了。你可以設(shè)置自己感興趣的值,但是記住訣竅就是能讓它計算的更快就行。

編譯并運行,你應(yīng)該能看到 table view 滑動的很流暢,看起來很棒。

這就是方法的最后實現(xiàn),這個 app 現(xiàn)在完成了!


從這去哪呢

你可以去下載完成項目,在這
旁白:上面的路徑下載下來的項目編譯會報錯,因為它是用Xcode6.3和Swift1.2做的,轉(zhuǎn)換到最新語法之后,在像上面已經(jīng)提到過的修改一個方法的代碼,添加http白名單。你也可以下載這里已經(jīng)修改過的代碼

Table views 可能是iOS里面組織數(shù)據(jù)視圖中最常用的了。你的 apps 會很復(fù)雜,你可能要使用各種類型的自定義 cell 來布局。幸運的是 iOS8 和 auto layout 能很容易的做到這些。
旁白:不幸的是,有多少 app 是只支持iOS8以上的呢。)

如果你有一些問題或建議,請在下面留言。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 自適應(yīng)Table View Cells 注意:這篇教程支持最新的Xcode 7.3,iOS 9和Swift 2.2...
    張嘉夫閱讀 3,055評論 9 50
  • 我認為這是最好的建議:不斷的思考你怎樣才能把事情做得更好并且不斷的質(zhì)疑自己。-Elon Musk,Tesla Mo...
    運營老周閱讀 5,290評論 1 9
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,241評論 4 61
  • 沒有人會因為你的一點小破事而改變什么,你的悲傷太渺小,無論放在哪里就像石沉大海。只有自己去讓自己快速成長起來,否則...
    晗凌閱讀 115評論 0 0
  • 年近四十,卻越發(fā)的覺得自己年輕了起來,對比以前的照片,我不禁發(fā)出感慨,誰說女人四十豆腐渣,只要你自己用心澆灌,一樣...
    子林林閱讀 489評論 2 2