2017-01-29 88 views
1

我似乎無法讓我的自定義UITableView類正常工作,作爲另一個UITableView中存在的UITextField的inputView。當numberOfRowsInSection大於1時,UITableView崩潰

請參考下面的截圖。我有MainTableViewController,它包含兩個部分:Section-1和Section-2。每個部分只包含一個單元格。每個單元格都有一個UITextField。第一部分是值得關注的領域。它包含一個帶有自定義UITableView作爲其inputView的UITextField。我給這個自定義的UITableView InputTableView命名並實現了UITableViewDataSource協議。

class InputTableView: UITableView, UITableViewDataSource 

目標是讓InputTableView像選項選取器一樣工作。用戶將在InputTableView中單擊一個單元格,該選擇將填充specialTextField

當我單擊單元格時,InputView將按預期方式顯示。 Test Title是UITableView標題。 Section 0 Row 0是單元格內容。

UITableViewController Image

這是InputTableView類numberOfRowsInSection

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    print("tableView numberOfRowsInSection") 
    return 1 
} 

如果我返回任何大於1,應用程序將因錯誤而崩潰,這樣的:

TableViewControllerTest [721:126633] *由於 未捕獲異常'NSRangeException'而終止應用程序,原因:'* - [__ NSSingleObjectArrayI objectAtIndex:]:索引1超出範圍[0 .. 0]」 ***第一擲調用堆棧:(0x18db211b8 0x18c55855c 0x18db12420 0x19417c7bc 0x193ee7a40 0x193d1f9d4 0x193af5c20 0x193ab5478 0x193ab48c8 0x193ab4654 0x193ab4488 0x193d12e74 0x193d125a4 0x193d124a0 0x193a54550 0x193d35120 0x193d34aac 0x193a5399c 0x193a52fcc 0x1939d3090 0x1939f6c58 0x1939d22c4 0x18e57ad10 0x1939d2138 0x1939de018 0x1939dd904 0x1943b2298 0x1943b37f8 0x1943ac3c8 0x1943b37c8 0x1943a8488 0x1943b331c 0x1943abe38 0x193a9d240 0x1a1d15e98 0x1939fca78 0x193a5ab4c 0x193a5aebc 0x193add0b4 0x193b84128 0x193b83630 0x193f9ef80 0x193fa2688 0x193b6973c 0x193a080f0 0x193f92680 0x193 f921e0 0x193f9149c 0x193a0630c 0x1939d6da0 0x1a1cb21e8 0x1941c075c 0x1941ba130 0x18daceb5c 0x18dace4a4 0x18dacc0a4 0x18d9fa2b8 0x18f4ae198 0x193a417fc 0x193a3c534 0x1000191f4 0x18c9dd5b8)的libC++ abi.dylib: 型NSException(LLDB的未捕獲的異常)

1)如果我添加另一端接行到MainTableViewController(將1行添加到Section-1總共2行),那麼numberOfRowsInSection可以返回2並運行得很好。如果我爲總共3行添加另一行,那麼該函數可以返回3並運行得很好。更大,它再次崩潰。我在想這些有些相關。但是,如果這兩個項目甚至不在同一個班上,那又如何呢?

2)如果我註釋掉行inputTableView.delegate = self那麼代碼再次運行而不會崩潰。我可以設置numberOfRowsInSection返回任何數字(我試過5,它的工作原理)。但是,因爲代理現在沒有設置,所以我無法對用戶交互進行任何操作。

我全碼:

// 
// MainTableViewController.swift 
// TableViewControllerTest 
// 
// Created by Zion Perez on 1/29/17. 
// Copyright © 2017 Zion Perez. All rights reserved. 
// 

import UIKit 

class InputTableView: UITableView, UITableViewDataSource { 

    func setupTableView(){ 
     self.dataSource = self 
    } 

    // MARK: - TableView DataSource 
    // https://developer.apple.com/reference/uikit/uitableviewdatasource 
    func numberOfSections(in tableView: UITableView) -> Int { 
     print("numberOfSections") 
     return 1 
    } 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     print("tableView numberOfRowsInSection") 
     return 1 
    } 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     print("tableView cellForRowAt: " + indexPath.description) 

     let id = "BasicCell" 
     var cell: UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: id) 
     if cell == nil { 
      cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: id) 
     } 
     cell?.textLabel?.text = "Section \(indexPath.section) Row \(indexPath.row)" 
     return cell! 
    } 

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 
     print("tableView titleForHeaderInSection") 
     return "Test Title" 
    } 
} 

class MainTableViewController: UITableViewController { 

    @IBOutlet weak var specialTextField: UITextField! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Do any additional setup after loading the view, typically from a nib. 
     let frame = CGRect(x: self.view.frame.minX, y: self.view.frame.minY, width: self.view.frame.width, height: 200.0) 
     let inputTableView = InputTableView(frame: frame) 
     inputTableView.setupTableView() 
     inputTableView.delegate = self 
     specialTextField.inputView = inputTableView 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 

    // MARK: - TableViewDelegate 
    // https://developer.apple.com/reference/uikit/uitableviewdelegate 
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
     print("selected row at " + indexPath.description) 
    } 

    override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { 
     print("deselected row at " + indexPath.description) 
    } 
} 

我的代碼也在這裏Github上:我已經研究 https://github.com/starkindustries/TableViewControllerTest

其它來源: UITableView crashes when number of rows >1

編輯(註解決):

Muescha的回答他幫我解決了我的問題。作爲參考,代碼的固定版本可以在下面的這個github鏈接中找到(delegateRefactor分支)。有問題的原始代碼位於上面的github鏈接(主分支)。

https://github.com/starkindustries/TableViewControllerTest/tree/delegateRefactor

+0

'specialTextField:的UITextField !; specialTextField.inputView = inputTableView'?你想做什麼?你的目標? – muescha

+0

你有這個崩潰的對象的完整的錯誤信息? – muescha

+0

目標是使InputTableView像選項選取器一樣工作。用戶將在InputTableView中單擊一個單元格,該選擇將填充specialTextField。完整的錯誤只是有很多十六進制字符來顯示堆棧。 –

回答

1

,你也需要把一個UITableViewDelegateInputTableView

爲什麼?因爲你之前在ViewController(你應該把它命名爲TableViewControllerMainTableViewController,以便其他人有一個不是普通視圖控制器的提示)。

但您已在界面生成器中繪製了2節和1節每節。這個單元格是從隱藏的UITableViewDataSource和隱藏的UITableViewDelegate填充的。如果我在表格視圖中單擊並在界面構建器中看到傳出連接,但沒有類文件。

我認爲當你的UITableViewDelegateInputTableView設置爲MainTableViewController,那麼它們會崩潰在其他請求上。在崩潰堆棧跟蹤中,還有一個heightForRowAt調用最後一個調用之一(這就是爲什麼:比你發佈到你的問題更多的錯誤和堆棧跟蹤:最好找出錯誤 - 非常好,你將你的示例項目發佈到github。主要故事板中的單元格設置將隱藏給審閱者)

但是在第一部分的主表中只有一個單元格。這就是爲什麼它與2個細胞墜毀。它嘗試獲取單元格2的高度,但只有0 ... 0索引表示= 1單元格。

難點是,UITableViewDataSourceUITableViewDelegateMainTableViewController隱藏在某處並自動裝配一些如何。(這就是爲什麼我個人不喜歡接口建設者和故事板和代碼做)

這裏的證據截圖:

enter image description here

代碼更改:

  • self.delegate = self移到InputTableView

  • 協議UITableViewDelegate已被添加到InputTableView

這裏是代碼:

import UIKit 

class InputTableView: UITableView, UITableViewDataSource, UITableViewDelegate { 

    func setupTableView(){ 
     self.dataSource = self 
     self.delegate = self 
    } 

    // MARK: - TableView DataSource 
    // https://developer.apple.com/reference/uikit/uitableviewdatasource 
    func numberOfSections(in tableView: UITableView) -> Int { 
     print("numberOfSections") 
     return 1 
    } 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     print("tableView numberOfRowsInSection") 
     return 8 
    } 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     print("tableView cellForRowAt: " + indexPath.description) 

     let id = "BasicCell" 
     var cell: UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: id) 
     if cell == nil { 
      cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: id) 
     } 
     cell?.textLabel?.text = "Section \(indexPath.section) Row \(indexPath.row)" 
     return cell! 
    } 

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 
     print("tableView titleForHeaderInSection") 
     return "Test Title" 
    } 
} 

class ViewController: UITableViewController { 

    @IBOutlet weak var specialTextField: UITextField! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Do any additional setup after loading the view, typically from a nib. 
     let frame = CGRect(x: self.view.frame.minX, y: self.view.frame.minY, width: self.view.frame.width, height: 200.0) 
     let inputTableView = InputTableView(frame: frame) 
     inputTableView.setupTableView() 
     specialTextField.inputView = inputTableView 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 

    // MARK: - TableViewDelegate 
    // https://developer.apple.com/reference/uikit/uitableviewdelegate 
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
     print("selected row at " + indexPath.description) 
    } 

    override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { 
     print("deselected row at " + indexPath.description) 
    } 
} 
+0

謝謝。我會立即嘗試你的建議。但是,我希望委託在MainTableViewController中,因爲這是我最後需要數據的地方。在InputTableView中選擇的選項需要填充specialTextField。希望這是有道理的。讓我知道是否需要澄清任何事情。 –

+0

我試過你的建議。我把委託方法放在InputTableView中。正如所料,委託方法確實被調用。另外,我可以改變行數,一切都很好。現在我需要從InputTableView的didSelectRow的結果到MainTableViewController對象。這就是我想要在MainTableVC中實現委託的原因。這也不能解釋爲什麼程序在MainTableVC中委託時崩潰。我明白,故事板數據源/代理以某種方式隱藏/自動裝配。但究竟是什麼導致了這個問題? –

+0

調用以獲取單元格的高度。但是因爲它們是隱藏的,所以它們從主表首先調用並且只有一行 – muescha

0

也許問題是因爲的UITableViewController子類的.. 它只需要一個表視圖。只需將輸入視圖的委託分配給它自己,然後使用自定義委託將所選索引或所需信息發送回表視圖控制器。

+1

爲什麼UITableViewController只需要一個tableView?委託方法有tableView:作爲參數,所以如果你有多個tableView,你可以檢查它是哪一個,然後採取適當的行動。 –

0

我想你應該直接在ViewController中處理InputTableView的那些dataSource方法。

import UIKit 

class InputTableView: UITableView, UITableViewDataSource { 

    func setupTableView(){ 
     self.dataSource = self 
    } 

    // MARK: - TableView DataSource 
    // https://developer.apple.com/reference/uikit/uitableviewdatasource 
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     print("tableView cellForRowAt: " + indexPath.description) 

     let id = "BasicCell" 
     var cell: UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: id) 
     if cell == nil { 
      cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: id) 
     } 
     cell?.textLabel?.text = "Section \(indexPath.section) Row \(indexPath.row)" 
     return cell! 
    } 

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 
     print("tableView titleForHeaderInSection") 
     return "Test Title" 
    } 
} 

class ViewController: UITableViewController { 

    @IBOutlet weak var specialTextField: UITextField! 
    var inputTableView: InputTableView! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Do any additional setup after loading the view, typically from a nib. 
     let frame = CGRect(x: self.view.frame.minX, y: self.view.frame.minY, width: self.view.frame.width, height: 200.0) 
     inputTableView = InputTableView(frame: frame) 
     inputTableView.setupTableView() 
     inputTableView.delegate = self 
     specialTextField.inputView = inputTableView 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 

    // MARK: - TableViewDelegate 
    // https://developer.apple.com/reference/uikit/uitableviewdelegate 
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
     print("selected row at " + indexPath.description) 
    } 

    override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { 
     print("deselected row at " + indexPath.description) 
    } 

    func numberOfSections(in tableView: UITableView) -> Int { 
     if tableView == inputTableView { 
      print("numberOfSections") 
      return 1 
     } else { 
      deal with your other tableView 
     } 
    } 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     if tableView == inputTableView { 
      print("numberOfRowsInSection ") 
      return 1 
     } else { 
      deal with your other tableView 
     } 
    } 
} 
+0

我知道把數據源放在TableViewController裏面可以。但是,「處理你的其他tableView」將如何? 「其他tableView」已經構建在Storyboard(Section-1和Section-2)中,因此將數據源添加到ViewController中僅增加了代碼的複雜性。 –

+0

您會發現隱藏的故事板數據源和用戶代理是導致崩潰的原因,因爲它隱藏在某處。當從數據集創建動態表單時,您可以更好地控制ist。代碼比隱藏信息的許多側面菜單更容易閱讀 – muescha