Parece que no puedo hacer que mi clase UITableView personalizada funcione correctamente como inputView para un UITextField que existe dentro de otro UITableView.

Consulte la captura de pantalla a continuación. Tengo MainTableViewController que contiene dos secciones: Sección-1 y Sección-2. Cada sección contiene solo una celda. Cada celda tiene un UITextField. La sección 1 es el área de preocupación. Contiene un UITextField con un UITableView personalizado como inputView. Llamé a este UITableView InputTableView personalizado e implementé el protocolo UITableViewDataSource.

class InputTableView: UITableView, UITableViewDataSource

El objetivo es hacer que InputTableView actúe como un selector de opciones. El usuario haría clic en una celda dentro de InputTableView y esa selección completará specialTextField.

Cuando hago clic en la celda, el InputView aparece como se esperaba. Test Title es el título de la sección UITableView. Section 0 Row 0 es el contenido de la celda.

UITableViewController Image

Este es el numberOfRowsInSection en la clase InputTableView:

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

Si devuelvo algo mayor que 1, la aplicación se bloqueará con un error como este:

TableViewControllerTest [721: 126633] * Finalizando la aplicación debido a excepción no detectada 'NSRangeException', razón: '* - [__ NSSingleObjectArrayI objectAtIndex:]: índice 1 más allá de los límites [0 .. 0] ' *** Pila de llamadas de primer lanzamiento: (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 0x193f921e0 0x193f9149c 0x193a0630c 0x1939d6da0 0x1a1cb21e8 0x1941c075c 0x1941ba130 0x18daceb5c 0x18dace4a4 0x18dacc0a4 0x18d9fa2b8 0x18f4ae198 0x193a417fc 0x193a3c534 0x1000191f4 0x18c9dd5b8) libc ++ abi.dylib: terminando con una excepción no capturada del tipo NSException (lldb)

1) Si agrego otra fila a MainTableViewController (agrego 1 fila a la Sección-1 para un total de 2 filas), el numberOfRowsInSection puede devolver 2 y ejecutarse bien. Si agrego otra fila para un total de 3 filas, entonces la función puede devolver 3 y ejecutarse bien. Algo mayor y se bloquea de nuevo. Estoy pensando que estos están de alguna manera relacionados. Sin embargo, ¿cómo puede ser eso si los dos elementos ni siquiera están en la misma clase?

2) Si comento la línea inputTableView.delegate = self, el código se ejecuta nuevamente sin fallar. Puedo configurar numberOfRowsInSection para devolver cualquier número (probé 5 y funciona). Sin embargo, debido a que el delegado ahora no está configurado, no puedo hacer nada con la interacción del usuario.

Mi código completo:

//
//  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)
    }
}

Mi código también está aquí en Github: https://github.com/starkindustries/TableViewControllerTest

Otras fuentes que he investigado: UITableView se bloquea cuando el número de filas> 1

Editar (Notas resueltas):

La respuesta de Muescha me ayudó a resolver mi problema. Como referencia, la versión fija del código se puede encontrar en este enlace de github a continuación (rama delegateRefactor). El código original con el problema está en el enlace de github anterior (rama maestra).

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

1
Zion Perez 29 ene. 2017 a las 09:35

3 respuestas

La mejor respuesta

Debes poner también un UITableViewDelegate en tu InputTableView.

¿por qué? ya que lo tiene antes en ViewController (debe nombrarlo TableViewController o MainTableViewController para que alguien más tenga una pista que no sea un controlador de vista normal).

Pero ha dibujado en el Creador de interfaces con 2 secciones y con 1 celda por sección. estas celdas se rellenan desde UITableViewDataSource oculto y UITableViewDelegate oculto. si hago clic en la vista de tabla y veo las conexiones salientes en el generador de interfaces pero no hay ningún archivo de clase allí.

Creo que cuando tienes el UITableViewDelegate de InputTableView está configurado en MainTableViewController, entonces fallan en otras solicitudes. en el crash stacktrace también hubo una llamada heightForRowAt una de las últimas llamadas (es por eso: que más errores y stacktrace publicas a tu pregunta: es mejor encontrar el error - muy bueno que publiques tu proyecto de ejemplo a github. de lo contrario, la configuración de las celdas en el guión gráfico principal estaría oculta para los revisores)

Pero solo tiene una celda en la Tabla principal en la sección uno. Es por eso que se estrelló con 2 celdas. intenta obtener la altura de la celda 2 pero solo hay un índice 0 ... 0 que significa = 1 celda.

El punto difícil fue que UITableViewDataSource y UITableViewDelegate de MainTableViewController están ocultos en algún lugar y con conexión automática de alguna manera. (es por eso que personalmente no me gusta el creador de interfaces y el guión gráfico y hago todo por código)

Aquí la prueba Captura de pantalla:

enter image description here

Cambios de código:

  • self.delegate = self movido a InputTableView

  • protocolo UITableViewDelegate agregado a InputTableView

Aquí está el código:

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)
    }
}
1
muescha 29 ene. 2017 a las 12:28

Quizás el problema se deba a la subclase UITableViewController. Requiere solo una vista de tabla. Simplemente asigne el delegado de la vista de entrada a sí mismo y luego use un delegado personalizado para enviar el índice seleccionado o la información necesaria al controlador de la vista de tabla.

0
Nick Marinov 29 ene. 2017 a las 06:51

Creo que debería tratar con esos métodos dataSource para InputTableView directamente en su 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
Ocunidee 29 ene. 2017 a las 07:49