Quiero subclase UITableViewController y ser capaz de crear instancias de ella, llamando a un inicializador predeterminado sin argumentos.

class TestViewController: UITableViewController {
    convenience init() {
        self.init(style: UITableViewStyle.Plain)
    }
}

Como de la Xcode 6 Beta 5, en el ejemplo anterior ya no funciona.

Overriding declaration requires an 'override' keyword
Invalid redeclaration of 'init()'
  • Este error se corrigió en iOS 9.
InformationsquelleAutor Nick Snyder | 2014-08-05

9 Comentarios

  1. 61

    NOTA Este error se corrigió en iOS 9, así que todo el asunto será irrelevante en ese momento. La discusión siguiente se aplica sólo para el sistema particular y la versión de Swift a la que está explícitamente dirigida.


    Esto es claramente un error, pero también hay una muy fácil solución. Voy a explicar el problema y, a continuación, dar la solución. Por favor, tenga en cuenta que estoy escribiendo esto para Xcode 6.3.2 y Swift 1.2; Apple ha sido todo el mapa en esto desde el día Swift salió por primera vez, para otras versiones se comportan de forma diferente.

    El fundamento del Ser

    Va a crear instancias de UITableViewController por mano (es decir, llamando a su inicializador en el código). Y desea subclase UITableViewController porque tiene las propiedades de la instancia que desea darle.

    El Problema

    Así, se inicia con una propiedad de instancia:

    class MyTableViewController: UITableViewController {
        let greeting : String
    }

    Esto no tiene ningún valor predeterminado, así que tienes que escribir un inicializador:

    class MyTableViewController: UITableViewController {
        let greeting : String
        init(greeting:String) {
            self.greeting = greeting
        }
    }

    Pero eso no es legal inicializador – tienes que llamar a super. Digamos que su llamada a super es llamar init(style:).

    class MyTableViewController: UITableViewController {
        let greeting : String
        init(greeting:String) {
            self.greeting = greeting
            super.init(style: .Plain)
        }
    }

    Pero aún no se puede compilar, porque usted tiene un requisito para implementar init(coder:). Lo que no es:

    class MyTableViewController: UITableViewController {
        let greeting : String
        required init(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        init(greeting:String) {
            self.greeting = greeting
            super.init(style: .Plain)
        }
    }

    El código compila ahora! Ahora felizmente (piensa) crear instancias de esta tabla de controlador de vista de la subclase llamando al inicializador escribió:

    let tvc = MyTableViewController(greeting:"Hello there")

    Todo se ve alegre y de color de rosa hasta que ejecutar la aplicación, en la que el punto de choque con este mensaje:

    fatal error: el uso de no implementadas inicializador init(nibName:bundle:)

    Lo que Ocasiona el Bloqueo y por Qué no Se Resuelve

    El accidente es causado por un error en el Cacao. Desconocido para usted, init(style:) llama init(nibName:bundle:). Y se lo llama en self. Que eres tú – MyTableViewController. Pero MyTableViewController no tiene ninguna implementación de init(nibName:bundle:). Y no heredar init(nibName:bundle:), ya sea, porque ya se prevé un inicializador designado, así cortar la herencia.

    Su única solución sería implementar init(nibName:bundle:). Pero no se puede, debido a que la implementación se requieren para establecer la propiedad de instancia greeting – y usted no sabe qué conjunto.

    La Solución Simple

    La solución simple – casi demasiado simple, que es por qué es tan difícil pensar en – es: no subclase UITableViewController. Por qué es esta una solución razonable? Porque en realidad nunca necesario subclase en el primer lugar. UITableViewController es un gran sentido de la clase; de no hacer nada que usted no pueda hacer por sí mismo.

    Por lo tanto, ahora vamos a reescribir nuestra clase como un UIViewController subclase lugar. Todavía tenemos una vista de tabla como nuestro punto de vista, así que vamos a crear en loadView, y vamos a conectarlo allí. Los cambios están marcados como protagonizó comentarios:

    class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { //*
        let greeting : String
        weak var tableView : UITableView! //*
        init(greeting:String) {
            self.greeting = greeting
            super.init(nibName:nil, bundle:nil) //*
        }
        required init(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        override func loadView() { //*
            self.view = UITableView(frame: CGRectZero, style: .Plain)
            self.tableView = self.view as! UITableView
            self.tableView.delegate = self
            self.tableView.dataSource = self
        }
    }

    También te queremos, por supuesto, agregar el mínimo de datos requeridos fuente de métodos. Ahora podemos crear una instancia de nuestra clase como esta:

    let tvc = MyViewController(greeting:"Hello there")

    Nuestro proyecto se compila y se ejecuta sin problemas. Problema resuelto!

    Una Objeción – No

    Se podría objetar que, al no utilizar UITableViewController hemos perdido la capacidad de obtener un prototipo de celda a partir del storyboard. Pero eso no es una objeción, porque nos nunca había que la capacidad en el primer lugar. Recuerde, nuestra hipótesis es que estamos subclases y llamando a nuestra propia subclase del inicializador. Si nos iban a dar el prototipo de celular desde el guión, el guión sería crear instancias de nosotros, llamando a init(coder:) y el problema nunca habría surgido en el primer lugar.

    • Gracias, excelente respuesta! Ahora entiendo cuál es el problema. Estoy de acuerdo, no hay una necesidad real para la subclase UITableViewController.
    • No te olvides de anular delegado y el origen de datos en deinit, se puede bloquear. En iOS9 y superior no es necesario deinit { self.formato tableview?.delegado = nil auto.formato tableview?.dataSource = nil }
    • También tenga en cuenta que UITableViewController hace cosas adicionales que usted tiene que reimplementar (y más o menos por duplicado). Creo que de la limpieza de selección en ciertos momentos, el parpadeo de desplazamiento de los indicadores, cosas así. No es gran cosa, pero simplemente la creación de una vista de tabla y lo carga como su punto de vista no es todo lo que hay.
    • Fue este error introducido por el iOS 9 SDK? Yo tenía el código que se ejecuta muy bien en iOS 8 antes de que yo publicado una actualización construido con el iOS 9 SDK. Ahora estoy viendo los accidentes en iOS 8 debido a [super initWithStyle:] llamada a [auto initWithNibName:paquete:].
    • mi respuesta, por favor. Mira el original fecha de mi respuesta. El error es fijo en iOS 9. El error es en iOS 8. Por lo tanto, el código que funciona en iOS 9 podría no funcionar en iOS 8.
    • entendido, pero este error no sucedió para mí cuando se compila contra el iOS 8 SDK. He resuelto una solución, pero estaba sorprendida de estar recibiendo los registros de bloqueo, cuando nunca he tocado esta parte de mi código durante mi iOS 9 actualización.
    • Me suena que tienes algo valioso que aportar, así que por favor siéntase libre de añadir una respuesta!
    • matt , he clasificado sub UITableViewController y la aplicación se bloquea indica, es una mala instrucción en la inicialización . He inicializado como la conveniencia de reemplazar init(estilo: UITableViewStyle) { self.init(estilo: .Agrupados) // Custom inicialización del auto.title = NSLocalizedString(«mdm.agente.común.desktopCentral», comentario : «») }
    • ¿Cómo podemos mover esta respuesta a la parte superior?!

  2. 20

    Xcode 6 Beta 5

    Parece que usted no puede declarar a un no-argumento de la conveniencia de inicializador para un UITableViewController subclase. En su lugar, usted necesita para reemplazar el valor predeterminado de inicializador.

    class TestViewController: UITableViewController {
        override init() {
            //Overriding this method prevents other initializers from being inherited.
            //The super implementation calls init:nibName:bundle:
            //so we need to redeclare that initializer to prevent a runtime crash.
            super.init(style: UITableViewStyle.Plain)
        }
    
        //This needs to be implemented (enforced by compiler).
        required init(coder aDecoder: NSCoder!) {
            //Or call super implementation
            fatalError("NSCoding not supported")
        }
    
        //Need this to prevent runtime error:
        //fatal error: use of unimplemented initializer 'init(nibName:bundle:)'
        //for class 'TestViewController'
        //I made this private since users should use the no-argument constructor.
        private override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
            super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        }
    }
    • Esto es una locura, yo tengo como 20 de esos errores. Es allí cualquier manera de aflojar la restricción impuesta por el compilador para hacer que se ejecute como pre beta 5?
    • No he encontrado una mejor manera.
    • Como de Xcode 6.3.1 esto es todavía un problema. Su imposible la costumbre de tener un método init ahora para un UITableViewController subclase. No llame a su designado inicializa en lugar de llamar a init(nibname:bundle).
  3. 3

    Lo hice así

    class TestViewController: UITableViewController {
    
        var dsc_var: UITableViewController?
    
        override convenience init() {
            self.init(style: .Plain)
    
            self.title = "Test"
            self.clearsSelectionOnViewWillAppear = true
        }
    }

    Crear y mostrar un ejemplo de TestViewController en un UISplitViewController hizo el trabajo para mí, con este código.
    Tal vez esta es una mala práctica, por favor, dime si es (acaba de empezar con swift).

    Para mí todavía hay un problema cuando no existen variables opcionales y la solución de Nick Snyder es el único que trabaja en esta situación

    Sólo hay 1 problema:
    Las variables se inicializan 2 veces.

    Ejemplo:

    var dsc_statistcs_ctl: StatisticsController?
    
    var dsrc_champions: NSMutableArray
    
    let dsc_search_controller: UISearchController
    let dsrc_search_results: NSMutableArray
    
    
    override init() {
        dsrc_champions = dsrg_champions!
    
        dsc_search_controller = UISearchController(searchResultsController: nil)
        dsrc_search_results = NSMutableArray.array()
    
        super.init(style: .Plain) //-> calls init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) of this class
    }
    
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        //following variables were already initialized when init() was called and now initialized again
        dsrc_champions = dsrg_champions!
    
        dsc_search_controller = UISearchController(searchResultsController: nil)
        dsrc_search_results = NSMutableArray.array()
    
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }
    • Esto es realmente una locura. Ugh.
  4. 2

    Apoyos a matt para una gran explicación. Yo he hecho uso de ambos matt y @Nick Snyder soluciones, sin embargo me encontré con un caso en el que tampoco acaba de funcionar, porque necesitaba (1) inicializar let campos, (2) el uso init(style: .Grouped) (sin recibir un error de tiempo de ejecución), y (3) utilizar el built-in refreshControl (de UITableViewController). Mi solución fue introducir una clase intermedia MyTableViewController en ObjC, a continuación, utilizar esa clase como la base de mi tabla de la vista de los controladores.

    MyTableViewController.h

    #import <UIKit/UIKit.h>
    //extend but only override 1 designated initializer
    @interface MyTableViewController : UITableViewController
    - (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER;
    @end

    MyTableViewController.m:

    #import "MyTableViewController.h"
    //clang will warn about missing designated initializers from
    //UITableViewController without the next line.  In this case
    //we are intentionally doing this so we disregard the warning.
    #pragma clang diagnostic ignored "-Wobjc-designated-initializers"
    @implementation MyTableViewController
    - (instancetype)initWithStyle:(UITableViewStyle)style {
        return [super initWithStyle:style];
    }
    @end

    Agregar el siguiente Proyecto del Puente de Encabezado.h

    #import "MyTableViewController.h"

    , A continuación, utilizar en swift. Ejemplo: «PuppyViewController.swift»:

    class PuppyViewController : MyTableViewController {
        let _puppyTypes : [String]
        init(puppyTypes : [String]) {
            _puppyTypes = puppyTypes //(1) init let field (once!)
            super.init(style: .Grouped) //(2) call super with style and w/o error
            self.refreshControl = MyRefreshControl() //(3) setup refresh control
        }
        //... rest of implementation ...
    }
  5. 1

    matt respuesta es la más completa, pero si usted desea utilizar un tableViewController en el .estilo sencillo (digamos, por motivos heredados). Entonces todo lo que necesita hacer es llamar a

    super.init(nibName: nil, bundle: nil)

    lugar de

    super.init(style: UITableViewStyle.Plain) o self.init(style: UITableViewStyle.Plain)

  6. 0

    Quería subclase UITableViewController y agregar un no-propiedad opcional que requiere reemplazar el inicializador y lidiar con todos los problemas descritos anteriormente.

    El uso de un Guión y una continuación te ofrece más opciones si usted puede trabajar con un opcional de var en lugar de un no-opcional deje en la subclase de UITableViewController

    Llamando performSegueWithIdentifier y primordial prepareForSegue en su presentación de controlador de vista, se puede obtener la instancia de la UITableViewController subclase y establecer las variables opcionales antes de la inicialización se ha completado:

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
            if segue.identifier == "segueA"{
                var viewController : ATableViewController = segue.destinationViewController as ATableViewController
                viewController.someVariable = SomeInitializer()
            }
    
            if segue.identifier == "segueB"{
                var viewController : BTableViewController = segue.destinationViewController as BTableViewController
                viewController.someVariable = SomeInitializer()
            }
    
        }
  7. 0

    Me he dado cuenta de un error similar cuando se utiliza estática en formato tableview células y tienes que implementar esta:

    init(coder decoder: NSCoder) {
        super.init(coder: decoder)
    }

    el caso de implementar:

    required init(coder aDecoder: NSCoder!) {
            //Or call super implementation
            fatalError("NSCoding not supported")
    }

    Estaba a un accidente no… Clase de como se esperaba. Espero que esto ayude.

  8. 0

    No está seguro de que está relacionado con tu pregunta, pero en el caso de que quieras init UITableView controlador con xib, Xcode 6.3 beta 4 Notas de la Versión de proporcionar una solución:

    • En su Swift proyecto, crear un vacío de la nueva iOS Objective-C archivo. Esto desencadenará una hoja de preguntándole «¿desea configurar un Objetivo-C puente de cabecera».
    • Presiona «Sí» para crear un puente de encabezado
    • Dentro de [YOURPROJECTNAME]-Puente-de Encabezado.h agregue el código siguiente:
    @import UIKit;
    @interface UITableViewController() //Extend UITableViewController to work around 19775924
    - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER ;
    @end
  9. 0
    class ExampleViewController: UITableViewController {
        private var userName: String = ""
    
        static func create(userName: String) -> ExampleViewController {
            let instance = ExampleViewController(style: UITableViewStyle.Grouped)
            instance.userName = userName
            return instance
        }
    }
    
    let vc = ExampleViewController.create("John Doe")

Dejar respuesta

Please enter your comment!
Please enter your name here