2017-01-29 1 views
1

Кажется, что мой пользовательский класс UITableView не работает корректно как inputView для UITextField, который существует в другом UITableView.UITableView Crashes Когда numberOfRowsInSection больше, чем 1

См. Снимок экрана ниже. У меня есть MainTableViewController, который содержит два раздела: Раздел-1 и Раздел-2. Каждый раздел содержит только одну ячейку. Каждая ячейка имеет UITextField. Раздел 1 является предметом озабоченности. Он содержит UITextField с пользовательским UITableView в качестве входного вида. Я назвал этот пользовательский 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 строку в раздел-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

Edit (Раскрыты Notes):

Ответ Muescha он Я решил решить свою проблему. Для справки, фиксированная версия кода может быть найдена по этой ссылке github ниже (branchateRefactor branch). Исходный код с проблемой находится по ссылке github выше (главная ветка).

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

+0

'specialTextField: UITextField !; specialTextField.inputView = inputTableView'? что вы пытаетесь сделать? ваша цель? – muescha

+0

у вас есть полное сообщение об ошибке с объектом, на котором это происходит? – muescha

+0

Цель состоит в том, чтобы заставить InputTableView действовать как сборщик опций. Пользователь будет щелкать ячейку в InputTableView, и этот выбор будет заполнять specialTextField. Полная ошибка имела много шестнадцатеричных символов, чтобы показать стек. –

ответ

1

вам нужно положить также UITableViewDelegate ваши InputTableView.

Почему? поскольку у вас есть это раньше в ViewController (ваш должен называть его TableViewController или MainTableViewController, так что у кого-то есть подсказка, которая не является обычным контроллером).

, но вы нарисовали в интерфейсе Builder с 2 разделами и с 1 ячейкой на секцию. эти ячейки заселены из скрытого UITableViewDataSource и скрытого UITableViewDelegate. если я щелкнул в представлении таблицы и просмотрю исходящие соединения в построителе интерфейса, но там нет файла класса.

Я думаю, что когда у вас есть UITableViewDelegate из InputTableView, установлено значение MainTableViewController, тогда они разбиваются по другим запросам. в crash stacktrace был также heightForRowAt, вызывающий один из последних вызовов (вот почему: чем больше ошибок и stacktrace вы отправляете на свой вопрос: лучше найти ошибку - очень хорошо, что вы отправляете свой примерный проект в github. настройки ячеек в основной раскадровке будут скрыты для рецензентов)

но у вас есть только одна ячейка на главной таблице в первом разделе. вот почему он разбился с 2 клетками. он пытается получить высоту для ячейки 2, но есть только индекс 0 ... 0, что означает = 1 ячейку.

трудный момент был, что UITableViewDataSource и UITableViewDelegateMainTableViewController спрятаны где-то и как-то автоповочили.(Вот почему я лично не нравится интерфейс строителя и раскадровку и делать все по коду)

здесь доказательство Скриншот:

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, должна заполнять специальныйTextField. Надеюсь, это имеет смысл. Дайте мне знать, если мне нужно что-то разъяснить. –

+0

Я попробовал ваше предложение. Я помещал методы делегата в InputTableView. Как и ожидалось, методы делегата вызываются. Кроме того, я могу изменить количество строк, и все в порядке. Теперь мне нужно получить результат didSelectRow из InputTableView в объект MainTableViewController. Именно по этой причине я хотел реализовать делегата внутри MainTableVC. Это также не совсем ответит, почему программа вылетает, когда делегат находится в MainTableVC. Я понимаю, что источник/делегат раскадровки скрыты/автоусилили каким-то образом. Но что именно вызывает эту проблему? –

+0

Звонок для получения высоты для ячейки. Но так как они скрыты, они вызываются сначала из основного стола и имеют только одну строку. – muescha

0

Может быть, проблема в том, что из подкласса UITableViewController .. Это требует только один вид таблицы. Просто присвойте делегату входного представления его себе, а затем используйте пользовательский делегат для отправки выбранного индекса или необходимой информации обратно в контроллер представления таблиц.

+1

Почему UITableViewController требует только одного tableView? Методы делегата имеют tableView: как параметр, так что если у вас есть несколько табличных элементов, вы можете проверить, какой из них он есть, а затем предпринять соответствующие действия. –

0

Я думаю, что вы должны иметь дело с этими методами dataSource для InputTableView непосредственно в вашем ViewController.

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» уже построен внутри раскадровки (раздел-1 и раздел-2), поэтому добавление источника данных в ViewController добавляет дополнительную сложность в код. –

+0

вы видите, что скрытый источник данных раскадровки и делегат ui были причиной краха, поскольку он где-то скрыт. когда вы создаете динамику формы из набора данных, у вас больше контроля над ist. код более легко читается, чем много боковых меню со скрытой информацией. – muescha