Intentando cargar la vista de un controlador de vista mientras se desasigna… UISearchController

80

Tengo un código que crea un UISearchController' in my UIVIew'sviewDidLoad`.

 self.resultSearchController = ({
        let controller = UISearchController(searchResultsController: nil)
        controller.searchResultsUpdater = self
        controller.searchBar.delegate = self
        controller.dimsBackgroundDuringPresentation = false
        controller.searchBar.sizeToFit()
        controller.hidesNavigationBarDuringPresentation = false //prevent search bar from moving
        controller.searchBar.placeholder = "Search for song"

        self.myTableView.tableHeaderView = controller.searchBar

        return controller

    })()

Inmediatamente después de que finalice este cierre, esta advertencia aparece en la consola:

Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UISearchController: 0x154d39700>)

No entiendo lo que estoy haciendo mal. Esta pregunta similar no es realmente mi situación (al menos no lo creo). Que esta pasando?

MortalMan
fuente
xkcd.com/583 Funciona bien si lo coloco en mi Table VC viewDidLoad(). Recomiende a) incluir la lista completa de fuentes de VC yb) asegurarse de que el error realmente esté ocurriendo donde y cuando usted cree que está.
BaseZen
Además, investigue más, como: stackoverflow.com/questions/31006045/… que tiene el mismo error
BaseZen
Entonces, hice un proyecto rápido, hice todo de manera programada, sin guiones gráficos, y no tengo ningún problema, ¿es este un problema de guión gráfico que está teniendo, tal vez, no lo sé, pero supongo que está usando guiones gráficos, verdad? Cuando digo programáticamente, me refiero a sin plumillas, sin guiones gráficos, todo el código y funciona bien
Larry Pickles
@BaseZen Establecí un punto de interrupción antes })()y después })(). El error se produce después de que finaliza el cierre. Tengo un UIViewController, no un tableViewController.
MortalMan
@Larcerax Tengo un guión gráfico. Contiene solo un controlador de navegación y un controlador UIViewController (están conectados)
MortalMan

Respuestas:

119

La vista de UISearchController debe eliminarse de su supervista antes de desasignar. (supongo que es un error)

C objetivo...

-(void)dealloc { 
    [searchController.view removeFromSuperview]; // It works!
}

Rápido 3 ...

deinit {
    self.searchController.view.removeFromSuperview()
}

Luché con este problema durante un par de semanas. ^^

JJH
fuente
1
Esto es realmente extraño ... Pero a mí también me sirvió. Supongo que es un error.
Mihai Fratu
22
Tuve el mismo problema con una UISearchControlerasignación en -viewDidLoad. Definitivamente es un error: si UISearchControllerse desasigna antes de que se cargue su vista, aparecerá esta advertencia. Si toco el campo de búsqueda (cargando así la vista), no aparece. Entonces, en mi dealloc, llamo [self.searchController loadViewIfNeeded](nuevo en iOS 9),
Leehro
4
El comentario de @ Leehro es la respuesta para mí. salió comoif #available(iOS 9.0, *) { self.searchController?.loadViewIfNeeded() }
Tim
6
Agregaría al comentario de @ Leehro que no hay necesidad de hacer esto en dealloc, puede hacer loadViewIfNeeded en viewDidLoad en su lugar.
Clafou
Gracias a @Leehro y @Clafou ... agregar la llamada [self.searchController loadViewIfNeeded];(Obj-C) es la respuesta que resolvió el problema en mi código.
andrewbuilder
36

¡Resuelto! Fue una solución simple. Cambié este código

class ViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {

    var resultSearchController = UISearchController()

a esto:

 class ViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {

    var resultSearchController: UISearchController!

Esto soluciona el problema.

MortalMan
fuente
1
Lo cambié por lo que querías decir. :-) De todos modos, es por eso que incluir toda la fuente en la pregunta es mejor, pero encontrarla por su cuenta es mejor ;-)
BaseZen
Yo también estaba teniendo esta molesta advertencia y, siguiendo tu respuesta, arreglé todo. ¡Pero no sé por qué! es necesario en lugar de asignar un nuevo UISearchController ... ¿me explicaría?
SagitarioA
No estoy seguro, también me gustaría una explicación.
MortalMan
Si cambio de var resultSearchController = UISearchController () a var resultSearchController: UISearchController! Recibo un error fatal: inesperadamente encontré nulo al desenvolver un valor opcional en noOfRowInSection. Tengo una gran cantidad de matriz, ¿está creando un error? sugiereme.
Pawriwes
Descubrí, en lugar de hacer delegar y fuente de datos desde el guión gráfico, escribí en el código y el problema se resolvió.
Pawriwes
20

Aquí está la versión Swift que funcionó para mí (similar a la respuesta de JJH):

deinit{
    if let superView = resultSearchController.view.superview
    {
        superView.removeFromSuperview()
    }
}
nijm
fuente
@alex Lo puse al final del controlador de vista que inicializa el resultSearchController.
nijm
2
Realmente no NECESITAS el if letpuesto .removeFromSuperView()que no hará nada sisuperview == nil
NSGangster
11
class SampleClass: UITableViewController, UISearchBarDelegate {

private let searchController =  UISearchController(searchResultsController: nil)

 override func viewDidLoad() {
        super.viewDidLoad()

        searchController.loadViewIfNeeded() // Add this line before accessing searchController
 }

}
harsh_v
fuente
10

Hackeando algunas soluciones, logré que la mía funcionara agregando líneas a viewDidLoad antes de configurar completamente el UISearchController:

override func viewDidLoad() {
    super.viewDidLoad()
    self.navigationItem.rightBarButtonItem = self.editButtonItem()

    if #available(iOS 9.0, *) {
        self.resultSearchController.loadViewIfNeeded()// iOS 9
    } else {
        // Fallback on earlier versions
        let _ = self.resultSearchController.view          // iOS 8
    }
    self.resultSearchController = ({
        let controller = UISearchController(searchResultsController: nil)
        controller.searchResultsUpdater = self
        controller.dimsBackgroundDuringPresentation = false
        controller.searchBar.sizeToFit()

        self.tableView.tableHeaderView = controller.searchBar

        return controller
    })()

    self.tableView.reloadData()

}
Derek
fuente
También probé todos los demás solución dada aquí, y no hubo quien suprimir la advertencia (aunque el controlador de búsqueda que hace el trabajo en tiempo de ejecución); esto lo hizo. ¡Gracias!
Nicolas Miari
Esto también me ha ayudado, pero tuve que agregar esa línea después de inicializar UISearchController. self.searchController = ({ let controller = UISearchController(searchResultsController: nil) controller.searchResultsUpdater = self controller.dimsBackgroundDuringPresentation = false controller.searchBar.delegate = self definesPresentationContext = true controller.searchBar.sizeToFit() return controller })() entonces self.searchController.loadViewIfNeeded()
Yuzer
¿Por qué necesitamos inicializar en el bloque?
code4latte
7

En Swift2 recibí el mismo mensaje de error debido a un error obvio:

let alertController = UIAlertController(title: "Oops",
    message:"bla.", preferredStyle: UIAlertControllerStyle.Alert)

alertController.addAction(UIAlertAction(title: "Ok", 
     style: UIAlertActionStyle.Default,handler: nil))

self.presentViewController(alertController, animated: true, completion: nil)

Debido a un error de copia estúpido de mí mismo, no había incluido la línea self.presentViewController. Esto provocó el mismo error.

Vincent
fuente
7

En la versión Swift 2.2 que funcionó para mí

deinit {
    self.searchController?.view.removeFromSuperview()
}

¡Creo que es útil!

Milos Mandic
fuente
2

No es un error. Parece que debe evitar crear ViewControllers sin presentarlos. Así que después SomeViewController()o let variable: SomeViewControllertienes que llamar a algo como esto self.presentViewController(yourViewController ...etc). Si no lo hace, recibirá esta advertencia cuando se desubique este controlador de vista.

Nikolai Ischuk
fuente
2

El mío funciona así

func initSearchControl(){

        searchController = UISearchController(searchResultsController: nil)

        if #available(iOS 9.0, *) {
            searchController.loadViewIfNeeded()
        } else {
            let _ = self.searchController.view
        }

        searchController.searchResultsUpdater = self
        searchController.dimsBackgroundDuringPresentation = false
        definesPresentationContext = true
        tableView.tableHeaderView = searchController.searchBar
        searchController.searchBar.sizeToFit()
    }

searchController.loadViewIfNeeded () resuelve el problema, pero debe llamarlo después de inicializar el searchController

mehmetsen80
fuente
2

La creación de un controlador de búsqueda viewDidLoad()y la configuración de su barra de búsqueda como vista de título del elemento de navegación no crea una referencia sólida al controlador de búsqueda, por lo que se desasigna.

Entonces, en lugar de hacer esto:

override func viewDidLoad() {
    super.viewDidLoad()
    // Create search controller
    let searchController = UISearchController(searchResultsController: nil)
    // Add search bar to navigation bar
    navigationItem.titleView = searchController.searchBar
    // Size search bar
    searchController.searchBar.sizeToFit()
}

Usted debe hacer esto:

var searchController: UISearchController!

override func viewDidLoad() {
    super.viewDidLoad()
    // Create search controller
    searchController = UISearchController(searchResultsController: nil)
    // Add search bar to navigation bar
    navigationItem.titleView = searchController.searchBar
    // Size search bar
    searchController.searchBar.sizeToFit()
}
Niels
fuente
1

Usé la respuesta de Derek, pero tuve que cambiarla ligeramente. La respuesta que se proporcionó se bloqueó porque la llamada a loadViewIfNeeded () ocurrió antes de que se definiera el resultSearchController. (Mi declaración fue

var resultSearchController: UISearchController!

). Así que lo moví después y funcionó.

Si omití la llamada por completo, el error se mantuvo, así que estoy seguro de que es una parte esencial de la respuesta. No pude probarlo en iOS 8.

Michael L. Mehr
fuente
1

Parece que la vista se carga de forma diferida, si asignó el controlador y nunca lo muestra, la vista no se carga. En este caso, si se cancela la asignación del controlador, recibirá esta advertencia. podría mostrarlo una vez, o llamar a su método loadViewIfNeed (), o usar 'let _ = controller.view' para forzar la carga de la vista para evitar esta advertencia.

david
fuente
en iOS 8, solo puede usar 'let _ = controller.view'.
David
0

Llego un poco tarde a la fiesta, pero esta es mi solución:

var resultSearchController: UISearchController!

override func viewDidLoad()
{
    super.viewDidLoad()

    self.resultSearchController = ({
        let searchController = UISearchController(searchResultsController: nil)
        searchController.searchResultsUpdater = self
        searchController.dimsBackgroundDuringPresentation = false
        searchController.searchBar.sizeToFit()
        return searchController
    })()

    self.tableView.tableHeaderView = self.resultSearchController.searchBar
    self.tableView.reloadData()
}

Espero que te funcione.

titusmagnus
fuente
¿En qué se diferencia de inicializar y configurar directamente? Quiero decir sin usar la sintaxis de bloque.
code4latte