iOS - UITableViewController

weixin_34321977發表於2016-08-10

前言

    NS_CLASS_AVAILABLE_IOS(2_0) @interface UITableView : UIScrollView <NSCoding>
    @available(iOS 2.0, *)       public class UITableView : UIScrollView, NSCoding

1、tableView 的建立

  • Objective-C

    • 遵守 UITableViewDelegate, UITableViewDataSource 協議

    • 資料來源 初始化

          // 宣告資料來源,必須宣告為全域性變數
          @property(nonatomic, retain)NSMutableArray *myDataArray;
      
          // 資料來源陣列初始化,定義一個可變陣列做為表格的資料來源
          myDataArray = [[NSMutableArray alloc] init];
      
          NSArray *array1 = @[@"UIWindow", @"UIApplication", @"UIView", @"UILabel", 
                              @"UIProgressView", @"UIAlertView", @"UIActionSheet", @"UIPickerView"];
          NSArray *array2 = @[@"視窗", @"應用", @"檢視", @"標籤", @"進度條", @"警告框", 
                              @"操作表", @"選擇框", @"風火輪", @"影象檢視", @"網頁檢視", @"滾動檢視", 
                              @"多行文字檢視"];
      
          // 向資料來源中新增資料
          [myDataArray addObject:array1];
          [myDataArray addObject:array2];
    • tableView 初始化

          // 宣告表格檢視物件,頭標題和腳標題懸浮顯示,預設型別
          UITableView *myTableView = [[UITableView alloc] initWithFrame:self.view.bounds];
      
          // 設定 tableView 的代理
          myTableView.delegate = self;
          myTableView.dataSource = self;
      
          // 將 tableView 新增到視窗中
          [self.view addSubview:myTableView];
    • 協議方法

          // 設定分段數
          - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
      
              // 資料來源陣列為多維陣列時,用陣列計算
              return myDataArray.count;
          }
      
          // 設定行數
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
      
              // section 段,返回每段中有多少行
              return [[myDataArray objectAtIndex:section] count];
          }
      
          // 設定每一行顯示的內容
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 建立標識詞,隨意設定,但不能和其它 tableView 的相同
              static NSString *indentifier = @"testIdentifier";
      
              // 根據標誌詞先從複用佇列裡查詢
              UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier];
      
              // 複用佇列中沒有時再建立
              if (cell == nil) {
      
                  // 建立新的 cell,預設為主標題模式
                  cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier];
              }
      
              // 設定每一行顯示的文字內容
              cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
      
              // indexPath.section 分段數,indexPath.row 行數,設定圖片內容,圖片在 Cell 的左端,圖片大小自動壓縮
              cell.imageView.image = [UIImage imageNamed:@"HQ_0003"];
      
              return cell;
          }
  • Swift

    • 遵守 UITableViewDelegate, UITableViewDataSource 協議

    • 資料來源 初始化

          // 宣告資料來源,必須宣告為全域性變數
          var myDataArray:[[String]] = Array()
      
          let array1:[String] = ["UIWindow", "UIApplication", "UIView", "UILabel", "UIProgressView", 
                                "UIAlertView", "UIActionSheet", "UIPickerView"]
          let array2:[String] = ["視窗", "應用", "檢視", "標籤", "進度條", "警告框", "操作表", "選擇框", 
                                "風火輪", "影象檢視", "網頁檢視", "滾動檢視", "多行文字檢視", "工具條"]
      
          // 向資料來源中新增資料
          myDataArray.append(array1)
          myDataArray.append(array2)
    • tableView 初始化

          // 宣告表格檢視物件,頭標題和腳標題懸浮顯示,預設型別
          let myTableView:UITableView = UITableView(frame: self.view.bounds)
      
          // 設定 tableView 的代理
          myTableView.delegate = self
          myTableView.dataSource = self
      
          // 將 tableView 新增到視窗中
          self.view.addSubview(myTableView)
    • 協議方法

          // 設定分段數
          func numberOfSectionsInTableView(tableView: UITableView) -> Int {
      
              // 資料來源陣列為多維陣列時,用陣列計算
              return myDataArray.count
          }
      
          // 設定行數
          func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      
              // section 段,返回每段中有多少行
              return myDataArray[section].count
          }
      
          // 設定每一行顯示的內容
          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      
              // 建立標識詞,隨意設定,但不能和其它 tableView 的相同
              let indentifier = "testIdentifier"
      
              // 根據標誌詞先從複用佇列裡查詢
              var cell = tableView.dequeueReusableCellWithIdentifier(indentifier)
      
              // 複用佇列中沒有時再建立
              if cell == nil {
      
                  // 建立新的 cell,預設為主標題模式
                  cell = UITableViewCell(style: .Default, reuseIdentifier: indentifier)
              }
      
              // 設定每一行顯示的文字內容
              cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row]
      
              // indexPath.section 分段數,indexPath.row 行數,設定圖片內容,圖片在 Cell 的左端,圖片大小自動壓縮
              cell!.imageView?.image = UIImage(named: "HQ_0003")
      
              return cell!
          }

2、tableView 的設定

  • Objective-C

    • 設定資料來源初始化方式

          // 將陣列指向新的空間,可以不提前申請空間(不初始化)
          myDataArray = @[array1, array2];
      
          // 將陣列裡的所有資料替換成新的,必須提前申請空間
          myDataArray.array = @[array1, array2];
    • 設定分段的頭標題和腳標題

          // 設定分段的頭標題和腳標題的型別
          /*
              UITableViewStylePlain,     // 簡單模式,每個分段之間緊密連線,頭腳標題懸浮顯示,預設型別
              UITableViewStyleGrouped    // 分組模式,每個分段之間分開,頭腳標題跟隨移動,頭標題英文自動大寫
          */
      
              // 頭標題和腳標題懸浮顯示,預設型別
              UITableView *myTableView = [[UITableView alloc] init];
      
              UITableView *myTableView = [[UITableView alloc] initWithFrame:frame];
      
              // 帶顯示型別的設定
              UITableView *myTableView = [[UITableView alloc] initWithFrame:frame style:UITableViewStyleGrouped];
      
          // 設定分段的頭標題高度,UITableViewDelegate 協議方法
          - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
      
              return 40;
          }
      
          // 設定分段的腳標題高度,UITableViewDelegate 協議方法
          - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
      
              return 30;
          }
      
          // 設定分段的頭標題內容,UITableViewDataSource 協議方法
          - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
      
              if (0 == section) {
                  return @"English Header";
              }
              else {
                  return @"中文 Header";
              }
          }
      
          // 設定分段的腳標題內容,UITableViewDataSource 協議方法
          - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
      
              if (0 == section) {
                  return @"English Footer";
              }
              else {
                  return @"中文 Footer";
              }
          }
      
          // 分段頭標題檢視,UITableViewDelegate 協議方法,返回自定義的標題檢視
          - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
      
              if (!section) {
                  label.text = @"English Header";
              }
              else{
                  label.text = @"中文 Header";
              }
      
              return label;
          }
      
          // 分段腳標題檢視,UITableViewDelegate 協議方法
          - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
      
              if (!section) {
                  label.text = @"English Footer";
              }
              else{
                  label.text = @"中文 Footer";
              }
      
              // 返回自定義的標題檢視
              return label;
         }
    • 設定表格的表頭和表尾檢視

          // 設定表格的表頭檢視
          /*
              只有檢視的高度設定起作用
          */
          myTableView.tableHeaderView = myHeaderView;
      
          // 設定表格的表尾檢視
          /*
              只有檢視的高度設定起作用
          */
          myTableView.tableFooterView = myFooterView;
    • 設定表格的背景

          // 設定表格的背景檢視
          myTableView.backgroundView = myImageView;
      
          // 設定表格的背景顏色
          myTableView.backgroundColor = [UIColor blueColor];
    • 設定表格的分割線

          // 設定表格的分割線顏色
          /*
              設定為 clearColor 時即可隱藏(不顯示)所有分割線
          */
          myTableView.separatorColor = [UIColor redColor];
      
          // 設定表格的分割線型別
          /*
              UITableViewCellSeparatorStyleNone,         // 沒有分割線
              UITableViewCellSeparatorStyleSingleLine,   // 單線型,預設
      
              // 嵌刻線型,This separator style is only supported for grouped style
              UITableViewCellSeparatorStyleSingleLineEtched
          */
          myTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
      
          // 設定表格的分割線邊距
          /*
              上、左、下、右,只有左、右設定有效
              設定左邊距時會使標題相應的右移
              左邊距設定為 0 時,分割線不會緊靠左邊框
          */
          myTableView.separatorInset = UIEdgeInsetsMake(0, 10, 0, 10);
      
          // 清除表格多餘的分割線
          /*
              表格為 UITableViewStylePlain 型別時,若表格的內容沒有佔滿螢幕時,沒有設定內容的部分表格也會有分割線
              建立自定義的 view,將該 view 的背景顏色清空(預設為透明),並新增到表格的腳檢視上
          */
          myTableView.tableFooterView = [[UIView alloc] init];
      
          // 設定表格分割線左邊距為零
          [myTableView setSeparatorInset:UIEdgeInsetsZero];
          [myTableView setLayoutMargins:UIEdgeInsetsZero];
      
          - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
              // UITableViewDelegate 協議方法
      
              [cell setSeparatorInset:UIEdgeInsetsZero];
              [cell setLayoutMargins:UIEdgeInsetsZero];
          }
      
          // 自定義表格分割線
          /*
              系統分割線的左邊無法緊靠表格左邊框,隱藏系統分割線,自定義檢視,新增到 Cell 的下邊實現
              同時可以清除表格在 UITableViewStylePlain 型別時的多餘分割線
          */
      
          myTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
      
          if (cell == nil) {
      
              // 建立新的 cell
              cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier];
      
              // 新增自定義分割線檢視
              CGRect frame = CGRectMake(0, cell.contentView.bounds.size.height, self.view.bounds.size.width, 1);
              UIView *mySeparatorView = [[UIView alloc] initWithFrame:frame];
              mySeparatorView.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.5];
              [cell.contentView addSubview:mySeparatorView];
          }
    • 設定表格的行高

          // 屬性設定
          /*
              設定全部行的高度,預設為 44
          */
          myTableView.rowHeight = 60;
      
          // 協議方法設定
          /*
              可單獨設定每一行的高度,預設為 44
          */
          - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              return 60;
          }
      
          // 設定估計行高
          /*
              設定全部行的高度
          */
          self.tableView.estimatedRowHeight = 80;
      
          // 協議方法設定估計行高
          /*
              可單獨設定每一行的估計行高
          */
          - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              return 80;
          }
      
          // 設定自動計算行高
          self.tableView.rowHeight = UITableViewAutomaticDimension;
    • 設定表格的編輯開關狀態

          // 開啟表格的編輯模式
          /*
              default is NO. setting is not animated
          */
          myTableView.editing = YES;
      
          // 翻轉表格的編輯模式
          myTableView.editing = !myTableView.editing;
      
          // 翻轉表格的編輯模式
          [myTableView setEditing:!myTableView.editing animated:YES];
    • 設定表格選擇狀態

          // 設定表格普通模式下是否允許單選
          /*
              default is YES. Controls whether rows can be selected when not in editing mode
          */
          myTableView.allowsSelection = YES;
      
          // 設定表格在編輯模式下是否允許單選
          /*
              default is NO. Controls whether rows can be selected when in editing mode
          */
          myTableView.allowsSelectionDuringEditing = YES;
      
          // 設定表格普通模式下是否允許多選
          /*
              default is NO. Controls whether multiple rows can be selected simultaneously
          */
          myTableView.allowsMultipleSelection = YES;
      
          // 設定表格在編輯模式下是否允許多選
          /*
              default is NO. Controls whether multiple rows can be selected simultaneously in editing mode
          */
          myTableView.allowsMultipleSelectionDuringEditing = YES;
      
          // 取消表格選擇
          /*
              在表格選中協議方法中設定,表格點選變色後恢復原來顏色,設定後無法實現表格多選
          */
          [tableView deselectRowAtIndexPath:indexPath animated:YES];
    • 過載表格檢視

          // 過載表格檢視
          /*
              重走所有的表格檢視方法,重新整理所有的表格
          */
          [tableView reloadData];
      
          // 過載某一分段
          [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] 
                   withRowAnimation:UITableViewRowAnimationAutomatic];            
          // 過載某一個行
          [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
                           withRowAnimation:UITableViewRowAnimationAutomatic];
      
          // 刪除一個 cell
          /*
              只重新整理刪除的 cell
          */
          [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
                           withRowAnimation:UITableViewRowAnimationAutomatic];
      
          // 插入一個 cell
          /*
              只重新整理插入的 cell
          */
          [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
                           withRowAnimation:UITableViewRowAnimationAutomatic]; 
  • Swift

    • 設定分段的頭標題和腳標題

          // 設定分段的頭標題和腳標題的型別
          /*
              case Plain       // 簡單模式,每個分段之間緊密連線,頭腳標題懸浮顯示,預設型別
              case Grouped     // 分組模式,每個分段之間分開,頭腳標題跟隨移動,頭標題英文自動大寫
          */
      
              // 頭標題和腳標題懸浮顯示,預設型別
              let myTableView:UITableView = UITableView()
      
              let myTableView:UITableView = UITableView(frame: frame)
      
              // 帶顯示型別的設定     
              let myTableView:UITableView = UITableView(frame: frame, style: .Grouped)
      
          // 設定分段的頭標題高度,UITableViewDelegate 協議方法
          func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
      
              return  40
          }
      
          // 設定分段的腳標題高度,UITableViewDelegate 協議方法
          func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
      
              return 30
          }
      
          // 設定分段的頭標題內容,UITableViewDataSource 協議方法
          func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
      
              if 0 == section {
                  return "English Header"
              }
              else {
                  return "中文 Header"
              }
          }
      
          // 設定分段的腳標題內容,UITableViewDataSource 協議方法
          func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
      
              if 0 == section {
                  return "English Footer"
              }
              else {
                  return "中文 Footer"
              }
          }
      
          // 分段頭標題檢視,UITableViewDelegate 協議方法,返回自定義的標題檢視
          func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
      
              if 0 == section {
                  label.text = "English Header"
              }
              else {
                  label.text = "中文 Header"
              }
      
              return label
          }
      
          // 分段腳標題檢視,UITableViewDelegate 協議方法,返回自定義的標題檢視
          func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
      
              if 0 == section {
                  label.text = "English Footer"
              }
              else {
                  label.text = "中文 Footer"
              }
      
              return label
          }
    • 設定表格的表頭和表尾檢視

          // 設定表格的表頭檢視
          /*
              只有檢視的高度設定起作用
          */
          myTableView.tableHeaderView = myHeaderView
      
          // 設定表格的表尾檢視
          /*
              只有檢視的高度設定起作用
          */
          myTableView.tableFooterView = myFooterView
    • 設定表格的背景

          // 設定表格的背景檢視
          myTableView.backgroundView = myImageView
      
          // 設定表格的背景顏色
          myTableView.backgroundColor = UIColor.blueColor()
    • 設定表格的分割線

          // 設定表格的分割線顏色
          /*
              設定為 clearColor 時即可隱藏(不顯示)所有分割線
          */
          myTableView.separatorColor = UIColor.redColor()
      
          // 設定表格分割線的型別
          /*
              case None                // 沒有分割線
              case SingleLine          // 單線型,預設
              case SingleLineEtched    // 嵌刻線型,This separator style is only supported for grouped style
          */
          myTableView.separatorStyle = .SingleLine
      
          // 設定表格的分割線邊距
          /*
              上、左、下、右,只有左、右設定有效
              設定左邊距時會使標題相應的右移
              左邊距設定為 0 時,分割線不會緊靠左邊框
          */
          myTableView.separatorInset = UIEdgeInsetsMake(0, 10, 0, 10)
      
          // 清除表格多餘的分割線
          /*
              表格為 Plain 型別時,若表格的內容沒有佔滿螢幕時,沒有設定內容的部分表格也會有分割線
              建立自定義的 view,將該 view 的背景顏色清空(預設為透明),並新增到表格的腳檢視上
          */
          myTableView.tableFooterView = UIView()
      
          // 自定義表格分割線
          /*
              系統分割線的左邊無法緊靠表格左邊框,隱藏系統分割線,自定義檢視,新增到 Cell 的下邊實現
              同時可以清除表格在 Plain 型別時的多餘分割線
          */
      
          myTableView.separatorStyle = .None
      
          if cell == nil {
      
              // 建立新的 cell
              cell = UITableViewCell(style: .Default, reuseIdentifier: indentifier)
      
              // 新增自定義分割線檢視
              let frame:CGRect = CGRectMake(0, cell!.contentView.bounds.size.height, self.view.bounds.size.width, 1)
              let mySeparatorView:UIView = UIView(frame: frame)
              mySeparatorView.backgroundColor = UIColor.lightGrayColor().colorWithAlphaComponent(0.5)
              cell!.contentView.addSubview(mySeparatorView)
          }
    • 設定表格的行高

          // 屬性設定
          /*
              設定全部行的高度,預設為 44
          */
          myTableView.rowHeight = 60
      
          // 協議方法設定
          /*
              可單獨設定每一行的高度,預設為 44
          */
          func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
      
              return 60
          }
      
          // 設定估計行高
          /*
              設定全部行的高度
          */
          self.tableView.estimatedRowHeight = 80
      
          // 協議方法設定估計行高
          /*
              可單獨設定每一行的估計行高
          */
          func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
      
              return 80
          }
      
          // 設定自動計算行高
          self.tableView.rowHeight = UITableViewAutomaticDimension
    • 設定表格的編輯開關狀態

          // 開啟表格的編輯模式
          myTableView.editing = true
      
          // 翻轉表格的編輯模式,直接出現
          myTableView.editing = !myTableView.editing
      
          // 翻轉表格的編輯模式,帶動畫效果
          myTableView.setEditing(!myTableView.editing, animated: true)
    • 設定表格選擇狀態

          // 設定表格普通模式下是否允許單選
          /*
              default is YES. Controls whether rows can be selected when not in editing mode
          */
          myTableView.allowsSelection = false
      
          // 設定表格在編輯模式下是否允許單選
          /*
              default is NO. Controls whether rows can be selected when in editing mode
          */
          myTableView.allowsSelectionDuringEditing = true
      
          // 設定表格普通模式下是否允許多選
          /*
              default is NO. Controls whether multiple rows can be selected simultaneously
          */
          myTableView.allowsMultipleSelection = true
      
          // 設定表格在編輯模式下是否允許多選
          /*
              default is NO. Controls whether multiple rows can be selected simultaneously in editing mode
          */
          myTableView.allowsMultipleSelectionDuringEditing = true
      
          // 取消表格選擇
          /*
              在表格選中協議方法中設定,表格點選變色後恢復原來顏色,設定後無法實現表格多選
          */
          tableView.deselectRowAtIndexPath(indexPath, animated: true)
    • 過載表格檢視

          // 過載表格檢視
          /*
              重走所有的表格檢視方法,重新整理所有的表格
          */
          tableView.reloadData()
      
          // 過載某一分段
          tableView.reloadSections(NSIndexSet(index: indexPath.section), withRowAnimation: .Automatic)
      
          // 過載某一個行
          tableView.reloadRowsAtIndexPaths(Array(arrayLiteral: indexPath), withRowAnimation: .Automatic)
      
          // 刪除一個 cell
          /*
              只重新整理刪除的 cell
          */
          tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], 
                          withRowAnimation: .Automatic)
      
          // 插入一個 cell
          /*
              只重新整理插入的 cell
          */
          tableView.insertRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], 
                          withRowAnimation: .Automatic)

3、Cell 的建立(系統型別 Cell)

  • 使用 dequeueReuseableCellWithIdentifier: 可不註冊,但是必須對獲取回來的 cell 進行判斷是否為空,若空則手動建立新的 cell;
  • 使用 dequeueReuseableCellWithIdentifier: forIndexPath: 必須註冊,但返回的 cell 可省略空值判斷的步驟。

  • tableViewCell 的複用機制:

    • 1、當一個 cell 滑出螢幕的時候,會被放到複用佇列裡(系統自動操作)。
    • 2、當一個 cell 即將出現的時候,我們需要先從複用佇列裡查詢,找到就直接用,找不到就建立。
  • 系統 Cell 的建立方式:

    • 程式碼建立 cell。
    • 註冊 cell。
    • 從 storyboard 載入 cell。

3.1 建立 Cell

  • 可以設定建立的 Cell 的型別。

  • Objective-C

        // 設定每一行顯示的內容
        - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
            // 建立標識詞,標識詞隨意設定,但不能和其它 tableView 的相同
            static NSString *resumeID = @"testIdentifier";
    
            // 根據標識詞先從複用佇列裡查詢
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:resumeID];
    
            // 複用佇列中沒有時再建立
            if (cell == nil) {
    
               // 建立新的 cell,預設為主標題模式
               cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:resumeID];
            }
    
            // 設定每一行顯示的文字內容,覆蓋資料
            cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
            cell.imageView.image = [UIImage imageNamed:@"HQ_0003"];
    
            return cell;
        }
  • Swift

        // 設定每一行顯示的內容
        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    
            // 建立標識詞,標識詞隨意設定,但不能和其它 tableView 的相同
            let resumeID = "testIdentifier"
    
            // 根據標識詞先從複用佇列裡查詢
            var cell = tableView.dequeueReusableCellWithIdentifier(resumeID)
    
            // 複用佇列中沒有時再建立
            if cell == nil {
    
                // 建立新的 cell,預設為主標題模式
                cell = UITableViewCell(style: .Default, reuseIdentifier: resumeID)
            }
    
            // 設定每一行顯示的文字內容,覆蓋資料
            cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row]
            cell!.imageView?.image = UIImage(named: "HQ_0003")
    
            return cell!
        }

3.2 註冊 Cell

  • 在 tableView 建立時,從 iOS7 開始多了一種建立 cell 的方式(註冊),讓 tableView 註冊一種 cell,需要設定複用標誌。

  • 建立的 Cell 為 UITableViewCellStyleDefault 預設型別,無法修改。

  • Objective-C

        // 定義重用標識,定義為全域性變數
        NSString *resumeID = @"testIdentifier";
    
        // 註冊 cell
        - (void)viewDidLoad {
            [super viewDidLoad];
    
            // 註冊某個標識對應的 cell 型別
            [myTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:resumeID];
        }
    
        // 設定每一行顯示的內容
        - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
            // 根據標識詞先從複用佇列裡查詢,複用佇列中沒有時根據註冊的 cell 自動建立
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:resumeID forIndexPath:indexPath]; 
    
            // 設定每一行顯示的文字內容,覆蓋資料
            cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
            cell.imageView.image = [UIImage imageNamed:@"HQ_0003"];
    
            return cell;
        }
  • Swift

        // 定義重用標識,定義為全域性變數
        let resumeID = "testIdentifier"
    
        // 註冊 cell
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // 註冊某個標識對應的 cell 型別
            myTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: resumeID)
        }
    
        // 設定每一行顯示的內容
        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    
            // 根據標識詞先從複用佇列裡查詢,複用佇列中沒有時根據註冊的 cell 自動建立
            var cell = tableView.dequeueReusableCellWithIdentifier(resumeID, forIndexPath: indexPath)
    
            // 設定每一行顯示的文字內容,覆蓋資料
            cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row]
            cell!.imageView?.image = UIImage(named: "HQ_0003")
    
            return cell!
        }

3.3 StoryBoard 載入 Cell

  • 在 storyboard 中設定 UITableView 的 Dynamic Prototypes Cell。

    TableView5

  • 設定 cell 的重用標識。

    TableView6

  • 在程式碼中利用重用標識獲取 cell。

    • Objective-C

          // 設定每一行顯示的內容
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 建立標識詞,標識詞隨意設定,但不能和其它 tableView 的相同
              static NSString *resumeID = @"cell";
      
              // 根據標誌詞從先複用佇列裡查詢,複用佇列中沒有時根據 storyboard 自動建立
              UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:resumeID forIndexPath:indexPath]; 
      
              // 設定每一行顯示的文字內容,覆蓋資料
              cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
              cell.imageView.image = [UIImage imageNamed:@"HQ_0003"];
      
              return cell;
          }
    • Swift

          // 設定每一行顯示的內容
          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      
              // 建立標識詞,標識詞隨意設定,但不能和其它 tableView 的相同
              let resumeID = "cell"
      
              // 根據標誌詞從先複用佇列裡查詢,複用佇列中沒有時根據 storyboard 自動建立
              var cell = tableView.dequeueReusableCellWithIdentifier(resumeID, forIndexPath: indexPath)
      
              // 設定每一行顯示的文字內容,覆蓋資料
              cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row]
              cell!.imageView?.image = UIImage(named: "HQ_0003")
      
              return cell!
          }

4、Cell 的設定

  • UITableView 的每一行都是一個 UITableViewCell,通過 dataSource的tableView:cellForRowAtIndexPath: 方法來初始化每一行。

  • UITableViewCell 內部有個預設的子檢視 contentView,contentView 是 UITableViewCell 所顯示內容的父檢視,可顯示一些輔助指示檢視。輔助指示檢視的作用是顯示一個表示動作的圖示,可以通過設定 UITableViewCell 的 accessoryType 來顯示,預設是 UITableViewCellAccessoryNone (不顯示輔助指示檢視)。

  • contentView 下預設有 3 個子檢視
    • 其中 2 個是 UILabel (通過 UITableViewCell 的 textLabel 和 detailTextLabel 屬性訪問)。
    • 第 3 個是 UIImageView (通過 UITableViewCell 的 imageView 屬性訪問)。
  • UITableViewCell 還有一個 UITableViewCellStyle 屬性,用於決定使用 contentView 的哪些子檢視,以及這些子檢視在 contentView 中的位置。

    TableView11

  • UITableViewCell 結構

    TableView12

  • Objective-C

    • 設定 Cell 的型別

          /*
              UITableViewCellStyleDefault,  // 可選圖片 + 主標題模式,預設
              UITableViewCellStyleValue1,   // 可選圖片 + 左右主副標題模式,兩端對齊
              UITableViewCellStyleValue2,   // 左右主副標題模式,中間對齊
              UITableViewCellStyleSubtitle  // 可選圖片 + 上下主副標題模式
          */
      
          // 主標題模式,預設型別
          cell = [[UITableViewCell alloc] init];
      
          // 設定型別
          cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"testIdentifier"];
    • 設定 Cell 的顯示內容

          // 主標題模式
      
              // 設定主標題內容
              cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
      
              // 設定圖片內容,圖片在 Cell 的左端,圖片大小自動壓縮
              cell.imageView.image = [UIImage imageNamed:@"HQ_0003"];
      
          // 主副標題模式
      
              // 設定主標題內容
              cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
      
              // 設定副標題內容
              cell.detailTextLabel.text = [NSString stringWithFormat:@"第 %li 行", indexPath.row];
      
              // 設定圖片內容,圖片在 Cell 的左端,圖片大小自動壓縮
              cell.imageView.image = [UIImage imageNamed:@"HQ_0003"];
    • 往 cell 上新增自定義 view

      • 不是直接新增在 cell 上,cell 給我們提供了一個專門用來新增子 view 的東西,當 cell 被複用的時候,不允許建立物件,如果想給系統的 cell 上新增一些子 view,需要在建立 cell 的時候新增,然後在複用的時候修改子 view 顯示的內容。
          // 新增 cell 自定義 view 檢視
          UILabel *myCellView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 44)];
          myCellView.tag = 100;
      
          // 在建立新的 cell 後新增
          [cell.contentView addSubview:myCellView];
          // 設定 cell 自定義 view 顯示內容,在 cell 複用的時候設定
      
          UILabel *myCellView = (id)[self.view viewWithTag:100];
          myCellView.text = [NSString stringWithFormat:@"自定義 Cell View %@", 
                           [[myDataArray objectAtIndex:indexPath.section] 
                                         objectAtIndex:indexPath.row]];
    • 設定 Cell 的背景檢視

          // Cell 的背景檢視設定
          /*
              設定自定義檢視為 Cell 背景檢視
              圖片大小自動壓縮填充
          */
          cell.backgroundView = myBackgroundView;
      
      
          // Cell 選中時的背景檢視設定
          cell.selectedBackgroundView = myBackgroundView;
    • 設定 Cell 的顏色

          // Cell 背景顏色的設定
          cell.backgroundColor = [UIColor yellowColor];
      
          // 設定 cell 被點選時的顏色
          /*
              UITableViewCellSelectionStyleNone,     // 無色,表格點選時無顏色變化
              UITableViewCellSelectionStyleBlue,     // 灰色
              UITableViewCellSelectionStyleGray,     // 灰色
              UITableViewCellSelectionStyleDefault   // 灰色,預設
          */
          cell.selectionStyle = UITableViewCellSelectionStyleDefault;
      
          // 取消表格選擇變色
          /*
              在表格選中協議方法中設定,表格點選變色後恢復原來顏色,設定後無法實現表格多選
          */
          [tableView deselectRowAtIndexPath:indexPath animated:YES];
    • 設定 Cell 的附屬控制元件

          // Cell 附屬控制元件型別的設定
          /*
              如果附屬控制元件裡有 button ,這個 button 會獨立出來
      
              UITableViewCellAccessoryNone,                    // 無附屬控制元件,預設
              UITableViewCellAccessoryDisclosureIndicator,     // 箭頭,不能點選
              UITableViewCellAccessoryDetailDisclosureButton,  // 詳情按鈕和箭頭,可以點選
              UITableViewCellAccessoryCheckmark,               // 對號,不能點選
              UITableViewCellAccessoryDetailButton             // 詳情按鈕,可以點選
          */
          cell.accessoryType = UITableViewCellAccessoryCheckmark;
      
          // Cell 附屬控制元件檢視的設定
          /*
              設定自定義檢視為 Cell 的附屬控制元件,需設定 view 的大小
          */
          cell.accessoryView = myAccessoryView;
    • 獲取 cell

          // 獲取指定行的 cell
          UITableViewCell *cell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:3 inSection:0]];
      
          // 獲取所有被選中的行
          NSArray *indexPaths = [tableView indexPathsForSelectedRows];
  • Swift

    • 設定 Cell 的型別

          /*
              case Default   // 可選圖片 + 主標題模式
              case Value1    // 可選圖片 + 左右主副標題模式,兩端對齊
              case Value2    // 左右主副標題模式,中間對齊
              case Subtitle  // 可選圖片 + 上下主副標題模式
          */
      
          // 主標題模式,預設型別
          cell = UITableViewCell()
      
          // 設定型別
          cell = UITableViewCell(style: .Subtitle, reuseIdentifier: "testIdentifier")
    • 設定 Cell 的顯示內容

          // 主標題模式
      
              // 設定主標題內容
              cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row]
      
              // 設定圖片內容,圖片在 Cell 的左端,圖片大小自動壓縮
              cell!.imageView?.image = UIImage(named: "HQ_0003")
      
          // 主副標題模式
      
              // 設定主標題內容
              cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row]
      
              // 設定副標題內容
              cell!.detailTextLabel?.text = "第 \(indexPath.row) 行"
      
              // 設定圖片內容,圖片在 Cell 的左端,圖片大小自動壓縮
              cell!.imageView?.image = UIImage(named: "HQ_0003")
    • 往 cell 上新增自定義 view

      • 不是直接新增在 cell 上,cell 給我們提供了一個專門用來新增子 view 的東西,當 cell 被複用的時候,不允許建立物件,如果想給系統的 cell 上新增一些子 view,需要在建立 cell 的時候新增,然後在複用的時候修改子 view 顯示的內容。
          // 新增 cell 自定義 view 檢視
          let myCellView:UILabel = UILabel(frame: CGRectMake(0, 0, 300, 44))
          myCellView.tag = 100
      
          // 在建立新的 cell 後新增
          cell!.contentView.addSubview(myCellView)
      
          // 設定 cell 自定義 view 顯示內容,在 cell 複用的時候設定
      
          if (self.view.viewWithTag(100) != nil) {
      
              let myCellView = self.view.viewWithTag(100) as! UILabel
              myCellView.text = String("自定義 Cell View \(myDataArray[indexPath.section][indexPath.row])")
          }
    • 設定 Cell 的背景檢視

          // Cell 的背景檢視設定
          /*
              設定自定義檢視為 Cell 背景檢視
              圖片大小自動壓縮填充
          */
          cell!.backgroundView = myBackgroundView
      
      
          // Cell 選中時的背景檢視設定
          cell!.selectedBackgroundView = myBackgroundView
    • 設定 Cell 的顏色

          // Cell 背景顏色的設定
          cell!.backgroundColor = UIColor.yellowColor()
      
          // 設定 cell 被點選時的顏色
          /*
              case None     // 無色,表格點選時無顏色變化
              case Blue     // 灰色
              case Gray     // 灰色
              case Default  // 灰色,預設
          */
          cell!.selectionStyle = UITableViewCellSelectionStyle.Default
      
          // 取消表格選擇變色
          /*
              在表格選中協議方法中設定,表格點選變色後恢復原來顏色,設定後無法實現表格多選
          */
          tableView.deselectRowAtIndexPath(indexPath, animated: true)
    • 設定 Cell 的附屬控制元件

          // Cell 附屬控制元件型別的設定
          /*
              如果附屬控制元件裡有 button ,這個 button 會獨立出來
      
              case None                      // 無附屬控制元件,預設
              case DisclosureIndicator       // 箭頭,不能點選
              case DetailDisclosureButton    // 詳情按鈕和箭頭,可以點選
              case Checkmark                 // 對號,不能點選
              case DetailButton              // 詳情按鈕,可以點選
          */
          cell!.accessoryType = UITableViewCellAccessoryType.DetailButton
      
          // Cell 附屬控制元件檢視的設定
          /*
              設定自定義檢視為 Cell 的附屬控制元件,需設定 view 的大小
          */
          cell!.accessoryView = myAccessoryView
    • 獲取 cell

          // 獲取指定行的 cell
          let cell:UITableViewCell = tableView.cellForRowAtIndexPath(NSIndexPath(forItem: 3, inSection: 0))!
      
          // 獲取所有被選中的行
          let indexPaths:[NSIndexPath] = tableView.indexPathsForVisibleRows!

5、自定義資料模型的建立與引用

  • Objective-C

    • BookModel.h

          @interface BookModel : NSObject
      
          // 根據需要使用的資料建立資料模型屬性變數
      
          @property(nonatomic, copy)NSString *title;
          @property(nonatomic, copy)NSString *detail;
          @property(nonatomic, copy)NSString *icon;
          @property(nonatomic, copy)NSString *price;
      
          + (instancetype)bookModelWithDict:(NSDictionary *)dict;
      
          @end
    • BookModel.m

          + (instancetype)bookModelWithDict:(NSDictionary *)dict {
      
              BookModel *model = [[self alloc] init];
      
              // KVC - Key Value Coding
              [model setValuesForKeysWithDictionary:dict];
      
              return model;
          }
    • ViewController.m

          // 向資料來源中新增資料
      
              // 定義資料來源
              @property(nonatomic, retain)NSArray *myDataArray;
      
              // 懶載入
              - (NSArray *)myDataArray {
                  if (_myDataArray == nil) {
      
                      // 載入 plist 中的字典陣列
                      NSString *filePath = [[NSBundle mainBundle] pathForResource:@"bookData" ofType:@"plist"];
                      NSArray *bookDataArray = [NSArray arrayWithContentsOfFile:filePath];
      
                      // 字典陣列 -> 模型陣列
                      NSMutableArray *dataArrayM = [NSMutableArray arrayWithCapacity:bookDataArray.count];
                      for (NSDictionary *bookInfoDic in bookDataArray) {
                          BookModel *bookModel = [BookModel bookModelWithDict:bookInfoDic];
                          [dataArrayM addObject:bookModel];
                      }
      
                      // 將從檔案中取出的資料新增到資料來源陣列中
                      _myDataArray = [dataArrayM copy];
                  }
                  return _myDataArray;
              }
      
          // 從資料來源中取出資料
      
              // UITableViewDataSource 協議方法
              - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
                  // 從資料來源陣列中取出資料
                  BookModel *bookModel = [self.myDataArray objectAtIndex:indexPath.row];
      
                  // 配置自定義 Cell 子檢視上顯示的內容
                  cell.book = bookModel;
              }
  • Swift

    • BookModel.swift

          class BookModel: NSObject {
      
              // 根據需要使用的資料建立資料模型屬性變數
      
              var title:String?
              var detail:String?
              var icon:String?
              var price:String?
      
              class func bookModelWithDict(dict:[String : AnyObject]) -> AnyObject {
      
                  let model = BookModel()
      
                  // KVC - Key Value Coding
                  model.setValuesForKeysWithDictionary(dict)
      
                  return model
              }
          }
    • ViewController.swift

          // 向資料來源中新增資料
      
              // 定義資料來源,懶載入
              lazy var myDataArray:[BookModel] = {
      
                  // 載入 plist 中的字典陣列
                  let filePath:String = NSBundle.mainBundle().pathForResource("bookData", ofType: "plist")!
                  let bookDataArray:NSArray = NSArray(contentsOfFile: filePath)!
      
                  // 字典陣列 -> 模型陣列
                  var dataArrayM:NSMutableArray = NSMutableArray(capacity: bookDataArray.count)
                  for bookInfoDic in bookDataArray {
                      let bookModel:BookModel = BookModel.bookModelWithDict(bookInfoDic as! NSDictionary)
                      dataArrayM.addObject(bookModel)
                  }
      
                  // 將從檔案中取出的資料新增到資料來源陣列中
                  return dataArrayM.copy() as! [BookModel]
              }()
      
          // 從資料來源中取出資料
      
              // UITableViewDataSource 協議方法
              func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      
                  // 從資料來源陣列中取出資料
                  let bookModel:BookModel = self.myDataArray[indexPath.row]
      
                  // 配置自定義 Cell 子檢視上顯示的內容
                  cell!.configWithModel(bookModel)
              }

6、自定義等高 Cell

6.1 StoryBoard 自定義 cell

  • 建立一個繼承自 UITableViewCell 的子類,比如 XMGDealCell。

    TableView7

  • 在 storyboard 中
    • 往 cell 裡面增加需要用到的子控制元件。

      TableView8

    • 設定 cell 的重用標識 。

      TableView9

    • 設定 cell 的 class 為 XMGDealCell。

      TableView10

  • 在 XMGDealCell 中
    • 將 storyboard 中的子控制元件連線到類擴充套件中。
    • 需要提供一個模型屬性,重寫模型的 set 方法,在這個方法中設定模型資料到子控制元件上。
  • 在控制器中
    • 利用重用標識找到 cell。
    • 給 cell 傳遞模型資料。
  • Objective-C

    • XMGDeal.h

          @interface XMGDeal : NSObject
      
          @property (strong, nonatomic) NSString *buyCount;
          @property (strong, nonatomic) NSString *price;
          @property (strong, nonatomic) NSString *title;
          @property (strong, nonatomic) NSString *icon;
      
          + (instancetype)dealWithDict:(NSDictionary *)dict;
      
          @end
    • XMGDeal.m

          @implementation XMGDeal
      
          + (instancetype)dealWithDict:(NSDictionary *)dict {
      
              XMGDeal *deal = [[self alloc] init];
              [deal setValuesForKeysWithDictionary:dict];
              return deal;
          }
      
          @end
    • XMGDealCell.h

          @class XMGDeal;
      
          @interface XMGDealCell : UITableViewCell
      
          /** 模型資料 */
          @property (nonatomic, strong) XMGDeal *deal;
      
          @end
    • XMGDealCell.m

          @interface XMGDealCell()
      
          @property (weak, nonatomic) IBOutlet UIImageView *iconView;
          @property (weak, nonatomic) IBOutlet UILabel *titleLabel;
          @property (weak, nonatomic) IBOutlet UILabel *priceLabel;
          @property (weak, nonatomic) IBOutlet UILabel *buyCountLabel;
      
          @end
      
          @implementation XMGDealCell
      
          - (void)setDeal:(XMGDeal *)deal {
      
              _deal = deal;
      
              // 設定資料
              self.iconView.image = [UIImage imageNamed:deal.icon];
              self.titleLabel.text = deal.title;
              self.priceLabel.text = [NSString stringWithFormat:@"¥%@", deal.price];
              self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已購買", deal.buyCount];
          }
      
          @end
    • XMGDealsViewController.m

          @interface XMGDealsViewController ()
      
          /** 所有的團購資料 */
          @property (nonatomic, strong) NSArray *deals;
      
          @end
      
          @implementation XMGDealsViewController
      
          - (NSArray *)deals {
      
              if (_deals == nil) {
      
                  // 載入plist中的字典陣列
                  NSString *path = [[NSBundle mainBundle] pathForResource:@"deals.plist" ofType:nil];
                  NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
      
                  // 字典陣列 -> 模型陣列
                  NSMutableArray *dealArray = [NSMutableArray array];
                  for (NSDictionary *dict in dictArray) {
                      XMGDeal *deal = [XMGDeal dealWithDict:dict];
                      [dealArray addObject:deal];
                  }
      
                  _deals = dealArray;
              }
              return _deals;
          }
      
          #pragma mark - Table view data source
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
              return self.deals.count;
          }
      
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              static NSString *ID = @"deal";
              XMGDealCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
      
              // 取出模型資料
              cell.deal = self.deals[indexPath.row];
      
              return cell;
          }
      
          @end

6.2 xib 自定義 cell

  • 建立一個繼承自 UITableViewCell 的子類,比如 BookCell2。
  • 建立一個 xib 檔案(檔名建議跟 cell 的類名一樣),比如 BookCell2.xib。
    • 拖拽一個 UITableViewCell 出來。
    • 修改 cell 的 class 為 BookCell2。
    • 設定 cell 的重用標識。
    • 往 cell 中新增需要用到的子控制元件。
  • 在 BookCell2 中
    • 將 xib 中的子控制元件連線到類擴充套件中。
    • 需要提供一個模型屬性,重寫模型的 set 方法,在這個方法中設定模型資料到子控制元件上。
    • 也可以將建立獲得 cell 的程式碼封裝起來(比如 cellWithTableView: 方法)。
  • 在控制器中
    • 手動載入 xib 檔案,或者利用 registerNib... 方法註冊 xib 檔案。
    • 利用重用標識找到 cell。
    • 給 cell 傳遞模型資料。

6.2.1 xib 建立 cell

  • 在 xib 檔案中必須設定 Identifier 屬性,否則 cell 不會被複用,會一直建立新的 cell,佔用大量的記憶體。

  • Objective-C

    • BookCell2.xib

      TableView4

      • xib 的 Identifier 屬性設定為 Book2ID
    • BookCell2.h

          @class BookModel;
      
          @interface BookCell2 : UITableViewCell
      
          // 定義 Cell 的資料模型
          @property (nonatomic, retain)BookModel *book;
      
          @end
    • BookCell2.m

          #import "BookCell2.h"
          #import "BookModel.h"
      
          @interface BookCell2 ()
      
          // 建立自定義 Cell 檢視包含的內容
      
          // 按住 control 鍵拖動 或右鍵拖動過來生成
          @property (weak, nonatomic) IBOutlet UIImageView *iconView;         
          @property (weak, nonatomic) IBOutlet UILabel *titleLabel;
          @property (weak, nonatomic) IBOutlet UILabel *detailLabel;
          @property (weak, nonatomic) IBOutlet UILabel *priceLabel;
      
          @end
      
          @implementation BookCell2
      
          // 設定顯示的資料
      
          - (void)setBook:(BookModel *)book {
      
              _book = book;
      
              // 設定資料,設定 cell 檢視上顯示的內容 內容
              self.iconView.image = [UIImage imageNamed:book.icon];
              self.titleLabel.text = book.title;
              self.detailLabel.text = book.detail;
              self.priceLabel.text = book.price;
          }
      
          @end
    • ViewController.m

          // 使用 xib 定義的 Cell 建立,UITableViewDataSource 協議方法
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 使用 xib 定義的 Cell 定義
              BookCell2 *cell = [tableView dequeueReusableCellWithIdentifier:@"Book2ID"];
      
              if (cell == nil) {
      
                  // 通過 xib 檔案建立新的 cell
                  cell = [[[NSBundle mainBundle] loadNibNamed:@"BookCell2" owner:self options: nil] lastObject];
              }
      
              BookModel *bookModel = [self.myDataArray objectAtIndex:indexPath.row];
              cell.book = bookModel;
      
              return cell;
          }
  • Swift

    • BookCell2.xib

      TableView4

      • xib 的 Identifier 屬性設定為 Book2ID 。
    • BookCell2.swift

          class BookCell2: UITableViewCell {
      
              // 建立自定義 Cell 檢視包含的內容
      
              @IBOutlet weak var iconView: UIImageView!
              @IBOutlet weak var titleLabel: UILabel!
              @IBOutlet weak var detailLabel: UILabel!
              @IBOutlet weak var priceLabel: UILabel!
      
              // 設定顯示的資料
      
              func configWithModel(bookModel:BookModel){
      
                  // 設定資料,設定 cell 檢視上顯示的內容 內容
                  iconView!.image = UIImage(named: bookModel.icon!)
                  titleLabel!.text = bookModel.title
                  detailLabel!.text = bookModel.detail
                  priceLabel!.text = bookModel.price
              }
          }
    • ViewController.swift

          // 使用 xib 定義的 Cell 建立,UITableViewDataSource 協議方法
          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      
              // 使用 xib 定義的 Cell 定義
              var cell:BookCell2? = tableView.dequeueReusableCellWithIdentifier("BookCell2") as? BookCell2 
      
              if cell == nil {
      
                  // 通過 xib 檔案建立新的 cell
                  cell = NSBundle.mainBundle().loadNibNamed("BookCell2", owner: self, options: nil).last as? BookCell2
              }
      
              let bookModel:BookModel = self.myDataArray[indexPath.row]
              cell!.configWithModel(bookModel)
      
              return cell!
          }

6.2.2 xib 註冊 cell

  • 如果 cell 使用 xib 註冊的方式,xib 中可以不指定複用標識 Identifier 屬性,如果 xib 中指定了,那麼所有地方都要同步。但無論如何程式碼中必須設定 Identifier 屬性。

  • 在 tableView 建立時,從 iOS7 開始多了一種建立 cell 的方式(註冊),讓 tableView 註冊一種 cell,需要設定複用標誌。
  • 用註冊方式建立 cell,如果 tableView 已經註冊了某一種 cell,從複用佇列裡查詢,如果找不到,系統會自動通過註冊的 cell 類來建立 cell 物件。

  • Objective-C

    • BookCell2.xib
    • BookCell2.h
    • BookCell2.m

      • xib 自定義 Cell 部分同上。
    • ViewController.m

          // 註冊 cell
          [myTableView registerNib:[UINib nibWithNibName:NSStringFromClass([BookCell2 class]) bundle:nil] forCellReuseIdentifier:@"Book2ID"];
      
          // 使用註冊的 xib cell 建立,UITableViewDataSource 協議方法
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 使用 xib 定義的 Cell 定義
              BookCell2 *cell = [tableView dequeueReusableCellWithIdentifier:@"Book2ID" forIndexPath:indexPath];
      
              BookModel *bookModel = [self.myDataArray objectAtIndex:indexPath.row];
              cell.book = bookModel;
      
              return cell;
          }
  • Swift

    • BookCell2.xib
    • BookCell2.swift

      • xib 自定義 Cell 部分同上。
    • ViewController.m

          // 註冊 cell
          myTableView.registerNib(UINib(nibName: "BookCell2", bundle: nil), forCellReuseIdentifier: "Book2ID")
      
          // 使用註冊的 xib cell 建立,UITableViewDataSource 協議方法
          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {           
              // 使用 xib 定義的 Cell 定義
              let cell:BookCell2? = tableView.dequeueReusableCellWithIdentifier("Book2ID", forIndexPath: indexPath) as? BookCell2
      
              let bookModel:BookModel = self.myDataArray[indexPath.row]
      
              cell!.configWithModel(bookModel)
      
              return cell!
          }

6.3 程式碼自定義 Cell

  • 程式碼自定義 cell(使用 frame)

    • 建立一個繼承自 UITableViewCell 的子類,比如 BookCell1。
      • 在 initWithStyle:reuseIdentifier: 方法中。
        • 新增子控制元件。
        • 設定子控制元件的初始化屬性(比如文字顏色、字型)。
      • 在 layoutSubviews 方法中設定子控制元件的 frame。
      • 需要提供一個模型屬性,重寫模型的 set 方法,在這個方法中設定模型資料到子控制元件。
    • 在控制器中
      • 利用 registerClass... 方法註冊 BookCell1 類。
      • 利用重用標識找到 cell(如果沒有註冊類,就需要手動建立 cell)。
      • 給 cell 傳遞模型資料。
      • 也可以將建立獲得 cell 的程式碼封裝起來(比如 cellWithTableView: 方法)。
  • 程式碼自定義 cell(使用 autolayout)

    • 建立一個繼承自 UITableViewCell 的子類,比如 BookCell1。
      • 在 initWithStyle:reuseIdentifier: 方法中。
        • 新增子控制元件。
        • 新增子控制元件的約束(建議使用 Masonry)。
        • 設定子控制元件的初始化屬性(比如文字顏色、字型)。
      • 需要提供一個模型屬性,重寫模型的 set 方法,在這個方法中設定模型資料到子控制元件。
    • 在控制器中
      • 利用 registerClass... 方法註冊 BookCell1 類。
      • 利用重用標識找到 cell(如果沒有註冊類,就需要手動建立 cell)。
      • 給 cell 傳遞模型資料。
      • 也可以將建立獲得 cell 的程式碼封裝起來(比如 cellWithTableView: 方法)。

6.3.1 程式碼建立 cell - frame

  • Objective-C

    • BookCell1.h

          @class BookModel;
      
          @interface BookCell1 : UITableViewCell
      
          // 定義 Cell 的資料模型
          @property (nonatomic, retain)BookModel *book;
      
          @end
    • BookCell1.m

          #import "BookCell1.h"
          #import "BookModel.h"
      
          @interface BookCell1()
      
          // 建立自定義 Cell 檢視包含的內容
      
          @property(nonatomic, retain)UIImageView *iconView;
          @property(nonatomic, retain)UILabel *titleLabel;
          @property(nonatomic, retain)UILabel *detailLabel;
          @property(nonatomic, retain)UILabel *priceLabel;
      
          @end
      
          @implementation BookCell1
      
          // 重寫初 Cell 始化方法,建立自定義 Cell
      
          - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
      
              if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
      
                  // 建立子檢視
      
                  // 建立 iconView 檢視,並新增到自定義 Cell 上
                  self.iconView = [[UIImageView alloc] init];
                  self.iconView.layer.borderColor = [[UIColor greenColor] CGColor];
                  self.iconView.layer.borderWidth = 2;
                  [self.contentView addSubview:self.iconView];
      
                  // 建立 titleLabel 檢視
                  self.titleLabel = [[UILabel alloc] init];
                  self.titleLabel.font = [UIFont boldSystemFontOfSize:14];
                  self.titleLabel.textColor = [UIColor redColor];
                  [self.contentView addSubview:self.titleLabel];
      
                  // 建立 detailLabel 檢視
                  self.detailLabel = [[UILabel alloc] init];
                  self.detailLabel.font = [UIFont systemFontOfSize:12];
                  [self.contentView addSubview:self.detailLabel];
      
                  // 建立 priceLabel 檢視
                  self.priceLabel = [[UILabel alloc] init];
                  self.priceLabel.font = [UIFont systemFontOfSize:12];
                  [self.contentView addSubview:self.priceLabel];
              }
              return self;
          }
      
          // 佈局子檢視
      
          - (void)layoutSubviews {
      
              [super layoutSubviews];
      
              // 佈局子檢視
              self.iconView.frame = CGRectMake(10, 10, 60, 60);
              self.titleLabel.frame = CGRectMake(90, 5, 200, 25);
              self.detailLabel.frame = CGRectMake(90, 30, 200, 25);
              self.priceLabel.frame = CGRectMake(90, 55, 200, 25);
          }
      
          // 設定顯示的資料
      
          - (void)setBook:(BookModel *)book {
      
              _book = book;
      
              // 設定資料,設定 cell 檢視上顯示的內容 內容
              self.iconView.image = [UIImage imageNamed:book.icon];
              self.titleLabel.text = book.title;
              self.detailLabel.text = book.detail;
              self.priceLabel.text = book.price;
          }
      
          @end
    • ViewController.m

          // 使用自定義 Cell 建立,UITableViewDataSource 協議方法
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 使用自定義的 Cell 定義
              BookCell1 *cell = [tableView dequeueReusableCellWithIdentifier:@"testIdentifier"];
      
              if (cell == nil) {
      
                  // 使用自定義的 Cell 建立
                  cell = [[BookCell1 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"testIdentifier"];
              }
      
              BookModel *bookModel = [self.myDataArray objectAtIndex:indexPath.row];
      
              cell.book = bookModel;
      
              return cell;
          }
  • Swift

    • BookCell1.swift

          class BookCell1: UITableViewCell {
      
              // 建立自定義 Cell 檢視包含的內容
      
              var iconView:UIImageView?
              var titleLabel:UILabel?
              var detailLabel:UILabel?
              var priceLabel:UILabel?
      
              // 重寫初 Cell 始化方法,建立自定義 Cell
      
              override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
      
                  super.init(style: style, reuseIdentifier: reuseIdentifier)
      
                  // 建立子檢視
      
                  // 建立 _iconView 檢視,並新增到自定義 Cell 上
                  iconView = UIImageView()                       
                  iconView!.layer.borderColor = UIColor.greenColor().CGColor
                  iconView!.layer.borderWidth = 2
                  self.contentView.addSubview(iconView!)
      
                  // 建立 _titleLabel 檢視
                  titleLabel = UILabel()                         
                  titleLabel!.font = UIFont.boldSystemFontOfSize(14)
                  titleLabel!.textColor = UIColor.redColor()
                  self.contentView.addSubview(titleLabel!)
      
                  // 建立 _detailLabel 檢視
                  detailLabel = UILabel()                       
                  detailLabel!.font = UIFont.systemFontOfSize(12)
                  self.contentView.addSubview(detailLabel!)
      
                  // 建立 _priceLabel 檢視
                  priceLabel = UILabel()                        
                  priceLabel!.font = UIFont.systemFontOfSize(12)
                  self.contentView.addSubview(priceLabel!)
              }
      
              // 佈局子檢視
      
              override func layoutSubviews() {
                  super.layoutSubviews()
      
                  // 佈局子檢視
                  iconView?.frame = CGRectMake(10, 10, 60, 60)
                  titleLabel?.frame = CGRectMake(90, 5, 200, 25)
                  detailLabel?.frame = CGRectMake(90, 30, 200, 25)
                  priceLabel?.frame = CGRectMake(90, 55, 200, 25)
              }
      
              // 設定顯示的資料
      
              func configWithModel(bookModel:BookModel){
      
                  // 設定資料,設定 cell 檢視上顯示的內容 內容
                  iconView?.image = UIImage(named: bookModel.icon!)
                  titleLabel?.text = bookModel.title
                  detailLabel?.text = bookModel.detail
                  priceLabel?.text = bookModel.price
              }
      
              required init?(coder aDecoder: NSCoder) {
                  fatalError("init(coder:) has not been implemented")
              }
          }
    • ViewController.swift

          // 使用自定義 Cell 建立,UITableViewDataSource 協議方法
          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      
              // 使用自定義的 Cell 定義
              var cell:BookCell1? = tableView.dequeueReusableCellWithIdentifier("testIdentifier") as? BookCell1       
      
              if cell == nil {
      
                  // 使用自定義的 Cell 建立
                  cell = BookCell1(style: UITableViewCellStyle.Default, reuseIdentifier: "testIdentifier")            
              }
      
              let bookModel:BookModel = self.myDataArray[indexPath.row]
      
              cell!.configWithModel(bookModel)
      
              return cell!
          }

6.3.2 程式碼註冊 cell - frame

  • 在 tableView 建立時,從 iOS7 開始多了一種建立 cell 的方式(註冊),讓 tableView 註冊一種 cell,需要設定複用標誌。
  • 用註冊方式建立 cell,如果 tableView 已經註冊了某一種 cell,從複用佇列裡查詢,如果找不到,系統會自動通過註冊的 cell 類來建立 cell 物件。

  • Objective-C

    • BookCell1.h
    • BookCell1.m

      • 自定義 Cell 部分同上。
    • ViewController.m

          // 註冊 cell
          [myTableView registerClass:[BookCell1 class] forCellReuseIdentifier:@"testIdentifier"];
      
          // 使用註冊的 Cell 建立,UITableViewDataSource 協議方法
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              BookCell1 *cell = [tableView dequeueReusableCellWithIdentifier:@"testIdentifier" forIndexPath:indexPath];                   
              BookModel *bookModel = [self.myDataArray objectAtIndex:indexPath.row];
      
              cell.book = bookModel;
      
              return cell;
          }
  • Swift

    • BookCell1.swift

      • 自定義 Cell 部分同上。
    • ViewController.swift

          // 註冊 cell
          myTableView.registerClass(BookCell1.self, forCellReuseIdentifier: "testIdentifier")
      
          // 使用註冊的 Cell 建立,UITableViewDataSource 協議方法
          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      
              let cell:BookCell1? = tableView.dequeueReusableCellWithIdentifier("testIdentifier", forIndexPath: indexPath) as? BookCell1
      
              let bookModel:BookModel = self.myDataArray[indexPath.row]
      
              cell!.configWithModel(bookModel)
      
              return cell!
          }

6.3.3 程式碼建立 cell - autolayout

  • 第三方框架 Masonry Github 網址:https://github.com/SnapKit/Masonry

  • StoryBoard

    • XMGDeal.h

          @interface XMGDeal : NSObject
      
          @property (strong, nonatomic) NSString *buyCount;
          @property (strong, nonatomic) NSString *price;
          @property (strong, nonatomic) NSString *title;
          @property (strong, nonatomic) NSString *icon;
      
          + (instancetype)dealWithDict:(NSDictionary *)dict;
      
          @end
    • XMGDeal.m

          @implementation XMGDeal
      
          + (instancetype)dealWithDict:(NSDictionary *)dict {
      
              XMGDeal *deal = [[self alloc] init];
      
              // KVC - Key Value Coding
              [deal setValuesForKeysWithDictionary:dict];
      
              return deal;
          }
      
          @end
    • XMGDealCell.h

          @class XMGDeal;
      
          @interface XMGDealCell : UITableViewCell
      
          /** 模型資料 */
          @property (nonatomic, strong) XMGDeal *deal;
      
          + (instancetype)cellWithTableView:(UITableView *)tableView;
      
          @end
    • XMGDealCell.m

          #define MAS_SHORTHAND
          #define MAS_SHORTHAND_GLOBALS
      
          #import "Masonry.h"
      
          @interface XMGDealCell()
      
          @property (weak, nonatomic) UIImageView *iconView;
          @property (weak, nonatomic) UILabel *titleLabel;
          @property (weak, nonatomic) UILabel *priceLabel;
          @property (weak, nonatomic) UILabel *buyCountLabel;
      
          @end
      
          @implementation XMGDealCell
      
          + (instancetype)cellWithTableView:(UITableView *)tableView {
      
              static NSString *ID = @"deal";
      
              // 建立cell
              XMGDealCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
      
              if (cell == nil) {
                  cell = [[XMGDealCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
              }
              return cell;
          }
      
          // 1.在 initWithStyle:reuseIdentifier: 方法中新增子控制元件
          - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
      
              if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
      
                  UIImageView *iconView = [[UIImageView alloc] init];
                  [self.contentView addSubview:iconView];
                  self.iconView = iconView;
      
                  UILabel *titleLabel = [[UILabel alloc] init];
                  [self.contentView addSubview:titleLabel];
                  self.titleLabel = titleLabel;
      
                  UILabel *priceLabel = [[UILabel alloc] init];
                  priceLabel.textColor = [UIColor orangeColor];
                  [self.contentView addSubview:priceLabel];
                  self.priceLabel = priceLabel;
      
                  UILabel *buyCountLabel = [[UILabel alloc] init];
                  buyCountLabel.textAlignment = NSTextAlignmentRight;
                  buyCountLabel.font = [UIFont systemFontOfSize:14];
                  buyCountLabel.textColor = [UIColor lightGrayColor];
                  [self.contentView addSubview:buyCountLabel];
                  self.buyCountLabel = buyCountLabel;
              }
              return self;
          }
      
          // 2.在 layoutSubviews 方法中設定子控制元件的 約束
          - (void)layoutSubviews {
      
              [super layoutSubviews];
      
              CGFloat margin = 10;
      
              [self.iconView makeConstraints:^(MASConstraintMaker *make) {
                  make.width.equalTo(100);
                  make.left.top.offset(margin);
                  make.bottom.offset(-margin);
              }];
      
              [self.titleLabel makeConstraints:^(MASConstraintMaker *make) {
                  make.top.equalTo(self.iconView);
                  make.left.equalTo(self.iconView.right).offset(margin);
                  make.right.offset(-margin);
              }];
      
              [self.priceLabel makeConstraints:^(MASConstraintMaker *make) {
                  make.left.equalTo(self.titleLabel);
                  make.bottom.equalTo(self.iconView);
                  make.width.equalTo(70);
              }];
      
              [self.buyCountLabel makeConstraints:^(MASConstraintMaker *make) {
                  make.bottom.equalTo(self.priceLabel);
                  make.right.equalTo(self.titleLabel);
                  make.left.equalTo(self.priceLabel.right).offset(margin);
              }];
          }
      
          // 3.重寫模型的 set 方法
          - (void)setDeal:(XMGDeal *)deal {
      
              _deal = deal;
      
              // 設定資料
              self.iconView.image = [UIImage imageNamed:deal.icon];
              self.titleLabel.text = deal.title;
              self.priceLabel.text = [NSString stringWithFormat:@"¥%@", deal.price];
              self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已購買", deal.buyCount];
          }
      
          @end
    • XMGDealsViewController.m

          @interface XMGDealsViewController ()
      
          /** 所有的團購資料 */
          @property (nonatomic, strong) NSArray *deals;
      
          @end
      
          @implementation XMGDealsViewController
      
          - (NSArray *)deals {
      
              if (_deals == nil) {
      
                  // 載入plist中的字典陣列
                  NSString *path = [[NSBundle mainBundle] pathForResource:@"deals.plist" ofType:nil];
                  NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
      
                  // 字典陣列 -> 模型陣列
                  NSMutableArray *dealArray = [NSMutableArray array];
                  for (NSDictionary *dict in dictArray) {
                      XMGDeal *deal = [XMGDeal dealWithDict:dict];
                      [dealArray addObject:deal];
                  }
      
                  _deals = dealArray;
              }
              return _deals;
          }
      
      
          - (void)viewDidLoad {
              [super viewDidLoad];
      
          //    [self.tableView registerClass:[XMGDealCell class] forCellReuseIdentifier:@"deal"];
          }
      
          #pragma mark - Table view data source
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
              return self.deals.count;
          }
      
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              //建立cell
              XMGDealCell *cell = [XMGDealCell cellWithTableView:tableView];
      
              // 取出模型資料
              cell.deal = self.deals[indexPath.row];
      
              return cell;
          }
      
          @end

7、自定義非等高 Cell

7.1 StoryBoard / Xib 自定義 cell

  • 在模型中增加一個 cellHeight 屬性,用來存放對應 cell 的高度。
  • 在 cell 的模型屬性 set 方法中呼叫 [self layoutIfNeed] 方法強制佈局,然後計算出模型的 cellheight 屬性值。
  • 在控制器中實現 tableView:estimatedHeightForRowAtIndexPath: 方法,返回一個估計高度,比如 200。
  • 在控制器中實現 tableView:heightForRowAtIndexPath: 方法,返回 cell 的真實高度(模型中的 cellHeight 屬性)。

  • 注意:StoryBoard 中 label 的約束不要設定右側約束值,否則編譯時會列印出一大堆提示資訊。

  • StoryBoard

    • Main.storyboard

      • 在 cell 上新增子控制元件,並設定約束。

        TableView13

  • Xib

    • XMGStatusCell.xib

      • 在 cell 上新增子控制元件,並設定約束。

        TableView14

  • Objective-C

    • XMGStatus.h

          @interface XMGStatus : NSObject
      
          @property (strong, nonatomic) NSString *name;
          @property (strong, nonatomic) NSString *text;
          @property (strong, nonatomic) NSString *icon;
          @property (strong, nonatomic) NSString *picture;
          @property (assign, nonatomic, getter=isVip) BOOL vip;
      
          /** cell 的高度 */
          @property (assign, nonatomic) CGFloat cellHeight;
      
          + (instancetype)statusWithDict:(NSDictionary *)dict;
      
          @end
    • XMGStatus.m

          @implementation XMGStatus
      
          + (instancetype)statusWithDict:(NSDictionary *)dict {
      
              XMGStatus *status = [[self alloc] init];
              [status setValuesForKeysWithDictionary:dict];
              return status;
          }
      
          @end
    • XMGStatusCell.h

          @class XMGStatus;
      
          @interface XMGStatusCell : UITableViewCell
      
          + (instancetype)cellWithTableView:(UITableView *)tableView;
      
          /** 模型資料 */
          @property (nonatomic, strong) XMGStatus *status;
      
          @end
    • XMGStatusCell.m

          @interface XMGStatusCell()
      
          @property (weak, nonatomic) IBOutlet UIImageView *iconView;
          @property (weak, nonatomic) IBOutlet UILabel *nameLabel;
          @property (weak, nonatomic) IBOutlet UIImageView *vipView;
          @property (weak, nonatomic) IBOutlet UILabel *contentLabel;
          @property (weak, nonatomic) IBOutlet UIImageView *pictureView;
      
          @end
      
          @implementation XMGStatusCell
      
          + (instancetype)cellWithTableView:(UITableView *)tableView {
      
              return [tableView dequeueReusableCellWithIdentifier:@"status"];
          }
      
          - (void)awakeFromNib {
              [super awakeFromNib];
      
              // 設定label每一行文字的最大寬度
              /* 
                  為了保證計算出來的數值 跟 真正顯示出來的效果 一致
              */
              self.contentLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
          }
      
          - (void)setStatus:(XMGStatus *)status {
      
              _status = status;
      
              // 設定顯示的資料
      
              self.iconView.image = [UIImage imageNamed:status.icon];
              self.nameLabel.text = status.name;
      
              if (status.isVip) {
                  self.nameLabel.textColor = [UIColor orangeColor];
                  self.vipView.hidden = NO;
              } else {
                  self.nameLabel.textColor = [UIColor blackColor];
                  self.vipView.hidden = YES;
              }
      
              self.contentLabel.text = status.text;
      
              if (status.picture) {
                  self.pictureView.hidden = NO;
                  self.pictureView.image = [UIImage imageNamed:status.picture];
              } else {
                  self.pictureView.hidden = YES;
              }
      
              // 計算 cell 高度
      
              // 強制佈局
              [self layoutIfNeeded];
      
              // 計算 cell 的高度
              if (self.pictureView.hidden) {  // 沒有配圖
                  _status.cellHeight = CGRectGetMaxY(self.contentLabel.frame) + 10;
              } else {                        // 有配圖
                  _status.cellHeight = CGRectGetMaxY(self.pictureView.frame) + 10;
              }
          }
      
          @end
    • XMGStatusesViewController.m

          @interface XMGStatusesViewController ()
      
          @property (strong, nonatomic) NSArray *statuses;
      
          @end
      
          @implementation XMGStatusesViewController
      
          - (NSArray *)statuses {
      
              if (_statuses == nil) {
      
                  // 載入plist中的字典陣列
                  NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil];
                  NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
      
                  // 字典陣列 -> 模型陣列
                  NSMutableArray *statusArray = [NSMutableArray array];
                  for (NSDictionary *dict in dictArray) {
                      XMGStatus *status = [XMGStatus statusWithDict:dict];
                      [statusArray addObject:status];
                  }
                  _statuses = statusArray;
              }
              return _statuses;
          }
      
          #pragma mark - Table view data source
      
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
              return self.statuses.count;
          }
      
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              XMGStatusCell *cell = [XMGStatusCell cellWithTableView:tableView];
              cell.status = self.statuses[indexPath.row];
              return cell;
          }
      
          #pragma mark - 代理方法
      
          // 返回每一行的高度
      
          - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              XMGStatus *staus = self.statuses[indexPath.row];
              return staus.cellHeight;
          }
      
          /**
           * 返回每一行的估計高度
           * 只要返回了估計高度,那麼就會先呼叫 tableView:cellForRowAtIndexPath: 方法建立 cell,
           * 再呼叫 tableView:heightForRowAtIndexPath: 方法獲取 cell 的真實高度
           */
          - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              return 200;
          }
      
          @end
  • 執行效果

    TableView21TableView22

7.2 程式碼自定義 cell

7.2.1 程式碼自定義(frame)

  • 新建一個繼承自 UITableViewCell 的類。
  • 重寫 initWithStyle:reuseIdentifier: 方法。
    • 新增所有需要顯示的子控制元件(不需要設定子控制元件的資料和 frame,  子控制元件要新增到 contentView 中)。
    • 進行子控制元件一次性的屬性設定(有些屬性只需要設定一次, 比如字型\固定的圖片)。
  • 提供 2 個模型。
    • 資料模型: 存放文字資料\圖片資料。
    • frame 模型: 存放資料模型\所有子控制元件的 frame\cell 的高度。
  • cell 擁有一個 frame 模型(不要直接擁有資料模型)。
  • 重寫 cell frame 模型屬性的 setter 方法: 在這個方法中設定子控制元件的顯示資料和 frame。
  • frame 模型資料的初始化已經採取懶載入的方式(每一個 cell 對應的 frame 模型資料只載入一次)。

7.2.2 程式碼自定義(Autolayout)

  • 新建一個繼承自 UITableViewCell 的類。
  • 重寫 initWithStyle:reuseIdentifier: 方法。
    • 新增所有需要顯示的子控制元件(不需要設定子控制元件的資料和 frame,  子控制元件要新增到 contentView 中)。
    • 進行子控制元件一次性的屬性設定(有些屬性只需要設定一次, 比如字型\固定的圖片)。
  • 設定 cell 上子控制元件的約束。
  • 在模型中增加一個 cellHeight 屬性,用來存放對應 cell 的高度。
  • 在 cell 的模型屬性 set 方法中呼叫 [self layoutIfNeed] 方法強制佈局,然後計算出模型的 cellheight 屬性值。
  • 在控制器中實現 tableView:estimatedHeightForRowAtIndexPath: 方法,返回一個估計高度,比如 200。
  • 在控制器中實現 tableView:heightForRowAtIndexPath: 方法,返回 cell 的真實高度(模型中的 cellHeight 屬性)。

  • Objective-C

    • XMGStatus.h

          @interface XMGStatus : NSObject
      
          @property (strong, nonatomic) NSString *name;
          @property (strong, nonatomic) NSString *text;
          @property (strong, nonatomic) NSString *icon;
          @property (strong, nonatomic) NSString *picture;
          @property (assign, nonatomic, getter=isVip) BOOL vip;
      
          /** cell 的高度 */
          @property (assign, nonatomic) CGFloat cellHeight;
      
          + (instancetype)statusWithDict:(NSDictionary *)dict;
      
          @end
    • XMGStatus.m

          @implementation XMGStatus
      
          + (instancetype)statusWithDict:(NSDictionary *)dict {
      
              XMGStatus *status = [[self alloc] init];
              [status setValuesForKeysWithDictionary:dict];
              return status;
          }
      
          @end
    • XMGStatusCell.h

          @class XMGStatus;
      
          @interface XMGStatusCell : UITableViewCell
      
          + (instancetype)cellWithTableView:(UITableView *)tableView;
      
          /** 模型資料 */
          @property (nonatomic, strong) XMGStatus *status;
      
          @end
    • XMGStatusCell.m

          #define MAS_SHORTHAND
          #define MAS_SHORTHAND_GLOBALS
      
          #import "Masonry.h"
      
          @interface XMGStatusCell()
      
          @property (weak, nonatomic) UIImageView *iconView;
          @property (weak, nonatomic) UILabel *nameLabel;
          @property (weak, nonatomic) UIImageView *vipView;
          @property (weak, nonatomic) UILabel *contentLabel;
          @property (weak, nonatomic) UIImageView *pictureView;
      
          @end
      
          @implementation XMGStatusCell
      
          + (instancetype)cellWithTableView:(UITableView *)tableView {
      
              static NSString *ID = @"status";
              XMGStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
      
              if (cell == nil) {
                  cell = [[XMGStatusCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
              }
              return cell;
          }
      
          - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
      
              if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
      
                  UIImageView *iconView = [[UIImageView alloc] init];
                  [self.contentView addSubview:iconView];
                  self.iconView = iconView;
      
                  UILabel *nameLabel = [[UILabel alloc] init];
                  [self.contentView addSubview:nameLabel];
                  self.nameLabel = nameLabel;
      
                  UIImageView *vipView = [[UIImageView alloc] init];
                  [self.contentView addSubview:vipView];
                  self.vipView = vipView;
      
                  UILabel *contentLabel = [[UILabel alloc] init];
                  contentLabel.numberOfLines = 0;
      
                  // 設定 label 每一行文字的最大寬度
                  contentLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20;
      
                  [self.contentView addSubview:contentLabel];
                  self.contentLabel = contentLabel;
      
                  UIImageView *pictureView = [[UIImageView alloc] init];
                  [self.contentView addSubview:pictureView];
                  self.pictureView = pictureView;
              }
              return self;
          }
      
          - (void)layoutSubviews {
      
              [super layoutSubviews];
      
              CGFloat margin = 10;
      
              [self.iconView makeConstraints:^(MASConstraintMaker *make) {
                  make.size.equalTo(30);
                  make.left.top.offset(margin);
              }];
      
              [self.nameLabel makeConstraints:^(MASConstraintMaker *make) {
                  make.top.equalTo(self.iconView);
                  make.left.equalTo(self.iconView.right).offset(margin);
              }];
      
              [self.vipView makeConstraints:^(MASConstraintMaker *make) {
                  make.size.equalTo(14);
                  make.left.equalTo(self.nameLabel.right).offset(margin);
                  make.centerY.equalTo(self.nameLabel.centerY);
              }];
      
              [self.contentLabel makeConstraints:^(MASConstraintMaker *make) {
                  make.top.equalTo(self.iconView.bottom).offset(margin);
                  make.left.offset(margin);
          //        make.right.offset(-margin);       // 可加可不加
              }];
      
              [self.pictureView makeConstraints:^(MASConstraintMaker *make) {
                  make.size.equalTo(100);
                  make.top.equalTo(self.contentLabel.bottom).offset(margin);
                  make.left.offset(margin);
              }];
          }
      
          - (void)setStatus:(XMGStatus *)status {
      
              _status = status;
      
              // 設定顯示的資料
      
              self.iconView.image = [UIImage imageNamed:status.icon];
              self.nameLabel.text = status.name;
      
              if (status.isVip) {
                  self.nameLabel.textColor = [UIColor orangeColor];
                  self.vipView.hidden = NO;
              } else {
                  self.nameLabel.textColor = [UIColor blackColor];
                  self.vipView.hidden = YES;
              }
      
              self.contentLabel.text = status.text;
      
              if (status.picture) {
                  self.pictureView.hidden = NO;
                  self.pictureView.image = [UIImage imageNamed:status.picture];
              } else {
                  self.pictureView.hidden = YES;
              }
      
              // 計算 cell 高度
      
              // 強制佈局
              [self layoutIfNeeded];
      
              // 計算 cell 的高度
              if (self.pictureView.hidden) {  // 沒有配圖
                  _status.cellHeight = CGRectGetMaxY(self.contentLabel.frame) + 10;
              } else {                        // 有配圖
                  _status.cellHeight = CGRectGetMaxY(self.pictureView.frame) + 10;
              }
          }
      
          @end
    • XMGStatusesViewController.m

          @interface XMGStatusesViewController ()
      
          @property (strong, nonatomic) NSArray *statuses;
      
          @end
      
          @implementation XMGStatusesViewController
      
          - (NSArray *)statuses {
      
              if (_statuses == nil) {
      
                  // 載入plist中的字典陣列
                  NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil];
                  NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
      
                  // 字典陣列 -> 模型陣列
                  NSMutableArray *statusArray = [NSMutableArray array];
                  for (NSDictionary *dict in dictArray) {
                      XMGStatus *status = [XMGStatus statusWithDict:dict];
                      [statusArray addObject:status];
                  }
                  _statuses = statusArray;
              }
              return _statuses;
          }
      
          #pragma mark - Table view data source
      
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
              return self.statuses.count;
          }
      
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              XMGStatusCell *cell = [XMGStatusCell cellWithTableView:tableView];
              cell.status = self.statuses[indexPath.row];
              return cell;
          }
      
          #pragma mark - 代理方法
      
          // 返回每一行的高度
      
          - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              XMGStatus *staus = self.statuses[indexPath.row];
              return staus.cellHeight;
          }
      
          /**
           * 返回每一行的估計高度
           * 只要返回了估計高度,那麼就會先呼叫 tableView:cellForRowAtIndexPath: 方法建立 cell,
           * 再呼叫 tableView:heightForRowAtIndexPath: 方法獲取 cell 的真實高度
           */
          - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              return 200;
          }
      
          @end
  • 執行效果

    TableView23TableView24

7.3 其它設定方式

7.3.1 計算方式

  • Objective-C

    • BookModel.h

          @property(nonatomic, copy)NSString *title;
          @property(nonatomic, copy)NSString *detail;
          @property(nonatomic, copy)NSString *icon;
          @property(nonatomic, copy)NSString *price;
    • BookCell.h

          @property(nonatomic, retain)UILabel *titleLabel;
          @property(nonatomic, retain)UILabel *detailLabel;
          @property(nonatomic, retain)UIImageView *iconView;
          @property(nonatomic, retain)UILabel *priceLabel;
    • 設定行高

          - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 從資料來源陣列中取出資料
              BookModel *bookModel = [myDataArray objectAtIndex:indexPath.row];
      
              // 計算 detailLabel 佔用的高度
              CGFloat detialHeight = [bookModel.detail boundingRectWithSize:CGSizeMake(self.view.bounds.size.width - 40, CGFLOAT_MAX) 
                                                                    options:NSStringDrawingUsesLineFragmentOrigin 
                                                                 attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:14]} 
                                                                    context:nil].size.height;
      
              // 判斷是否有圖片
              if (bookModel.icon.length) {
      
                  // 60 為圖片的高度
                  return 30 + detialHeight + 60 + 30;
              }
              else {
                  return 30 + detialHeight + 30;
              }
          }
    • 設定每一行顯示的內容

          // 設定每一行顯示的內容
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              BookCell3 *cell = [tableView dequeueReusableCellWithIdentifier:@"test" forIndexPath:indexPath];
              BookModel *bookModel = [myDataArray objectAtIndex:indexPath.row];
      
              // 設定 titleLabel
              cell.titleLabel.text = bookModel.title;
      
              // 設定 detailLabel
      
              // 計算 detailLabel 的高度
              CGSize detialSize = [bookModel.detail boundingRectWithSize:CGSizeMake(self.view.bounds.size.width - 40, CGFLOAT_MAX) 
                                                                 options:NSStringDrawingUsesLineFragmentOrigin 
                                                              attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:14]} 
                                                                 context:nil].size;
      
              CGRect detialFrame = cell.detailLabel.frame;
              detialFrame.size.height = detialSize.height + 5;    // 加偏移量 5,適應標點無法換行
              detialFrame.size.width = detialSize.width + 5;
              cell.detailLabel.frame = detialFrame;               // 設定 detailLabel 的 frame
      
              cell.detailLabel.text = bookModel.detail;
      
              // 判斷是否有圖片
              if (bookModel.icon.length) {
      
                  // 設定 iconView
      
                  CGRect iconFrame = cell.iconView.frame;
                  iconFrame.origin.y = detialFrame.origin.y + detialFrame.size.height;
                  cell.iconView.frame = iconFrame;
      
              cell.iconView.image = [UIImage imageNamed: bookModel.icon];
      
                  // 設定 priceLabel
      
                  CGRect priceFrame = cell.priceLabel.frame;
                  priceFrame.origin.y = iconFrame.origin.y + iconFrame.size.height;
                  cell.priceLabel.frame = priceFrame;
      
                  cell.priceLabel.text = bookModel.price;
              }
              else {
      
                  // 設定 priceLabel
      
                  CGRect priceFrame = cell.priceLabel.frame;
                  priceFrame.origin.y = detialFrame.origin.y + detialFrame.size.height;
                  cell.priceLabel.frame = priceFrame;
      
                  cell.priceLabel.text = bookModel.price;
              }
              return cell;
          }
  • Swift

    • BookModel.swift

          var title:String?
          var detail:String?
          var icon:String?
          var price:String?
    • BookCell.swift

          var titleLabel:UILabel?
          var detailLabel:UILabel?
          var iconView:UIImageView?
          var priceLabel:UILabel?
    • 設定行高

          func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
      
              // 從資料來源陣列中取出資料
              let bookModel:BookModel = myDataArray.objectAtIndex(indexPath.row) as! BookModel
      
              // 計算 detailLabel 佔用的高度
              let detialHeight:CGFloat = NSString(string: bookModel.detail!)
                             .boundingRectWithSize(CGSizeMake(self.view.bounds.size.width - 40, CGFloat.max), 
                                           options: .UsesLineFragmentOrigin, 
                                        attributes: [NSFontAttributeName : (UIFont.systemFontOfSize(14) as AnyObject)], 
                                           context: nil).size.height
      
              // 判斷是否有圖片
              if bookModel.icon?.characters.count != 0 {
                  return 30 + detialHeight + 60 + 30         // 60 為圖片的高度
              }
              else {
                  return 30 + detialHeight + 30
              }
          }
    • 設定每一行顯示的內容

          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      
              let cell = tableView.dequeueReusableCellWithIdentifier("test", forIndexPath: indexPath) as! BookCell3
              let bookModel:BookModel = myDataArray.objectAtIndex(indexPath.row) as! BookModel
      
              // 設定 titleLabel
              cell.titleLabel!.text = bookModel.title
      
              // 設定 detailLabel
      
              // 計算 detailLabel 的高度
              let detialSize:CGSize = NSString(string: bookModel.detail!)
                                  .boundingRectWithSize(CGSizeMake(self.view.bounds.size.width - 40, CGFloat.max), 
                                                options: .UsesLineFragmentOrigin, 
                                             attributes: [NSFontAttributeName : (UIFont.systemFontOfSize(14) as AnyObject)], 
                                                context: nil).size
      
              var detialFrame:CGRect = cell.detailLabel!.frame
              detialFrame.size.height = detialSize.height + 5   // 加偏移量 5,適應標點無法換行
              detialFrame.size.width = detialSize.width + 5
              cell.detailLabel!.frame = detialFrame             // 設定 detailLabel 的 frame
      
              cell.detailLabel!.text = bookModel.detail
      
              // 判斷是否有圖片
              if bookModel.icon?.characters.count != 0 {
      
                  // 設定 iconView
      
                  var iconFrame:CGRect  = cell.iconView!.frame
                  iconFrame.origin.y = detialFrame.origin.y + detialFrame.size.height
                  cell.iconView!.frame = iconFrame
      
                  cell.iconView!.image = UIImage(named: bookModel.icon!)
      
                  // 設定 priceLabel
      
                  var priceFrame:CGRect = cell.priceLabel!.frame
                  priceFrame.origin.y = iconFrame.origin.y + iconFrame.size.height
                  cell.priceLabel!.frame = priceFrame
      
                  cell.priceLabel!.text = bookModel.price
              }
              else {
      
                  // 設定 priceLabel
      
                  var priceFrame:CGRect = cell.priceLabel!.frame
                  priceFrame.origin.y = detialFrame.origin.y + detialFrame.size.height
                  cell.priceLabel!.frame = priceFrame
      
                  cell.priceLabel!.text = bookModel.price
              }
              return cell;
          }

7.3.2 系統自動佈局方式

  • 自適應 cell 中較高的一個檢視的高度。
  • ImageView 與 Label 同行顯示,且都設定了上下邊緣約束,ImageView 的圖片填充模式為 Aspect Fit,否則圖片將會被拉長。

  • Objective-C

    • 協議方法 方式設定

          // 動態設定行高
          - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              /*
                  行高自適應 Label 高度
              */
      
              secondTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"secondTableViewCell" forIndexPath:indexPath];
      
              cell.secondLabel.text = [_labelArray objectAtIndex:indexPath.row];
      
              return [cell.contentView systemLayoutSizeFittingSize:(UILayoutFittingCompressedSize)].height + 1;
          }
      
          // 屬性變數 方式設定
          self.tableView.estimatedRowHeight = 80;
          self.tableView.rowHeight = UITableViewAutomaticDimension;
  • Swift

    • 協議方法 方式設定

          // 動態設定行高
          override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
      
              /*
                  行高自適應 Label 高度
              */
      
              var cell = tableView.dequeueReusableCellWithIdentifier("secondTableViewCell", forIndexPath: indexPath) as! secondTableViewCell
      
              cell.secondLabel.text = labelArray[indexPath.row]
      
              return cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 1
          }
      
          // 屬性變數 方式設定
          self.tableView.estimatedRowHeight = 80
          self.tableView.rowHeight = UITableViewAutomaticDimension

8、分段索引條的建立

  • Objective-C

    • 建立索引條

          // UITableViewDataSource 協議方法
          - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
      
              // 索引條資料來源陣列初始化,例項化索引條上的字元存放的陣列物件
              NSMutableArray *titleIndexArray = [NSMutableArray array];
      
              // 向陣列中新增系統自帶放大鏡圖示,會被處理成一個放大鏡
              [titleIndexArray addObject:UITableViewIndexSearch];
      
              // 向資料來源中新增資料
              for (int i = 'A'; i<='Z'; i++) {
      
                  // 點選索引條上第幾個圖示,tableView 就會跳到第幾段
                  [titleIndexArray addObject:[NSString stringWithFormat:@"%c 組 ", i]];
              }
      
              // 索引條上字元顏色,預設為藍色
              tableView.sectionIndexColor = [UIColor redColor];
      
              // 索引條上常規時背景顏色,預設為白色
              tableView.sectionIndexBackgroundColor = [UIColor blackColor];
      
              // 索引條上點選時背景顏色,預設為白色
              tableView.sectionIndexTrackingBackgroundColor = [UIColor grayColor];
      
              return titleIndexArray;
          }
    • 設定索引條偏移量

          // UITableViewDataSource 協議方法
          /*
              預設索引條與分段一一對應時,可以不寫該方法。如果索引條的前面加了個搜尋小圖示等,需要重寫這個方法。A 所在的分段在 tableView 中為第 0 段
          */
          - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
              return index - 1;
          }
  • Swift

    • 建立索引條

          // UITableViewDataSource 協議方法
          func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
      
              // 索引條資料來源陣列初始化,例項化索引條上的字元存放的陣列物件
              var titleIndexArray:[String] = Array()
      
              // 向陣列中新增系統自帶放大鏡圖示,會被處理成一個放大鏡
              titleIndexArray.append(UITableViewIndexSearch)
      
                  // 向資料來源中新增資料
                  for i in 65...90 {
      
                      // 點選索引條上第幾個圖示,tableView 就會跳到第幾段
                      titleIndexArray.append("\(Character(UnicodeScalar(i))) 組 ")
                  }
      
                  // 索引條上索引條上字元顏色,預設為藍色
                  tableView.sectionIndexColor = UIColor.redColor()
      
                  // 索引條上常規時背景顏色,預設為白色
                  tableView.sectionIndexBackgroundColor = UIColor.blackColor()
      
                  // 索引條上點選時背景顏色,預設為白色
                  tableView.sectionIndexTrackingBackgroundColor = UIColor.grayColor()
      
                  return titleIndexArray
          }
    • 設定索引條偏移量

          // UITableViewDataSource 協議方法
          /*
              預設索引條與分段一一對應時,可以不寫該方法。如果索引條的前面加了個搜尋小圖示等,需要重寫這個方法。A 所在的分段在 tableView 中為第 0 段
          */
          func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
              return index - 1
          }
  • 執行效果

    TableView25TableView26

9、搜尋框的建立

  • 在 iOS 8.0 以上版本中, 我們可以使用 UISearchController 來非常方便地在 UITableView 中新增搜尋框. 而在之前版本中, 我們還是必須使用 UISearchDisplayController + UISearchBar 的組合方式。

  • 我們建立的 tableView 和搜尋控制器建立的 tableView 都會走代理方法,需要在代理方法中判斷響應代理方法的 tableView 是哪一個,如果響應代理方法的 tableView 不是我建立的,說明一定是搜尋控制器建立的。在 iOS 8.0 以下版本中需使用 tableView == myTableView 判斷,在 iOS 8.0 以上版本中需使用 mySearchController.active 判斷。

9.1 在 iOS 8.0 以下版本中

  • Objective-C

    • 遵守協議 UISearchDisplayDelegate

    • 搜尋結果陣列初始化

          // 宣告搜尋結果存放陣列
          @property(nonatomic, retain)NSMutableArray *mySearchResultArray;
      
          // 初始化搜尋結果存放陣列
          mySearchResultArray = [[NSMutableArray alloc] init];
    • searchDisplayController 初始化

          // 宣告搜尋控制器,自帶一個表格檢視,用來展示搜尋結果,必須設定為全域性變數
          @property(nonatomic, retain)UISearchDisplayController *mySearchDisplayController;
      
          // 例項化搜尋條
          UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
      
          // 例項化搜尋控制器物件
          mySearchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
      
          // 設定搜尋控制器的代理
          mySearchDisplayController.delegate = self;
      
          // 為搜尋控制器自帶 tableView 指定代理
          mySearchDisplayController.searchResultsDelegate = self;
          mySearchDisplayController.searchResultsDataSource = self;
      
          // 將搜尋條設定為 tableView 的表頭
          myTableView.tableHeaderView = searchBar;
    • UISearchDisplayDelegate 協議方法

          // 更新搜尋結果
          /*
              只要搜尋框的文字發生了改變,這個方法就會觸發。searchString 為搜尋框內輸入的內容。
          */
          - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
      
              // 清空上一次搜尋的內容
              [mySearchResultArray removeAllObjects];
      
              for (NSArray *subArray in myDataArray) {
      
                  // 將搜尋的結果存放到陣列中
                  for (NSString *str in subArray) {
      
                      NSRange range = [str rangeOfString:searchString];
      
                      if (range.length) {
                          [mySearchResultArray addObject:str];
                      }
                  }
              }
              return YES;
          }
    • UITableView 協議方法

          // 設定分段頭標題
          - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
      
              if (tableView == myTableView) {
                  return [NSString stringWithFormat:@"%c", (char)('A' + section)];
              }
              return @"搜尋結果";
          }
      
          // 設定分段數
          - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
      
              if (tableView == myTableView) {
                  return myDataArray.count;
              }
              return 1;
          }
      
          // 設定行數
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
      
              if (tableView == myTableView) {
                  return [[myDataArray objectAtIndex:section] count];
              }
              return mySearchResultArray.count;
          }
      
          // 設定每段顯示的內容
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"testIdentifier"];
      
              if (!cell) {
                  cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"testIdentifier"];
              }
      
              if (tableView == myTableView) {
                  cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
              }
              else {
                  cell.textLabel.text = [mySearchResultArray objectAtIndex:indexPath.row];
              }
              return cell;
          }
  • Swift

    • 遵守協議 UISearchDisplayDelegate

    • 搜尋結果陣列初始化

          // 初始化搜尋結果存放陣列
          var mySearchResultArray:[String] = Array()
    • searchDisplayController 初始化

          // 宣告搜尋控制器,自帶一個表格檢視,用來展示搜尋結果,必須設定為全域性變數
          var mySearchDisplayController:UISearchDisplayController!
      
          // 例項化搜尋條
          let searchBar:UISearchBar = UISearchBar(frame: CGRectMake(0, 0, self.view.frame.size.width, 44))
      
          // 例項化搜尋控制器物件
          mySearchDisplayController = UISearchDisplayController(searchBar: searchBar, contentsController: self)
      
          // 設定搜尋控制器的代理
          mySearchDisplayController.delegate = self
      
          // 為搜尋控制器自帶 tableView 指定代理
          mySearchDisplayController.searchResultsDelegate = self
          mySearchDisplayController.searchResultsDataSource = self
      
          // 將搜尋條設定為 tableView 的表頭
          myTableView.tableHeaderView = searchBar
    • UISearchDisplayDelegate 協議方法

          // 更新搜尋結果
          /*
              只要搜尋框的文字發生了改變,這個方法就會觸發。searchString 為搜尋框內輸入的內容
          */
          func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String?) -> Bool {
      
          // 清空上一次搜尋的內容
          mySearchResultArray.removeAll()
      
          // 將搜尋的結果存放到陣列中
          for subArray in myDataArray {                                                                                       
              for str in subArray {
      
                  let range:NSRange = (str as NSString).rangeOfString(searchString!)
      
                  if range.length != 0 {
                      mySearchResultArray.append(str)
                  }
              }
          }
          return true
      }
    • UITableView 協議方法

          // 設定分段頭標題
          func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {                               
              if tableView == myTableView {
                  return "\(Character(UnicodeScalar(65 + section)))"
              }
              return "搜尋結果"
          }
      
          // 設定分段數
          func numberOfSectionsInTableView(tableView: UITableView) -> Int {
      
              if tableView == myTableView {
                  return myDataArray.count
              }
              return 1
          }
      
          // 設定行數
          func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      
              if tableView == myTableView {
                  return myDataArray[section].count
              }
              return mySearchResultArray.count
          }
      
          // 設定每段顯示的內容
          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      
              var cell = tableView.dequeueReusableCellWithIdentifier("testIdentifier")
      
              if cell == nil {
                  cell = UITableViewCell(style: .Default, reuseIdentifier: "testIdentifier")
              }
      
              if tableView == myTableView {
                  cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row]
              }
              else {
                  cell!.textLabel?.text = mySearchResultArray[indexPath.row]
              }
              return cell!
          }

9.2 在 iOS 8.0 及以上版本中

  • Objective-C

    • 遵守協議 UISearchResultsUpdating

    • 搜尋結果陣列初始化

          // 宣告搜尋結果存放陣列
          @property(nonatomic, retain)NSMutableArray *mySearchResultArray;
      
          // 初始化搜尋結果存放陣列
          mySearchResultArray = [[NSMutableArray alloc] init];
    • searchController 初始化

          // 宣告搜尋控制器,自帶一個表格檢視控制器,用來展示搜尋結果,必須設定為全域性變數
          @property(nonatomic, retain)UISearchController *mySearchController;
      
          // 例項化搜尋控制器
          mySearchController = [[UISearchController alloc] initWithSearchResultsController:nil];
      
          // 設定搜尋代理
          mySearchController.searchResultsUpdater = self;
      
          // 設定搜尋條大小
          [mySearchController.searchBar sizeToFit];
      
          // 設定搜尋期間背景檢視是否取消操作,default is YES
          mySearchController.dimsBackgroundDuringPresentation = NO;
      
          // 設定搜尋期間是否隱藏導航條,default is YES
          mySearchController.hidesNavigationBarDuringPresentation = NO;
      
          // 將 searchBar 新增到表格的開頭
          myTableView.tableHeaderView = mySearchController.searchBar;
    • UISearchResultsUpdating 協議方法

          // 更新搜尋結果
          /*
              只要搜尋框的文字發生了改變,這個方法就會觸發。searchController.searchBar.text 為搜尋框內輸入的內容
          */
          - (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
      
              // 清除上一次的搜尋結果
              [mySearchResultArray removeAllObjects];
      
              // 將搜尋的結果存放到陣列中
              for (NSArray *subArray in myDataArray) {                                                                            
                  for (NSString *str in subArray) {
      
                      NSRange range = [str rangeOfString:searchController.searchBar.text];
      
                      if (range.length) {
                          [mySearchResultArray addObject:str];
                      }
                  }
              }
      
              // 重新載入表格檢視,不載入的話將不會顯示搜尋結果
              [myTableView reloadData];
          }
    • UITableView 協議方法

          // 設定分段頭標題
          - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {                           
      
              if (mySearchController.active) {
                  return @"搜尋結果";
              }
              return [NSString stringWithFormat:@"%c", (char)('A' + section)];
          }
      
          // 設定分段數
          - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
      
              if (mySearchController.active) {
                  return 1;
              }
              return myDataArray.count;
          }
      
          // 設定行數
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
      
              if (mySearchController.active) {
                  return mySearchResultArray.count;
              }
              return [[myDataArray objectAtIndex:section] count];
          }
      
          // 設定每段顯示的內容
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {                
      
              UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"testIdentifier"];
      
              if (!cell) {
                  cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"testIdentifier"];
              }
      
              if (mySearchController.active) {
                  cell.textLabel.text = [mySearchResultArray objectAtIndex:indexPath.row];
              }
              else {
                  cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
              }
              return cell;
          }
  • Swift

    • 遵守協議 UISearchResultsUpdating

    • 搜尋結果陣列初始化

          // 初始化搜尋結果存放陣列
          var searchResultArray:[String] = Array()
    • searchController 初始化

          // 宣告搜尋控制器,自帶一個表格檢視控制器,用來展示搜尋結果,必須設定為全域性變數
          var mySearchController:UISearchController!
      
          // 例項化搜尋控制器
          mySearchController = UISearchController(searchResultsController: nil)
      
          // 設定搜尋代理
          mySearchController.searchResultsUpdater = self
      
          // 設定搜尋條大小
          mySearchController.searchBar.sizeToFit()
      
          // 設定搜尋期間背景檢視是否取消操作,default is YES
          mySearchController.dimsBackgroundDuringPresentation = false
      
          // 設定搜尋期間是否隱藏導航條,default is YES
          mySearchController.hidesNavigationBarDuringPresentation = false
      
          // 將 searchBar 新增到表格的開頭
          myTableView.tableHeaderView = mySearchController.searchBar
    • UISearchResultsUpdating 協議方法

          // 更新搜尋結果
          /*
              只要搜尋框的文字發生了改變,這個方法就會觸發。searchController.searchBar.text 為搜尋框內輸入的內容
          */
          func updateSearchResultsForSearchController(searchController: UISearchController) {
      
              // 清除上一次的搜尋結果
              searchResultArray.removeAll()
      
              // 將搜尋的結果存放到陣列中
              for subArray in myDataArray {
                  for str in subArray {
      
                      let range:NSRange = (str as NSString).rangeOfString(searchController.searchBar.text!)
      
                      if range.length != 0 {
                          searchResultArray.append(str)
                      }
                  }
              }
      
              // 重新載入表格檢視,不載入的話將不會顯示搜尋結果
              myTableView.reloadData()
          }
    • UITableView 協議方法

          // 設定分段頭標題
          func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
      
              if mySearchController.active {
                  return "搜尋結果"
              }
              return "\(Character(UnicodeScalar(65 + section)))"
          }
      
          // 設定分段數
          func numberOfSectionsInTableView(tableView: UITableView) -> Int {
      
              if mySearchController.active {
                  return 1
              }
              return myDataArray.count
          }
      
          // 設定行數
          func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      
              if mySearchController.active {
                  return searchResultArray.count
              }
              return myDataArray[section].count
          }
      
          // 設定每段顯示的內容
          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      
              var cell = tableView.dequeueReusableCellWithIdentifier("testIdentifier")
      
              if cell == nil {
                  cell = UITableViewCell(style: .Default, reuseIdentifier: "testIdentifier")
              }
      
              if mySearchController.active {
                  cell!.textLabel?.text = searchResultArray[indexPath.row]
              }
              else {
                  cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row]
              }
              return cell!
          }
  • 執行效果

    TableView27TableView28

10、表格摺疊

  • 通過改變分段的行數實現分段的摺疊與開啟。分段處於摺疊狀態時,設定分段的行數為 0。

  • Objective-C

    • 分段摺疊狀態陣列初始化

          // 宣告記錄摺疊狀態陣列
          @property(nonatomic, retain)NSMutableArray *foldStatusArray;
      
          // 初始化記錄摺疊狀態陣列
          foldStatusArray = [[NSMutableArray alloc] init];
      
          // 給分段摺疊狀態陣列賦初值,狀態值為 1 時,分段摺疊
          for (int i = 0; i < myDataArray.count; i++) {
              [foldStatusArray addObject:[NSNumber numberWithBool:YES]];
          }
    • UITableView 協議方法

          // 設定行數,UITableViewDataSource 協議方法
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
      
              // 獲取分段的摺疊狀態,foldStatusArray 存放的是 NSNumber 型別的值
              BOOL isFold = [[foldStatusArray objectAtIndex:section] boolValue];
      
              if (isFold) {
      
                  // 分段處於摺疊狀態時,設定分段的行數為 0
                  return 0;
              }
              return [[myDataArray objectAtIndex:section] count];
          }
      
          // 設定分段頭標題高度,UITableViewDelegate 協議方法
          - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
              return 30;
          }
      
          // 設定分段頭標題檢視,UITableViewDelegate 協議方法
          - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
      
              UIButton *headerButton = [UIButton buttonWithType:UIButtonTypeCustom];
              headerButton.frame = CGRectMake(0, 0, tableView.frame.size.width, 30);
              headerButton.backgroundColor = [UIColor orangeColor];
      
              headerButton.titleLabel.font = [UIFont boldSystemFontOfSize:20];
              headerButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
              headerButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
              headerButton.layer.borderColor = [[UIColor lightGrayColor] CGColor];
              headerButton.layer.borderWidth = 1;
      
              // 設定分段頭標題顯示內容
              [headerButton setTitle:[NSString stringWithFormat:@" %c", (char)('A' + section)] forState:UIControlStateNormal];
      
              // 設定分段的 tag 值
              headerButton.tag = 100 + section;
      
              // 新增分段頭標題點選響應事件
              [headerButton addTarget:self action:@selector(headerButtonClick:) forControlEvents:UIControlEventTouchUpInside];        
              return headerButton;
          }
    • 頭標題點選響應事件

          - (void)headerButtonClick:(UIButton *)button {
      
              // 獲取分段的摺疊狀態,foldStatusArray 存放的是 NSNumber 型別的值
              BOOL isFold = [[foldStatusArray objectAtIndex:button.tag - 100] boolValue];
      
              // 改變分段的摺疊狀態
              foldStatusArray[button.tag - 100] = [NSNumber numberWithInt: isFold ? NO : YES];
      
              // 過載分段
              [myTableView reloadSections:[NSIndexSet indexSetWithIndex:button.tag - 100] withRowAnimation:UITableViewRowAnimationAutomatic];
          }
  • Swift

    • 分段摺疊狀態陣列初始化

          // 初始化分段摺疊狀態陣列
          var foldStatusArray:[NSNumber] = Array()
      
          // 分段摺疊狀態陣列賦值,狀態值為 1 時,分段摺疊
          for _ in 0 ..< myDataArray.count {
              foldStatusArray.append(NSNumber(bool: true))
          }
    • UITableView 協議方法

          // 設定行數
          func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      
              // 獲取分段的摺疊狀態,foldStatusArray 存放的是 NSNumber 型別的值
              let isFold:Bool = foldStatusArray[section].boolValue
      
              if isFold {
      
                  // 分段處於摺疊狀態時,設定分段的行數為 0
                  return 0
              }
              return myDataArray[section].count
          }
      
          // 設定分段頭標題的高度
          func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
              return 30
          }
      
          // 設定分段頭標題檢視
          func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
      
              let headerButton:UIButton = UIButton(type: .Custom)
              headerButton.frame = CGRectMake(0, 0, tableView.frame.size.width, 30)
              headerButton.backgroundColor = UIColor.orangeColor()
      
              headerButton.titleLabel!.font = UIFont.boldSystemFontOfSize(20)
              headerButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.Left
              headerButton.contentVerticalAlignment = UIControlContentVerticalAlignment.Center
              headerButton.layer.borderColor = UIColor.lightGrayColor().CGColor
              headerButton.layer.borderWidth = 1
      
              // 設定分段頭標題顯示內容
              headerButton.setTitle(" \(Character(UnicodeScalar(65 + section)))", forState: UIControlState.Normal)
      
              // 設定分段的 tag 值
              headerButton.tag = 100 + section
      
              // 新增分段頭標題點選響應事件
              headerButton.addTarget(self, action: #selector(UiTableViewController10.headerButtonClick(_:)), 
                                 forControlEvents: UIControlEvents.TouchUpInside)
      
              return headerButton
          }
    • 頭標題點選響應事件

          func headerButtonClick(button:UIButton) {
      
              // 獲取分段的摺疊狀態,foldStatusArray 存放的是 NSNumber 型別的值
              let isFold:Bool = foldStatusArray[button.tag - 100].boolValue
      
              // 改變分段的摺疊狀態
              foldStatusArray[button.tag - 100] = NSNumber(bool: isFold ? false : true)
      
              // 過載分段
              myTableView.reloadSections(NSIndexSet(index: button.tag - 100), withRowAnimation: .Automatic)
          }
  • 執行效果

    TableView29TableView30

11、表格編輯

  • Objective-C

    • 設定表格編輯開關狀態

          // 設定表格的編輯狀態
          myTableView.editing = YES;
      
          // 翻轉表格的編輯狀態
          myTableView.editing = !myTableView.editing;
      
          // 帶動畫翻轉表格的編輯狀態
          [myTableView setEditing:!myTableView.editing animated:YES];
    • 修改左滑刪除按鈕的內容

          // UITableViewDelegate 協議方法
          /*
              預設為 Delete
          */
          - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {   
              return @"刪除";
          }
    • 設定左滑多按鈕

          - (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              UITableViewRowAction *action0 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal
                                                                                 title:@"關注"
                                                                               handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
      
                  NSLog(@"點選了關注");
      
                  // 收回左滑出現的按鈕(退出編輯模式)
                  tableView.editing = NO;
              }];
      
              UITableViewRowAction *action1 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault
                                                                                 title:@"刪除"
                                                                               handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
      
                  [[myDataArray objectAtIndex:indexPath.section] removeObjectAtIndex:indexPath.row];
                  [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
              }];
      
              // 按鈕從右向左的順序排列
              return @[action1, action0];
          }
    • 設定編輯模式

          /*
              UITableViewCellEditingStyleNone;        // 無
              UITableViewCellEditingStyleDelete;      // 刪除模式,預設
              UITableViewCellEditingStyleInsert;      // 插入模式
              UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert;   // 多選模式
          */
      
          // UITableViewDelegate 協議方法
          - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 刪除、插入、多選刪除,不設定預設時為刪除
      
              if (0 == indexPath.section) {
                  return UITableViewCellEditingStyleDelete;
              }
              else {
                  return UITableViewCellEditingStyleInsert;
              }
          }
    • 表格刪除、插入

          表格刪除:
              1. 先將資料從資料來源裡刪除,
              2. 再從 tableView 裡刪除 cell:
                      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
                                       withRowAnimation:UITableViewRowAnimationAutomatic];
      
                  或者再直接過載整個表格:
                      [tableView reloadData];
      
                  或者在直接過載分段:
                      [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] 
                               withRowAnimation:UITableViewRowAnimationAutomatic];
      
          表格插入:
              1. 先將資料插入到資料來源中,
              2. 然後再插入一個 cell:
                      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
                                       withRowAnimation:UITableViewRowAnimationAutomatic];
      
                  或者再直接過載整個表格:
                      [tableView reloadData];
      
                  或者在直接過載分段:
                      [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] 
                               withRowAnimation:UITableViewRowAnimationAutomatic];
          // 表格刪除或插入,預設為刪除模式,寫入該方法即表示允許刪除
      
          // UITableViewDataSource 協議方法
          - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 判斷編輯風格,預設是刪除
              if (editingStyle == UITableViewCellEditingStyleDelete) {
      
                  // 表格刪除
      
                      // 從資料來源裡刪除
                      [[myDataArray objectAtIndex:indexPath.section] removeObjectAtIndex:indexPath.row];
      
                      // 從 tableView 裡刪除 cell
                      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 
              }
              else if (editingStyle == UITableViewCellEditingStyleInsert) {
      
                  Person *person = [[Person alloc] init];                 person.name = @"xiao bai";
                  person.age = 18;
      
                  // 表格插入
      
                      // 插入到資料來源中
                      [[myDataArray objectAtIndex:indexPath.section] insertObject:person atIndex:indexPath.row];
      
                      // 插入一個 cell
                      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
              }
          }
    • 表格移動

          表格移動:
              1. 先在資料來源中找到需要移動的物件。
              2. 然後在資料來源陣列中從原始位置刪掉。
              3. 再在資料來源陣列中插入到新位置。
              4. 最後重新載入表格:
                      [tableView reloadData];
      
                  或者在直接過載分段:
                      [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] 
                               withRowAnimation:UITableViewRowAnimationAutomatic];
          // 寫入該方法即表示允許移動
      
          // UITableViewDataSource 協議方法
          - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
      
              // 找到需要移動的物件
              Person *person = [[myDataArray objectAtIndex:sourceIndexPath.section] objectAtIndex:sourceIndexPath.row];
      
              // 從原始位置刪掉
              [[myDataArray objectAtIndex:sourceIndexPath.section] removeObjectAtIndex:sourceIndexPath.row];
      
              // 插入到新位置
              [[myDataArray objectAtIndex:destinationIndexPath.section] insertObject:person atIndex:destinationIndexPath.row];
      
              // 重新整理 tableView
              [tableView reloadData];
          }
  • Swift

    • 設定表格編輯開關狀態

          // 設定表格的編輯狀態
          myTableView.editing = true
      
          // 翻轉表格的編輯狀態
          myTableView.editing = !myTableView.editing
      
          // 帶動畫翻轉表格的編輯狀態
          myTableView.setEditing(!myTableView.editing, animated: true)
    • 修改左滑刪除按鈕的內容

          // UITableViewDelegate 協議方法
          func tableView(tableView: UITableView, titleForDeleteConfirmationButtonForRowAtIndexPath indexPath: NSIndexPath) -> String? {
      
              // 預設為 Delete
              return "刪除"
          }
    • 設定左滑多按鈕

          func tableView(tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
      
              let action0:UITableViewRowAction = UITableViewRowAction(style: .normal, title: "關注") 
                  { (action:UITableViewRowAction, indexPath:IndexPath) in
      
                      print("點選了關注")
      
                      // 收回左滑出現的按鈕(退出編輯模式)
                      tableView.isEditing = false
                  }
      
              let action1:UITableViewRowAction = UITableViewRowAction(style: .normal, title: "刪除") 
                  { (action:UITableViewRowAction, indexPath:IndexPath) in
      
                      myDataArray[indexPath.section].remove(at: indexPath.row)
                      tableView.deleteRows(at: NSArray(object: indexPath) as! [IndexPath], with: .automatic)
                  }
      
              // 按鈕從右向左的順序排列
              return [action1, action0]
          }
    • 設定編輯模式

          /*
              刪除、插入,不設定預設時為刪除,不能設定多選刪除模式,
              若要實現多選刪除,需設定 myTableView.allowsMultipleSelectionDuringEditing = true
      
              UITableViewCellEditingStyle.None       // 無
              UITableViewCellEditingStyle.Delete     // 刪除模式,預設
              UITableViewCellEditingStyle.Insert     // 插入模式
          */
          // UITableViewDelegate 協議方法
          func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
      
              if 0 == indexPath.section {
                  return .Delete
              }
              else {
                  return .Insert
              }
          }
    • 表格刪除、插入

          表格刪除:
              1. 先將資料從資料來源裡刪除,
              2. 再從 tableView 裡刪除 cell:
                      tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], 
                                      withRowAnimation: .Automatic)
      
                  或者再直接過載整個表格:
                      tableView.reloadData()
      
                  或者在直接過載分段:
                      tableView.reloadSections(NSIndexSet(index: indexPath.section) , 
                              withRowAnimation: .Automatic)
      
          表格插入:
              1. 先將資料插入到資料來源中,
              2. 然後再插入一個 cell:
                      tableView.insertRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], 
                                      withRowAnimation: .Automatic)
      
                  或者再直接過載整個表格:
                      tableView.reloadData()
      
                  或者在直接過載分段:
                      tableView.reloadSections(NSIndexSet(index: indexPath.section) , 
                              withRowAnimation: .Automatic)
          // 表格刪除或插入,預設為刪除模式,寫入該方法即表示允許刪除
      
          // UITableViewDataSource 協議方法
          func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
      
              // 判斷編輯風格,預設是刪除
              if editingStyle == UITableViewCellEditingStyle.Delete {
      
                  // 表格刪除
      
                      // 從資料來源裡刪除
                      myDataArray[indexPath.section].removeAtIndex(indexPath.row)
      
                      // 從 tableView 裡刪除 cell
                      tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], withRowAnimation: .Automatic)
              }
              else if editingStyle == UITableViewCellEditingStyle.Insert {
      
                  let person:Person = Person()
                  person.name = "xiao bai”
                  person.age = 18
      
                  // 表格插入
      
                      // 插入到資料來源中
                      myDataArray[indexPath.section].insert(person, atIndex: indexPath.row)
      
                      // 插入一個 cell
                      tableView.insertRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], withRowAnimation: .Automatic)
              }
          }
    • 表格移動

          表格移動:
              1. 先在資料來源中找到需要移動的物件。
              2. 然後在資料來源陣列中從原始位置刪掉。
              3. 再在資料來源陣列中插入到新位置。
              4. 最後重新載入表格:
                      tableView.reloadData()
      
                  或者在直接過載分段:
                      tableView.reloadSections(NSIndexSet(index: indexPath.section) , 
                              withRowAnimation: .Automatic)
          // 寫入該方法即表示允許移動
      
          // UITableViewDataSource 協議方法
          func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
      
              // 找到需要移動的物件
              let person:Person = myDataArray[sourceIndexPath.section][sourceIndexPath.row]
      
              // 從原始位置刪掉
              myDataArray[sourceIndexPath.section].removeAtIndex(sourceIndexPath.row)
      
              // 插入到新位置
              myDataArray[destinationIndexPath.section].insert(person, atIndex: destinationIndexPath.row)
      
              tableView.reloadData()
          }
  • 執行效果

    TableView31TableView32

12、表格多選刪除

12.1 系統方式

  • 將要刪除的資料新增到待刪陣列中,從資料來源中刪除待刪陣列中包含的資料,重新整理表格。

  • OC 中可設定編輯模式為 UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert; 或者設定 myTableView.allowsMultipleSelectionDuringEditing = YES; 進入多選模式。

  • Swift 需設定 myTableView.allowsMultipleSelectionDuringEditing = true 進入多選模式。

  • Objective-C

    • 待刪資料陣列初始化

          // 宣告待刪資料陣列
          @property(nonatomic, retain)NSMutableArray *tempDeleteArray;
      
          // 初始化待刪資料陣列
          tempDeleteArray = [[NSMutableArray alloc] init];
    • 自定義方法

          // 編輯按鈕點選響應事件
          - (void)editClick:(UIButton *)button {
      
              // 改變編輯開關狀態
              [myTableView setEditing:!myTableView.editing animated:YES];
      
              // 設定編輯模式,允許編輯時多選,或者在協議方法中設定
              myTableView.allowsMultipleSelectionDuringEditing = YES;
      
              // 當編輯狀態發生改變的時候,清空待刪陣列
              [tempDeleteArray removeAllObjects];
      
              [myTableView reloadData];
          }
      
          // 刪除按鈕點選響應事件
          - (void)deleteClick:(UIButton *)button {
      
              // 從資料來源中刪除待選陣列中包含的資料
              [myDataArray removeObjectsInArray:tempDeleteArray];
      
              // 清空待刪陣列
              [tempDeleteArray removeAllObjects];
      
              [myTableView reloadData];
          }
    • UITableView 協議方法

          // 設定編輯模式
          /*
              刪除、插入、多選刪除,不設定預設時為刪除,
              或者在編輯按鈕點選事件中直接設定 myTableView.allowsMultipleSelectionDuringEditing = YES;
          */
          - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 多選刪除
              return UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert;                       
          }
      
          // 表格選中點選響應事件
          - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 判斷 tableView 的編輯狀態,表格處於編輯狀態
              if (tableView.isEditing) {
      
                  // 選中 cell 的時候,將對應的資料來源模型新增到待刪除陣列中
                  [tempDeleteArray addObject:[myDataArray objectAtIndex:indexPath.row]];
              }
              else {
      
                  // 恢復未選中狀態時的顏色
                  [tableView deselectRowAtIndexPath:indexPath animated:YES];
              }
          }
      
          // 表格取消選中點選響應事件
          - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 判斷 tableView 的編輯狀態,表格處於編輯狀態
              if (tableView.isEditing) {
      
                  // 將對應的資料模型從待刪除陣列中移除
                  [tempDeleteArray removeObject:[myDataArray objectAtIndex:indexPath.row]];
              }
          }
  • Swift

    • 待刪資料陣列初始化

          // 初始化待刪資料陣列
          var tempDeleteArray:[Dog] = Array()
    • 自定義方法

          // 編輯按鈕點選響應事件
          func editClick(button:UIButton){
      
              // 改變編輯開關狀態
              myTableView.setEditing(!myTableView.editing, animated: true)
      
              // 設定編輯模式,允許編輯時多選
              myTableView.allowsMultipleSelectionDuringEditing = true
      
              // 當編輯狀態發生改變的時候,清空待刪陣列
              tempDeleteArray.removeAll()
      
              myTableView.reloadData()
          }
      
          // 刪除按鈕點選響應事件
          func deleteClick(button:UIButton){
      
              // 從資料來源中刪除待選陣列中包含的資料
              myDataArray.removeObjectsInArray(tempDeleteArray)
      
              // 清空待刪陣列
              tempDeleteArray.removeAll()
      
              myTableView.reloadData()
          }
    • UITableView 協議方法

          // 表格被選中
          func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
      
              // 判斷 tableView 的編輯狀態,表格處於編輯狀態
              if tableView.editing {
      
                  // 選中 cell 的時候,將對應的資料來源模型新增到待刪除陣列中
                  tempDeleteArray.append(myDataArray.objectAtIndex(indexPath.row) as! Dog)
              }
              else {
      
                  // 恢復未選中狀態時的顏色
                  tableView.deselectRowAtIndexPath(indexPath, animated: true)
              }
          }
      
          // 表格被取消選中
          func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
      
              // 判斷 tableView 的編輯狀態,表格處於編輯狀態
              if tableView.editing {
      
                  // 將對應的資料模型從待刪除陣列中移除
                  tempDeleteArray.append(myDataArray.objectAtIndex(indexPath.row) as! Dog)
              }
          }
  • 執行效果

    TableView33TableView34

12.2 自定義方式 1

  • Objective-C

    • XMGDeal.h

          #import <Foundation/Foundation.h>
      
          @interface XMGDeal : NSObject
      
          @property (strong, nonatomic) NSString *buyCount;
          @property (strong, nonatomic) NSString *price;
          @property (strong, nonatomic) NSString *title;
          @property (strong, nonatomic) NSString *icon;
      
          /** 狀態量標識有無被打鉤 */
          @property (assign, nonatomic, getter=isChecked) BOOL checked;
      
          + (instancetype)dealWithDict:(NSDictionary *)dict;
      
          @end
    • XMGDeal.m

          #import "XMGDeal.h"
      
          @implementation XMGDeal
      
          + (instancetype)dealWithDict:(NSDictionary *)dict {
      
              XMGDeal *deal = [[self alloc] init];
              [deal setValuesForKeysWithDictionary:dict];
      
              return deal;
          }
      
          @end
    • XMGDealCell.xib

      TableView15

    • XMGDealCell.h

          #import <UIKit/UIKit.h>
      
          @class XMGDeal;
      
          @interface XMGDealCell : UITableViewCell
      
          /** 團購模型資料 */
          @property (nonatomic, strong) XMGDeal *deal;
      
          + (instancetype)cellWithTableView:(UITableView *)tableView;
      
          @end
    • XMGDealCell.m

          #import "XMGDealCell.h"
          #import "XMGDeal.h"
      
          @interface XMGDealCell()
      
          @property (weak, nonatomic) IBOutlet UIImageView *iconView;
          @property (weak, nonatomic) IBOutlet UILabel *titleLabel;
          @property (weak, nonatomic) IBOutlet UILabel *buyCountLabel;
          @property (weak, nonatomic) IBOutlet UIImageView *checkView;
          @property (weak, nonatomic) IBOutlet UILabel *priceLabel;
      
          @end
      
          @implementation XMGDealCell
      
          + (instancetype)cellWithTableView:(UITableView *)tableView {
      
              static NSString *ID = @"deal";
              XMGDealCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
              if (cell == nil) {
                  cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([XMGDealCell class]) 
                                                        owner:nil 
                                                      options:nil] lastObject];
              }
              return cell;
          }
      
          - (void)setDeal:(XMGDeal *)deal {
      
              _deal = deal;
      
              // 設定資料
              self.iconView.image = [UIImage imageNamed:deal.icon];
              self.titleLabel.text = deal.title;
              self.priceLabel.text = [NSString stringWithFormat:@"¥%@", deal.price];
              self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已購買", deal.buyCount];
      
              // 設定打鉤控制元件的顯示和隱藏
              self.checkView.hidden = !deal.isChecked;
          }
      
          @end
    • XMGDealsViewController.m

          #import "XMGDealsViewController.h"
          #import "XMGDeal.h"
          #import "XMGDealCell.h"
      
          @interface XMGDealsViewController () <UITableViewDataSource, UITableViewDelegate>
      
          @property (weak, nonatomic) IBOutlet UITableView *tableView;
      
          /** 所有的團購資料 */
          @property (nonatomic, strong) NSMutableArray *deals;
      
          @end
      
          @implementation XMGDealsViewController
      
          - (NSMutableArray *)deals {
      
              if (_deals == nil) {
      
                  // 載入plist中的字典陣列
                  NSString *path = [[NSBundle mainBundle] pathForResource:@"deals.plist" ofType:nil];
                  NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
      
                  // 字典陣列 -> 模型陣列
                  NSMutableArray *dealArray = [NSMutableArray array];
                  for (NSDictionary *dict in dictArray) {
                      XMGDeal *deal = [XMGDeal dealWithDict:dict];
                      [dealArray addObject:deal];
                  }
      
                  _deals = dealArray;
              }
              return _deals;
          }
      
          - (IBAction)remove {
      
              // 臨時陣列:存放即將需要刪除的團購資料
              NSMutableArray *deletedDeals = [NSMutableArray array];
              for (XMGDeal *deal in self.deals) {
                  if (deal.isChecked) [deletedDeals addObject:deal];
              }
      
              // 刪除模型資料
              [self.deals removeObjectsInArray:deletedDeals];
      
              // 重新整理表格
              [self.tableView reloadData];
          }
      
          #pragma mark - Table view data source
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
              return self.deals.count;
          }
      
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              XMGDealCell *cell = [XMGDealCell cellWithTableView:tableView];
      
              // 取出模型資料
              cell.deal = self.deals[indexPath.row];
      
              return cell;
          }
      
          #pragma mark - TableView 代理方法
          - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 取消選中這一行
              [tableView deselectRowAtIndexPath:indexPath animated:YES];
      
              // 模型的打鉤屬性取反
              XMGDeal *deal = self.deals[indexPath.row];
              deal.checked = !deal.isChecked;
      
              // 重新整理表格
              [tableView reloadData];
          }
      
          @end
  • 執行效果

    TableView35TableView36

12.3 自定義方式 2

  • Objective-C

    • XMGDeal.h

          #import <Foundation/Foundation.h>
      
          @interface XMGDeal : NSObject
      
          @property (strong, nonatomic) NSString *buyCount;
          @property (strong, nonatomic) NSString *price;
          @property (strong, nonatomic) NSString *title;
          @property (strong, nonatomic) NSString *icon;
      
          + (instancetype)dealWithDict:(NSDictionary *)dict;
      
          @end
    • XMGDeal.m

          #import "XMGDeal.h"
      
          @implementation XMGDeal
      
          + (instancetype)dealWithDict:(NSDictionary *)dict {
      
              XMGDeal *deal = [[self alloc] init];
              [deal setValuesForKeysWithDictionary:dict];
      
              return deal;
          }
      
          @end
    • XMGDealCell.xib

      TableView39

    • XMGDealCell.h

          #import <UIKit/UIKit.h>
      
          @class XMGDeal;
      
          @interface XMGDealCell : UITableViewCell
      
          /** 團購模型資料 */
          @property (nonatomic, strong) XMGDeal *deal;
      
          + (instancetype)cellWithTableView:(UITableView *)tableView;
      
          @end
    • XMGDealCell.m

          #import "XMGDealCell.h"
          #import "XMGDeal.h"
      
          @interface XMGDealCell()
      
          @property (weak, nonatomic) IBOutlet UIImageView *iconView;
          @property (weak, nonatomic) IBOutlet UILabel *titleLabel;
          @property (weak, nonatomic) IBOutlet UILabel *buyCountLabel;
          @property (weak, nonatomic) IBOutlet UIImageView *checkView;
          @property (weak, nonatomic) IBOutlet UILabel *priceLabel;
      
          @end
      
          @implementation XMGDealCell
      
          + (instancetype)cellWithTableView:(UITableView *)tableView {
      
              static NSString *ID = @"deal";
              XMGDealCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
              if (cell == nil) {
                  cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([XMGDealCell class]) 
                                                        owner:nil 
                                                      options:nil] lastObject];
              }
              return cell;
          }
      
          - (void)setDeal:(XMGDeal *)deal {
      
              _deal = deal;
      
              // 設定資料
              self.iconView.image = [UIImage imageNamed:deal.icon];
              self.titleLabel.text = deal.title;
              self.priceLabel.text = [NSString stringWithFormat:@"¥%@", deal.price];
              self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已購買", deal.buyCount];
          }
      
          @end
    • XMGDealsViewController.m

          #import "XMGDealsViewController.h"
          #import "XMGDeal.h"
          #import "XMGDealCell.h"
      
          @interface XMGDealsViewController () <UITableViewDataSource, UITableViewDelegate>
      
          @property (weak, nonatomic) IBOutlet UITableView *tableView;
      
          /** 所有的團購資料 */
          @property (nonatomic, strong) NSMutableArray *deals;
      
          /** 即將要刪除的團購 */
          @property (nonatomic, strong) NSMutableArray *deletedDeals;
      
          @end
      
          @implementation XMGDealsViewController
      
          - (NSMutableArray *)deletedDeals {
      
              if (!_deletedDeals) {
                  _deletedDeals = [NSMutableArray array];
              }
              return _deletedDeals;
          }
      
          - (NSMutableArray *)deals {
      
              if (_deals == nil) {
      
                  // 載入plist中的字典陣列
                  NSString *path = [[NSBundle mainBundle] pathForResource:@"deals.plist" ofType:nil];
                  NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
      
                  // 字典陣列 -> 模型陣列
                  NSMutableArray *dealArray = [NSMutableArray array];
                  for (NSDictionary *dict in dictArray) {
                      XMGDeal *deal = [XMGDeal dealWithDict:dict];
                      [dealArray addObject:deal];
                  }
      
                  _deals = dealArray;
              }
              return _deals;
          }
      
          - (IBAction)remove {
      
              // 刪除模型資料
              [self.deals removeObjectsInArray:self.deletedDeals];
      
              // 重新整理表格
              [self.tableView reloadData];
      
              // 清空陣列
              [self.deletedDeals removeAllObjects];
          }
      
          #pragma mark - Table view data source
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
              return self.deals.count;
          }
      
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              XMGDealCell *cell = [XMGDealCell cellWithTableView:tableView];
      
              // 取出模型資料
              cell.deal = self.deals[indexPath.row];
      
              cell.checkView.hidden = ![self.deletedDeals containsObject:cell.deal];
      
              return cell;
          }
      
          #pragma mark - TableView代理方法
          - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 取消選中這一行
              [tableView deselectRowAtIndexPath:indexPath animated:YES];
      
              // 取出模型
              XMGDeal *deal = self.deals[indexPath.row];
              if ([self.deletedDeals containsObject:deal]) {
                  [self.deletedDeals removeObject:deal];
              } else {
                  [self.deletedDeals addObject:deal];
              }
      
              // 重新整理表格
              [tableView reloadData];
          }
      
          @end
  • 執行效果

    TableView37TableView38

13、聊天佈局

  • Objective-C

    • XMGMessage.h

          #import <UIKit/UIKit.h>
      
          typedef enum {
              XMGMessageTypeMe = 0,
              XMGMessageTypeOther = 1
          } XMGMessageType;
      
          @interface XMGMessage : NSObject
      
          @property (nonatomic, strong) NSString *text;
          @property (nonatomic, strong) NSString *time;
          @property (nonatomic, assign) XMGMessageType type;
      
          /** cell 的高度 */
          @property (nonatomic, assign) CGFloat cellHeight;
      
          /** 是否隱藏時間 */
          @property (nonatomic, assign, getter=isHideTime) BOOL hideTime;
      
          + (instancetype)messageWithDict:(NSDictionary *)dict;
      
          @end
    • XMGMessage.m

          #import "XMGMessage.h"
      
          @implementation XMGMessage
      
          + (instancetype)messageWithDict:(NSDictionary *)dict {
      
              XMGMessage *message = [[self alloc] init];
              [message setValuesForKeysWithDictionary:dict];
              return message;
          }
      
          @end
    • Main.storyboard

      • 設定 cell

        • 單 Cell 佈局

          TableView16

        • 多 Cell 佈局

          TableView20

      • 設定氣泡圖片拉伸

        TableView18

      • 設定按鈕邊距

        TableView19

    • XMGMessageCell.h

          #import <UIKit/UIKit.h>
      
          @class XMGMessage;
      
          @interface XMGMessageCell : UITableViewCell
      
          @property (nonatomic, strong) XMGMessage *message;
      
          @end
    • XMGMessageCell.m

      • 單 Cell 佈局

            #import "XMGMessageCell.h"
            #import "XMGMessage.h"
        
            #define MAS_SHORTHAND
            #define MAS_SHORTHAND_GLOBALS
            #import "Masonry.h"
        
            @interface XMGMessageCell()
        
            @property (weak, nonatomic) IBOutlet UILabel *timeLabel;
        
            @property (weak, nonatomic) IBOutlet UIButton *textButton;
            @property (weak, nonatomic) IBOutlet UIImageView *iconView;
        
            @property (weak, nonatomic) IBOutlet UIButton *otherTextButton;
            @property (weak, nonatomic) IBOutlet UIImageView *otherIconView;
        
            @end
        
            @implementation XMGMessageCell
        
            - (void)awakeFromNib {
        
                self.textButton.titleLabel.numberOfLines = 0;
                self.otherTextButton.titleLabel.numberOfLines = 0;
            }
        
            - (void)setMessage:(XMGMessage *)message {
        
                _message = message;
        
                if (message.hideTime) { // 隱藏時間
                    self.timeLabel.hidden = YES;
                    [self.timeLabel updateConstraints:^(MASConstraintMaker *make) {
                        make.height.equalTo(0);
                    }];
                } else { // 顯示時間
                    self.timeLabel.text = message.time;
                    self.timeLabel.hidden = NO;
                    [self.timeLabel updateConstraints:^(MASConstraintMaker *make) {
                        make.height.equalTo(21);
                    }];
                }
        
                // 強制更新
                [self layoutIfNeeded];
        
                if (message.type == XMGMessageTypeMe) { // 右邊
                    [self settingShowTextButton:self.textButton 
                                   showIconView:self.iconView 
                                 hideTextButton:self.otherTextButton 
                                   hideIconView:self.otherIconView];
                } else { // 左邊
                    [self settingShowTextButton:self.otherTextButton 
                                   showIconView:self.otherIconView 
                                 hideTextButton:self.textButton 
                                   hideIconView:self.iconView];
                }
            }
        
            /**
             * 處理左右按鈕、頭像
             */
            - (void)settingShowTextButton:(UIButton *)showTextButton 
                             showIconView:(UIImageView *)showIconView 
                           hideTextButton:(UIButton *)hideTextButton 
                             hideIconView:(UIImageView *)hideIconView {
        
                hideTextButton.hidden = YES;
                hideIconView.hidden = YES;
        
                showTextButton.hidden = NO;
                showIconView.hidden = NO;
        
                // 設定按鈕的文字
                [showTextButton setTitle:self.message.text forState:UIControlStateNormal];
        
                // 強制更新
                [showTextButton layoutIfNeeded];
        
                // 設定按鈕的高度就是titleLabel的高度
                [showTextButton updateConstraints:^(MASConstraintMaker *make) {
                    CGFloat buttonH = showTextButton.titleLabel.frame.size.height + 30;
                    make.height.equalTo(buttonH);
                }];
        
                // 強制更新
                [showTextButton layoutIfNeeded];
        
                // 計算當前 cell 的高度
                CGFloat buttonMaxY = CGRectGetMaxY(showTextButton.frame);
                CGFloat iconMaxY = CGRectGetMaxY(showIconView.frame);
                self.message.cellHeight = MAX(buttonMaxY, iconMaxY) + 10;
            }
        
            @end
      • 多 Cell 佈局

            #import "XMGMessageCell.h"
            #import "XMGMessage.h"
        
            #define MAS_SHORTHAND
            #define MAS_SHORTHAND_GLOBALS
            #import "Masonry.h"
        
            @interface XMGMessageCell()
        
            @property (weak, nonatomic) IBOutlet UILabel *timeLabel;
            @property (weak, nonatomic) IBOutlet UIButton *textButton;
            @property (weak, nonatomic) IBOutlet UIImageView *iconView;
        
            @end
        
            @implementation XMGMessageCell
        
            - (void)awakeFromNib {
        
                self.textButton.titleLabel.numberOfLines = 0;
            }
        
            - (void)setMessage:(XMGMessage *)message {
        
                _message = message;
        
                // 時間處理
                if (message.hideTime) { // 隱藏時間
                    self.timeLabel.hidden = YES;
                    [self.timeLabel updateConstraints:^(MASConstraintMaker *make) {
                        make.height.equalTo(0);
                    }];
                } else { // 顯示時間
                    self.timeLabel.text = message.time;
                    self.timeLabel.hidden = NO;
                    [self.timeLabel updateConstraints:^(MASConstraintMaker *make) {
                        make.height.equalTo(21);
                    }];
                }
        
                // 處理顯示的訊息文字
                // 設定按鈕的文字
                [self.textButton setTitle:self.message.text forState:UIControlStateNormal];
        
                // 強制更新
                [self layoutIfNeeded];
        
                // 設定按鈕的高度就是titleLabel的高度
                [self.textButton updateConstraints:^(MASConstraintMaker *make) {
                    CGFloat buttonH = self.textButton.titleLabel.frame.size.height + 30;
                    make.height.equalTo(buttonH);
                }];
        
                // 強制更新
                [self layoutIfNeeded];
        
                // 計算當前cell的高度
                CGFloat buttonMaxY = CGRectGetMaxY(self.textButton.frame);
                CGFloat iconMaxY = CGRectGetMaxY(self.iconView.frame);
                self.message.cellHeight = MAX(buttonMaxY, iconMaxY) + 10;
            }
        
            @end
    • XMGChatingViewController.m

          #import "XMGChatingViewController.h"
          #import "XMGMessage.h"
          #import "XMGMessageCell.h"
      
          @interface XMGChatingViewController () <UITableViewDataSource, UITableViewDelegate>
      
          @property (nonatomic, strong) NSArray *messages;
      
          @end
      
          @implementation XMGChatingViewController
      
          - (NSArray *)messages {
      
              if (_messages == nil) {
      
                  // 載入plist中的字典陣列
                  NSString *path = [[NSBundle mainBundle] pathForResource:@"messages.plist" ofType:nil];
                  NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
      
                  // 字典陣列 -> 模型陣列
                  NSMutableArray *messageArray = [NSMutableArray array];
      
                  // 用來記錄上一條訊息模型
                  XMGMessage *lastMessage = nil;
                  for (NSDictionary *dict in dictArray) {
                      XMGMessage *message = [XMGMessage messageWithDict:dict];
      
                      //埋下伏筆,載入資料時,判斷哪個時間值相等。
                      message.hideTime = [message.time isEqualToString:lastMessage.time];
                      [messageArray addObject:message];
      
                      lastMessage = message;
                  }
      
                  _messages = messageArray;
              }
              return _messages;
          }
      
          #pragma mark - <UITableViewDataSource>
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
              return self.messages.count;
          }
      
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              XMGMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"message"];
      
              cell.message = self.messages[indexPath.row];
      
              return cell;
          }
      
          #pragma mark - <UITableViewDelegate>
          - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
              return 200;
          }
      
          - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              XMGMessage *message = self.messages[indexPath.row];
              return message.cellHeight;
          }
      
          @end
    • 效果

      TableView17

14、tableView 的協議方法

  • 需遵守協議 UITableViewDataSource, UITableViewDelegate,並設定代理

  • UITableViewDelegate 繼承自 UIScrollViewDelegate

        @protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>

14.1 UITableViewDataSource 和 UITableViewDelegate 協議方法

  • Objective-C

    • 分段、行 設定

          // 設定分段數,設定 tableView 有多少個分段
          - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
      
              return myDataArray.count;
          }
      
          // 設定行數,設定 tableView 中每段中有多少行,section 就是第幾分段
          - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
      
              return [[myDataArray objectAtIndex:section] count];
          }
      
          // 設定行高 ,預設為 44
          - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              return 60;
          }
      
          // 設定估計行高   
          - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              /*
                  只要返回了估計高度,那麼就會先呼叫 tableView:cellForRowAtIndexPath: 方法建立 cell,
                  再呼叫 tableView:heightForRowAtIndexPath: 方法獲取 cell 的真實高度,
                  並且顯示一個 cell,呼叫一次 tableView:heightForRowAtIndexPath: 方法。
      
                  如果不返回估計高度,會先呼叫 tableView:heightForRowAtIndexPath: 方法,
                  再呼叫 tableView:heightForRowAtIndexPath: 方法,
                  並且一次性全部呼叫總 cell 數量次 tableView:heightForRowAtIndexPath: 方法。
              */
      
              return 60;
          }
      
          // 設定每一行顯示的內容,每當有一個 cell 進入視野範圍內就會呼叫
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              return cell;
          }
    • 分段的頭、腳標題 設定

          // 設定分段的頭標題高度
          - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
      
              return 40;
          }
      
          // 設定分段的腳標題高度
          - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
      
              return 30;
          }
      
          // 設定分段的頭標題估計高度
          - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section {
      
              return 40;
          }
      
          // 設定分段的腳標題估計高度
          - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForFooterInSection:(NSInteger)section {
      
              return 30;
          }
      
          // 設定分段的頭標題內容
          - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
      
              if (0 == section) {
                  return @"1 Header";
              }
              else{
                  return @"2 rHeader";
              }
          }
      
          // 設定分段的腳標題內容
          - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
      
              if (0 == section) {
                  return @"2 Footer";
              }
              else{
                  return @"2 Footer";
              }
          }
      
          // 設定分段頭標題檢視,返回自定義的標題檢視
          - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
      
              return myView;
          }
      
          // 設定分段腳標題檢視,返回自定義的標題檢視
          - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
      
              return myView;
          }
    • 分段索引條 設定

          // 建立索引條
          - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
      
              return array;
          }
      
          // 設定索引條偏移量,預設索引條與分段一一對應時,可以不寫該方法
          - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
              /*
                   點選索引條上字串的時候 tableView 會跳轉到對應的分段,是根據位置計算的,點選索引條上第幾個,tableView 就會跳到第幾段。
      
                   如果索引條的前面加了個搜尋小圖示等,需要重寫這個方法。
               */
          }
    • 表格點選 設定

          // 表格選中點選響應事件,表格被選中
          - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
      
          }
      
          // 表格取消選中點選響應事件,表格被取消選中
          - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
      
          }
      
          // 附屬控制元件 button 點選響應事件
          - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
      
              // 如果系統自帶的附屬控制元件裡有 button ,附屬控制元件的點選事件會獨立出來
          }
    • 表格編輯 設定

          // 表格刪除、插入
          - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 表格刪除或插入,預設為刪除模式,寫入該方法即表示允許刪除。
          }
      
          // 設定編輯模式
          - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 刪除、插入、多選刪除,不設定預設時為刪除
          }
      
          // 修改左滑刪除按鈕的內容
          - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              return @"刪除";
          }
      
          // 設定左滑多按鈕
          - (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
      
              // 按鈕從右向左的順序排列
              return @[action1, action0];
          }
      
          // 表格移動
          - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
      
          }
  • Swift

    • 分段、行 設定

          // 設定分段數,設定 tableView 有多少個分段
          func numberOfSectionsInTableView(tableView: UITableView) -> Int {
      
              return myDataArray.count
          }
      
          // 設定行數,設定 tableView 中每段中有多少行,section 就是第幾分段
          func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      
              return myDataArray[section].count
          }
      
          // 設定行高,預設為 44
          func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
      
              return 60
          }
      
          // 設定估計行高   
          func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
      
              /*
                  只要返回了估計高度,那麼就會先呼叫 tableView:cellForRowAtIndexPath: 方法建立 cell,
                  再呼叫 tableView:heightForRowAtIndexPath: 方法獲取 cell 的真實高度,
                  並且顯示一個 cell,呼叫一次 tableView:heightForRowAtIndexPath: 方法。
      
                  如果不返回估計高度,會先呼叫 tableView:heightForRowAtIndexPath: 方法,
                  再呼叫 tableView:heightForRowAtIndexPath: 方法,
                  並且一次性全部呼叫總 cell 數量次 tableView:heightForRowAtIndexPath: 方法。
              */
      
              return 60
          }
      
          // 設定每一行顯示的內容,每當有一個 cell 進入視野範圍內就會呼叫
          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      
              return cell
          }
    • 分段的頭、腳標題 設定

          // 設定分段的頭標題高度
          func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
      
              return 40
          }
      
          // 設定分段的腳標題高度
          func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
      
              return 30
          }
      
          // 設定分段的頭標題估計高度
          func tableView(tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
      
              return 40
          }
      
          // 設定分段的腳標題估計高度
          func tableView(tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat {
      
              return 30
          }
      
          // 設定分段的頭標題內容
          func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
      
              if 0 == section {
                  return "1 Header"
              }
              else {
                  return "2 Header"
              }
          }
      
          // 設定分段的腳標題內容
          func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
      
              if 0 == section {
                  return "1 Footer"
              }
              else {
                  return "2 Footer"
              }
          }
      
          // 設定分段頭標題檢視,返回自定義的標題檢視
          func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
      
              return myView
          }
      
          // 設定分段腳標題檢視,返回自定義的標題檢視
          func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
      
              return myView
          }
    • 分段索引條 設定

          // 建立索引條
          func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
      
              return array
          }
      
          // 設定索引條偏移量,預設索引條與分段一一對應時,可以不寫該方法
          func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
      
              /*
                  點選索引條上字串的時候 tableView 會跳轉到對應的分段,是根據位置計算的,點選索引條上第幾個,tableView 就會跳到第幾段。
      
                  如果索引條的前面加了個搜尋小圖示等,需要重寫這個方法。
              */
          }
    • 表格點選 設定

          // 表格選中點選響應事件,表格被選中
          func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
      
          }
      
          // 表格取消選中點選響應事件,表格被取消選中
          func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
      
          }
      
          // 附屬控制元件 button 點選響應事件
          func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath) {
      
              // 如果系統自帶的附屬控制元件裡有 button ,附屬控制元件的點選事件會獨立出來
          }
    • 表格編輯 設定

          // 表格刪除、插入
          func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
      
              // 表格刪除或插入,預設為刪除模式,寫入該方法即表示允許刪除。
          }
      
          // 設定編輯模式
          func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
      
              // 刪除、插入、多選刪除,不設定預設時為刪除
          }
      
          // 修改左滑刪除按鈕的內容
          func tableView(tableView: UITableView, titleForDeleteConfirmationButtonForRowAtIndexPath indexPath: NSIndexPath) -> String? {
      
              return "刪除"
          }
      
          // 設定左滑多按鈕
          func tableView(tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
      
              // 按鈕從右向左的順序排列
              return [action1, action0]
          }
      
          // 表格移動
          func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
      
          }

14.2 UIScrollViewDelegate 協議方法

  • Objective-C

    • 拖拽

          // 將要開始拖拽
          - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
      
          }
      
          // 將要結束拖拽
          - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
      
          }
      
          // 已經結束拖拽,decelerate 鬆手後 是否有慣性滾動 0:沒有,1:有
          - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
      
          }
    • 滾動

          // 滾動過程中,只要滾動就會觸發
          - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 
      
          }
      
          // 已經結束滾動,滾動動畫停止時執行,程式碼改變時觸發,也就是 setContentOffset 改變時
          - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
      
          }
    • 慣性滾動

          // 將要開始慣性滾動
          - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
      
          }
      
          // 已經結束慣性滾動
          - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
      
          }
    • 滾到頂端

          // 設定點選狀態列時是否滾到頂端
          - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {
      
              return YES;
          }
      
          // 已經滾到頂端,點選狀態列時呼叫
          - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
      
          }
    • 縮放

          // 設定被縮放的空間,一個 scrollView 中只能有一個子控制元件被縮放,如果有很多個子控制元件縮放時會引起錯亂
          - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
      
              return [scrollView.subviews[0] viewWithTag:100];
          }
      
          // 將要開始縮放
          - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
      
          }
      
          // 已經結束縮放
          - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
      
          }
      
          // 縮放過程中,只要縮放就會觸發
          - (void)scrollViewDidZoom:(UIScrollView *)scrollView {
      
          }
  • Swift

    • 拖拽

          // 將要開始拖拽
          func scrollViewWillBeginDragging(scrollView: UIScrollView) {
      
          }
      
          // 將要結束拖拽
          func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
      
          }
      
          // 已經結束拖拽,decelerate 鬆手後 是否有慣性滾動 0:沒有,1:有
          func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
      
          }
    • 滾動

          // 滾動過程中,只要滾動就會觸發,只要滾動就會觸發
          func scrollViewDidScroll(scrollView: UIScrollView) {
      
          }
      
          // 已經結束滾動,滾動動畫停止時執行,程式碼改變時觸發,也就是 setContentOffset 改變時
          func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) {
      
          }
    • 慣性滾動

          // 將要開始慣性滾動
          func scrollViewWillBeginDecelerating(scrollView: UIScrollView) {
      
          }
      
          // 已經結束慣性滾動
          func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
      
          }
    • 滾到頂端

          // 設定點選狀態列時是否滾到頂端
          func scrollViewShouldScrollToTop(scrollView: UIScrollView) -> Bool {
      
              return true
          }
      
          // 已經滾到頂端,點選狀態列時呼叫
          func scrollViewDidScrollToTop(scrollView: UIScrollView) {
      
          }
    • 縮放

          // 設定被縮放的空間,一個 scrollView 中只能有一個子控制元件被縮放,如果有很多個子控制元件縮放時會引起錯亂
          func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
      
              return scrollView.subviews[0].viewWithTag(100)
          }
      
          // 將要開始縮放
          func scrollViewWillBeginZooming(scrollView: UIScrollView, withView view: UIView?) {
      
          }
      
          // 已經結束縮放
          func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat) {
      
          }
      
          // 縮放過程中,只要縮放就會觸發
          func scrollViewDidZoom(scrollView: UIScrollView) {
      
          }

15、Storyboard 中設定

  • 在 Storyboard 場景中設定

    • Table View Controller

      TableView1

      Selection 選項
      -- Clear on Appearance
      Refreshing UIRefreshControl 重新整理設定
    • Table View

      TableView2

      Content 設定表格 Cell 型別
      -- Dynamic Prototypes 動態 Cell,可以設定自定義 Cell
      -- Static Cells 靜態 Cell,cell 的數量和內容無需(或大部分不需要)做動態的變化
      Sections 設為靜態 Cell 時,設定分段數
      Prototype Cells 設為動態 Cell 時,設定不同型別表格的數量,需設定不同的 Identifier
      Style 設定表格型別
      Plain 懸浮顯示
      Grouped 跟隨移動,多餘的表格將自動清除
      Separator 設定分割線型別/顏色
      Separator Insets 設定分割線邊距
      Selection 設定表格選擇型別,不允許選擇/單選/多選
      Editing 設定編輯狀態時表格的選擇型別
      Show Selection on Touch 顯示選擇
      Index Row Limit
      Text
      Background
    • Table View Cell

      TableView3

      Style 設定 Cell 的型別
      Image 設定 Cell 顯示的圖片
      Identifier 設定 Cell 的複用 ID
      Selection 設定 Cell 點選時的顏色
      Accessory 設定 Cell 附屬控制元件型別
      Editing Acc.
      Indentation
      Separator 設定分割線邊距
    • 在 Storyboard 場景繫結的 Controller 中設定

          @interface TableViewController : UITableViewController
      
          self.tableView.backgroundColor = [UIColor greenColor];

相關文章