激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - IOS - iOS App開發(fā)中使cell高度自適應(yīng)的黑魔法詳解

iOS App開發(fā)中使cell高度自適應(yīng)的黑魔法詳解

2021-01-11 15:59Vito Zhang IOS

這篇文章主要介紹了iOS App開發(fā)中使cell高度自適應(yīng)的黑魔法詳解,作者利用iOS8以后的新特性講解了TableView、CollectionView中的cell高度自適應(yīng)以及UITextView輸入內(nèi)容實(shí)時(shí)更新cell高度的方法,需要的朋友可以參考下

在使用 table view 的時(shí)侯經(jīng)常會(huì)遇到這樣的需求:table view 的 cell 中的內(nèi)容是動(dòng)態(tài)的,導(dǎo)致在開發(fā)的時(shí)候不知道一個(gè) cell 的高度具體是多少,所以需要提供一個(gè)計(jì)算 cell 高度的算法,在每次加載到這個(gè) cell 的時(shí)候計(jì)算出 cell 真正的高度。

在 ios 8 之前

沒(méi)有使用 autolayout 的情況下,需要實(shí)現(xiàn) table view delegate 的 tableview(tableview: uitableview, heightforrowatindexpath indexpath: nsindexpath) -> cgfloat 方法,在這個(gè)方法中計(jì)算并返回 cell 的高度。比如,我有一個(gè)可以顯示任意行數(shù)的純文本 cell,計(jì)算 cell 的代碼可以是這樣:

復(fù)制代碼 代碼如下:


override func tableview(tableview: uitableview, heightforrowatindexpath indexpath: nsindexpath) -> cgfloat {
    let content = self.datas[indexpath.row] as string

 

    let padding: cgfloat = 20
    let width = tableview.frame.size.width - padding * 2;

    let size = cgsizemake(width, cgfloat.max)
    let attributes = [nsfontattributename: uifont(name: "helvetica", size: 14)!]
    let frame = content.boundingrectwithsize(size,
        options: nsstringdrawingoptions.useslinefragmentorigin,
        attributes: attributes,
        context: nil)
    return frame.size.height+1;
}


上面的代碼是一個(gè)最簡(jiǎn)單的例子,這個(gè)例子看起來(lái)好像沒(méi)有什么問(wèn)題。但是通過(guò)查看這個(gè) delegate 方法的文檔后,可以知道,在每次 reload tableview 的時(shí)候,程序會(huì)先計(jì)算出每一個(gè) cell 的高度,等所有高度計(jì)算完畢,確定了 tableview 的總的高度后,才開始渲染視圖并顯示在屏幕上。這意味著在顯示 table view 之前需要執(zhí)行一堆的計(jì)算,并且這是在主線程中進(jìn)行的,如果計(jì)算量太大程序就很有可能出現(xiàn)卡頓感。比如: table view 的數(shù)據(jù)有上千條,或者計(jì)算高度的代碼中還要先獲取圖片再根據(jù)圖片計(jì)算高度,這些操作都是非常慢的。

 

如果在 cell 中使用了 autolayout,在計(jì)算 cell 高度時(shí)會(huì)更麻煩。有興趣的可以看這里有篇關(guān)于如何在 autolayout 下動(dòng)態(tài)計(jì)算高度 的文章。

為什么不能等滾動(dòng)到某個(gè) cell 的時(shí)候,再調(diào)用計(jì)算這個(gè) cell 高度的 delegate 呢?原因是 tableview 需要獲得它的內(nèi)容的總高度,用這個(gè)高度去確定滾動(dòng)條的大小等。直到 ios 7 uitableviewdelegate中添加了新的 api

tableview(tableview: uitableview, estimatedheightforrowatindexpath indexpath: nsindexpath) -> cgfloat
這個(gè)方法用于返回一個(gè) cell 的預(yù)估高度,如果在程序中實(shí)現(xiàn)了這個(gè)方法,tableview 首次加載的時(shí)候就不會(huì)調(diào)用heightforrowatindexpath 方法,而是用 estimatedheightforrowatindexpath 返回的預(yù)估高度計(jì)算 tableview 的總高度,然后 tableview 就可以顯示出來(lái)了,等到 cell 可見的時(shí)候,再去調(diào)用heightforrowatindexpath 獲取 cell 的正確高度。

通過(guò)使用estimatedheightforrowatindexpath 這個(gè) delegate 方法,解決了首次加載 table view 出現(xiàn)的性能問(wèn)題。但還有一個(gè)麻煩的問(wèn)題,就是在 cell 沒(méi)有被加載的時(shí)候計(jì)算 cell 的高度,上面給出的代碼中,僅僅是計(jì)算一個(gè) nsstring 的高度,就需要不少代碼了。這種計(jì)算實(shí)際上是必須的,然而在 ios 8 開始,你可能可以不用再寫這些煩人的計(jì)算代碼了!

ios 8 的魔法

在 ios 8 中,self size cell 提供了這樣一種機(jī)制:cell 如果有一個(gè)確定的寬度/高度,autolayout 會(huì)自動(dòng)根據(jù) cell 中的內(nèi)容計(jì)算出對(duì)應(yīng)的高度/寬度。

tableview 中的 cell 自適應(yīng)

要讓 table view 的 cell 自適應(yīng)內(nèi)容,有幾個(gè)要點(diǎn):

設(shè)置的 autolayout 約束必須讓 cell 的 contentview 知道如何自動(dòng)延展。關(guān)鍵點(diǎn)是 contentview 的 4 個(gè)邊都要設(shè)置連接到內(nèi)容的約束,并且內(nèi)容是會(huì)動(dòng)態(tài)改變尺寸的。
uitableview 的 rowheight 的值要設(shè)置為 uitableviewautomaticdimension
和 ios 7 一樣,可以實(shí)現(xiàn) estimatedheightforrowatindexpath 方法提升 table view 的第一次加載速度。
任何時(shí)候 cell 的 intrinsiccontentsize 改變了(比如 table view 的寬度變了),都必須重新加載 table view 以更新 cell。
例子

在 xcode 中新建一個(gè)項(xiàng)目,在 storyboard 中創(chuàng)建一個(gè) uitableviewcontroller 的 ib,創(chuàng)建一個(gè)如下樣子的 cell:

iOS App開發(fā)中使cell高度自適應(yīng)的黑魔法詳解

這個(gè) cell 中有 3 個(gè)元素,其中 imageview 的 autolayout 約束為:

  • imageview 左邊離 contentview 左邊 0

  • imageview 上邊離 contentview 上邊 0

  • imageview 的 width 和 height 為 80

  • imageview 下邊離 contentview 下邊大于等于 0(為了防止內(nèi)容太少,導(dǎo)致 cell 高度小于圖片高度)

titlelabel 的 autolayout 約束為:

  • titlelabel 左邊離 imageview 右邊 8

  • titlelabel 上邊和 imageview 上邊在同一只線上

  • titlelabel 右邊離 contentview 右邊 0

  • titlelabel 下邊離 description 上邊 8

  • titlelabel 的高度小于等于 22,優(yōu)先級(jí)為 250

descriptionlabel 的約束為:

  • descriptionlabel 左邊和 titlelabel 左邊在同一直線上

  • descriptionlabel 上邊里 titlelabel 8

  • descriptionlabel 下邊里 contentview 下邊 0

  • descriptionlabel 右邊離 contentview 右邊 0

然后在這個(gè) ib 對(duì)應(yīng)的 uitableviewcontroller 中加載一些數(shù)據(jù)進(jìn)去,顯示效果如圖:

iOS App開發(fā)中使cell高度自適應(yīng)的黑魔法詳解

實(shí)現(xiàn)這個(gè)效果,我除了設(shè)置了 autolayout,還設(shè)置了 tableview 的 rowheight = uitableviewautomaticdimension,然后就是這樣了。一點(diǎn)計(jì)算 cell 高度的代碼都沒(méi)有!!我連 heightforrowatindexpath都不用實(shí)現(xiàn),真的是….爽出味啊!所以如果已經(jīng)在開發(fā) ios 8 only 的應(yīng)用了一定要用autolayout,把煩人的計(jì)算交給 autolayout 去吧。

collectionview 中的 cell 自適應(yīng)

在 collection view 中也能讓 cell 自適應(yīng)內(nèi)容大小,如果 uicollectionview 的 layout 是一個(gè) uicollectionviewflowlayout,只需要將 layout.itemsize = ... 改成 layout.estimateditemsize = ...。 只要設(shè)置了 layout 的 estimateditemsize,collection view 就會(huì)根據(jù) cell 里面的 autolayout 約束去確定cell 的大小。

原理:

  • collection view 根據(jù) layout 的 estimateditemsize 算出估計(jì)的 contentsize,有了 contentsize collection view 就開始顯示

  • collection view 在顯示的過(guò)程中,即將被顯示的 cell 根據(jù) autolayout 的約束算出自適應(yīng)內(nèi)容的 size

  • layout 從 collection view 里獲取更新過(guò)的 size attribute

  • layout 返回最終的 size attribute 給 collection view

  • collection 使用這個(gè)最終的 size attribute 展示 cell

uitextview 輸入內(nèi)容實(shí)時(shí)更新 cell 的高度
在一個(gè)動(dòng)態(tài)數(shù)據(jù)的 table view 中,cell 根據(jù) text view 內(nèi)容的輸入實(shí)時(shí)改變 cell 和 table view 的高度。自動(dòng)計(jì)算 cell 高度的功能使用 ios 8 才支持的自適應(yīng) cell,先上圖,我們最終要實(shí)現(xiàn)的效果是這樣的:

iOS App開發(fā)中使cell高度自適應(yīng)的黑魔法詳解

實(shí)現(xiàn)上面效果的基本原理是:

  • 在 cell 中設(shè)置好 text view 的 autolayout,讓 cell 可以根據(jù)內(nèi)容自適應(yīng)大小

  • text view 中輸入內(nèi)容,根據(jù)內(nèi)容更新 textview 的高度

  • 調(diào)用 tableview 的 beginupdates 和 endupdates,重新計(jì)算 cell 的高度

將 text view 更新后的數(shù)據(jù)保存,以免 table view 滾動(dòng)超過(guò)一屏再滾回來(lái) text view 中的數(shù)據(jù)又不刷新成原來(lái)的數(shù)據(jù)了。

功能具體實(shí)現(xiàn)方法:

新建一個(gè)項(xiàng)目,拉出 tableviewcontroller,在 cell 上添加一個(gè) uitextview。

首先設(shè)置 text view 的 autolayout,比較關(guān)鍵的 constraint 是要設(shè)置 textview 的高度大于等于一個(gè)值。如圖:

iOS App開發(fā)中使cell高度自適應(yīng)的黑魔法詳解

然后,設(shè)置 uitextview 的 scrollenable 為 no。這一點(diǎn)很關(guān)鍵,如果不設(shè)置為 no,uitextview 在內(nèi)容超出 frame 后,重新設(shè)置 text view 的高度會(huì)失效,并出現(xiàn)滾動(dòng)條。

iOS App開發(fā)中使cell高度自適應(yīng)的黑魔法詳解

根據(jù)剛才在 storyboard 中創(chuàng)建的 cell,新建一個(gè) uitableviewcell 類。

復(fù)制代碼 代碼如下:


#import <uikit/uikit.h>

 

@interface textviewcell : uitableviewcell

@property (weak, nonatomic) iboutlet uitextview *textview;

@end


創(chuàng)建 tableviewcontroller 并初始化一些數(shù)據(jù)

復(fù)制代碼 代碼如下:


#import "tableviewcontroller.h"
#import "textviewcell.h"

 

@interface tableviewcontroller ()

@property (nonatomic, strong) nsarray *data;

@end

 

復(fù)制代碼 代碼如下:


@implementation tableviewcontroller

 

- (void)viewdidload {
  [super viewdidload];

  //  支持自適應(yīng) cell
  self.tableview.estimatedrowheight = 100;
  self.tableview.rowheight = uitableviewautomaticdimension;

  self.data = @[@"cell 1 ", @"cell 2", @"cell 3", @"cell 4", @"cell 5", @"cell 6", @"cell 7", @"cell 8"];
}

#pragma mark - table view data source

- (nsinteger)tableview:(uitableview *)tableview numberofrowsinsection:(nsinteger)section {
  return [self.data count];
}

- (uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath {
  textviewcell *cell = [tableview dequeuereusablecellwithidentifier:@"textviewcell" forindexpath:indexpath];
  cell.textview.text = self.data[indexpath.row];
  return cell;
}


使用上面的代碼項(xiàng)目已經(jīng)可以運(yùn)行了,但是 text view 還不能自動(dòng)更新大小,下面來(lái)實(shí)現(xiàn) text view 根據(jù)內(nèi)容計(jì)算高度

 

先在 storyboard 中,將 uitextview 的 delegate 設(shè)置為 cell

iOS App開發(fā)中使cell高度自適應(yīng)的黑魔法詳解

在 textviewcell.m 中實(shí)現(xiàn) - (void)textviewdidchange:(uitextview *)textview,每次 text view 內(nèi)容改變的時(shí)候,就重新計(jì)算一次 text view 的大小,并讓 table view 更新高度。

復(fù)制代碼 代碼如下:


#import "textviewcell.h"

 

@implementation textviewcell

- (void)textviewdidchange:(uitextview *)textview
{
  cgrect bounds = textview.bounds;

  // 計(jì)算 text view 的高度
  cgsize maxsize = cgsizemake(bounds.size.width, cgfloat_max);
  cgsize newsize = [textview sizethatfits:maxsize];
  bounds.size = newsize;

  textview.bounds = bounds;

  // 讓 table view 重新計(jì)算高度
  uitableview *tableview = [self tableview];
  [tableview beginupdates];
  [tableview endupdates];
}

- (uitableview *)tableview
{
  uiview *tableview = self.superview;

  while (![tableview iskindofclass:[uitableview class]] && tableview) {
    tableview = tableview.superview;
  }

  return (uitableview *)tableview;
}

@end


這樣就已經(jīng)實(shí)現(xiàn)了 text view 改變內(nèi)容自動(dòng)更新 cell 高度的功能,這篇文章沒(méi)有涉及到計(jì)算 cell 高度的代碼,因?yàn)橛?jì)算 cell 高度的工作全部交給 ios 8 的 autolayout 自動(dòng)計(jì)算了,這讓我們少寫了許多令人痛苦的代碼。

 

最后:為了防止 table view 過(guò)長(zhǎng),導(dǎo)致滾動(dòng)后重新加載 cell,會(huì)讓 text view 中的內(nèi)容還原的問(wèn)題,我們應(yīng)該在更新了 text view 的內(nèi)容之后保存數(shù)據(jù)。(如果是在編輯狀態(tài)下,還需要考慮取消編輯后的回滾功能。 普通數(shù)組數(shù)據(jù),可以保存一個(gè)原始數(shù)據(jù)的副本,如果用戶取消編輯,就設(shè)置 data 為原始數(shù)據(jù)的副本。如果是 nsmanagedobject 對(duì)象可以使用 nsundomanage,不過(guò)這些已經(jīng)超出本篇文章的內(nèi)容范圍了。)

為了在 text view 更新后能讓 tableviewcontroller 中的 data 更新,需要為 cell 添加一個(gè) delegate,在 text view 更新后調(diào)用 delegate,tableviewcontroller 中收到 delegate 信息后更新 data。

修改后的 textviewcell.h

復(fù)制代碼 代碼如下:


#import <uikit/uikit.h>

 

@protocol textviewcelldelegate;

@interface textviewcell : uitableviewcell

@property (weak, nonatomic) iboutlet uitextview *textview;

@property (weak, nonatomic) id<textviewcelldelegate> delegate;

@end

 

復(fù)制代碼 代碼如下:


@protocol textviewcelldelegate <nsobject>

 

- (void)textviewcell:(textviewcell *)cell didchangetext:(nsstring *)text;

@end


在 textview.m的 - (void)textviewdidchange:(uitextview *)textview 中添加 delegate 的調(diào)用

復(fù)制代碼 代碼如下:


- (void)textviewdidchange:(uitextview *)textview
{
  if ([self.delegate respondstoselector:@selector(textviewcell:didchangetext:)]) {
    [self.delegate textviewcell:self didchangetext:textview.text];
  }

 

  // 計(jì)算 text view 的高度
  ...
  // 讓 table view 重新計(jì)算高度
  ...
}


最后在 tableviewcontroller.m 的最后實(shí)現(xiàn) textviewcelldelegate 的方法,更新 data

復(fù)制代碼 代碼如下:


#pragma mark - textviewcelldelegate

 

- (void)textviewcell:(textviewcell *)cell didchangetext:(nsstring *)text
{
  nsindexpath *indexpath = [self.tableview indexpathforcell:cell];

  nsmutablearray *data = [self.data mutablecopy];
  data[indexpath.row] = text;
  self.data = [data copy];
}

 

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲美女网站在线观看 | 操碰视频在线观看 | 特级黄色一级毛片 | 黄色网址免费在线 | 久久伊人精品视频 | 精品国产高清一区二区三区 | 久久羞羞 | 亚洲一区二区不卡视频 | av在线播放电影 | 蜜桃网站免费 | 国产一区精品在线观看 | 国产亚洲欧美一区久久久在 | 在线观看国产免费视频 | 伊人久操视频 | 欧美三级美国一级 | 欧美一级免费在线观看 | 蜜桃麻豆视频 | 精品一区二区三区在线观看国产 | 国产流白浆高潮在线观看 | 免费小毛片 | 九九热精品在线视频 | 香蕉秀| 色综合久久99 | 久久精品中文字幕 | 1024亚洲天堂 | 成人在线免费视频观看 | 在线视频观看一区二区 | 午夜视频福利 | 最新亚洲视频 | 久久久精品视频国产 | 在线成人看片 | 国产精品aⅴ | 色七七网站 | 草久免费 | 欧美精品一区二区三区四区 | 日日操夜夜操狠狠操 | 久久久一区二区三区视频 | 欧洲精品久久 | 久草干| 欧美综合在线观看视频 | 日夜操天天干 |