Próba załadowania widoku kontrolera widoku podczas zwalniania… UISearchController


80

Mam kod, który tworzy 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

    })()

Zaraz po zakończeniu tego zamknięcia w konsoli pojawi się to ostrzeżenie:

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

Nie rozumiem tego, co robię źle. To podobne pytanie nie jest tak naprawdę moją sytuacją (przynajmniej nie sądzę). Co się dzieje?


xkcd.com/583 Działa dobrze, jeśli wrzucę to do mojej tabeli VC viewDidLoad(). Zalecamy a) w tym całą listę źródeł VC oraz b) upewnienie się, że błąd rzeczywiście występuje tam, gdzie myślisz, że jest.
BaseZen

Zrób też więcej badań, takich jak: stackoverflow.com/questions/31006045/… który ma ten sam błąd
BaseZen

Zrobiłem więc szybki projekt, zrobiłem wszystko programowo, bez scenorysów, i nie mam problemu, czy to jest problem storyboardu, który masz, być może, nie wiem, ale zakładam, że używasz scenorysów, prawda? Kiedy mówię programowo, mam na myśli brak stalówek, scenorysów, cały kod i działa dobrze
Larry Pickles

@BaseZen Ustawiłem punkt przerwania przed })()i po })(). Błąd jest zgłaszany po zakończeniu zamykania. Mam, a UIViewControllernie tableViewController.
MortalMan

@Larcerax Mam jedną scenorys. Zawiera tylko kontroler nawigacji i kontroler UIViewController (są one połączone)
MortalMan

Odpowiedzi:


119

Widok UISearchControllera musi zostać usunięty z jego superviewu przed cofnięciem przydziału. (chyba to błąd)

Cel C...

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

Swift 3 ...

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

Zmagałem się z tym problemem przez kilka tygodni. ^^


1
To naprawdę dziwne ... Ale mnie też to załatwiło. Myślę, że to rzeczywiście błąd.
Mihai Fratu

22
Miałem ten sam problem z UISearchControlerprzydzielaniem w -viewDidLoad. To zdecydowanie błąd - jeśli UISearchControllerzostanie zwolniony przed załadowaniem widoku, pojawi się to ostrzeżenie. Jeśli kliknę w pole wyszukiwania (w ten sposób ładuję widok), nie pojawia się. Więc w moim deallocdzwonię [self.searchController loadViewIfNeeded](nowość w iOS 9),
Leehro

4
Komentarz @ Leehro jest dla mnie odpowiedzią. wyszło jakoif #available(iOS 9.0, *) { self.searchController?.loadViewIfNeeded() }
Tim

6
Dodałbym do komentarza @ Leehro, że nie ma potrzeby tego robić w dealloc, zamiast tego możesz wykonać loadViewIfNeeded w viewDidLoad.
Clafou

Dzięki @Leehro i @Clafou ... dodanie wywołania [self.searchController loadViewIfNeeded];(Obj-C) to odpowiedź, która rozwiązała problem w moim kodzie.
andrewbuilder

36

Rozwiązany! To była prosta poprawka. Zmieniłem ten kod

class ViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {

    var resultSearchController = UISearchController()

do tego:

 class ViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {

    var resultSearchController: UISearchController!

To rozwiązuje problem.


1
Zmieniłem to na to, co miałeś na myśli. :-) W każdym razie dlatego uwzględnienie całego źródła w pytaniu jest lepsze, ale znalezienie go samodzielnie jest najlepsze ;-)
BaseZen

Miałem też to irytujące ostrzeżenie i podążając za twoją odpowiedzią, naprawiłem wszystko. Ale nie wiem, dlaczego! jest potrzebny zamiast przydzielania nowego kontrolera UISearchController ... czy mógłbyś mi wyjaśnić?
Strzelec A

Nie jestem pewien, też chciałbym wyjaśnienia.
MortalMan,

Jeśli zmienię z var resultSearchController = UISearchController () na var resultSearchController: UISearchController! Otrzymuję błąd krytyczny: nieoczekiwanie znaleziono zero podczas rozpakowywania wartości opcjonalnej w noOfRowInSection. Mam dużą liczbę tablic, czy to powoduje błąd? zasugeruj mi.
Pawriwes

Zorientowałem się, że zamiast robić delegata i źródło danych ze scenorysu, napisałem w kodzie i problem został rozwiązany.
Pawriwes

20

Oto wersja Swift, która działała dla mnie (podobna do odpowiedzi JJH):

deinit{
    if let superView = resultSearchController.view.superview
    {
        superView.removeFromSuperview()
    }
}

@alex umieściłem go na końcu kontrolera widoku, który inicjuje resultSearchController.
nijm

2
Tak naprawdę nie POTRZEBUJESZ, if letponieważ .removeFromSuperView()nic nie zrobisz, jeślisuperview == 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
 }

}

10

Łącząc kilka rozwiązań, udało mi się uruchomić moje, dodając linie do viewDidLoad przed pełnym skonfigurowaniem 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()

}

Próbowałem też każde inne rozwiązanie podane tutaj, a nikt nie tłumić ostrzeżenie (choć kontroler wyszukiwania robi pracy w czasie pracy); to zrobiło to. Dziękuję Ci!
Nicolas Miari

To również mi pomogło, ale musiałem dodać tę linię po zainicjowaniu 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 })() potem self.searchController.loadViewIfNeeded()
yuzer

Dlaczego musimy inicjalizować w bloku?
code4latte

7

W Swift2 otrzymałem ten sam komunikat o błędzie z powodu oczywistego błędu:

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)

Z powodu popełnionego przeze mnie głupiego błędu kopiowania nie dołączyłem wiersza self.presentViewController. To spowodowało ten sam błąd.


7

W wersji Swift 2.2, która działała dla mnie

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

Myślę, że to pomocne!


2

To nie jest błąd. Wygląda na to, że musisz unikać tworzenia ViewControllerów bez ich prezentowania. Więc po SomeViewController()lub let variable: SomeViewControllermusisz zadzwonić do czegoś takiego self.presentViewController(yourViewController ...etc). Jeśli tego nie zrobisz, otrzymasz to ostrzeżenie, gdy ten kontroler widoku zostanie przydzielony.


2

Mój tak działa

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 () rozwiązuje problem, ale musisz go wywołać po zainicjowaniu searchController


2

Utworzenie kontrolera wyszukiwania w programie viewDidLoad()i ustawienie jego paska wyszukiwania jako widoku tytułu elementu nawigacji nie tworzy silnego odniesienia do kontrolera wyszukiwania, dlatego jest on cofnięty.

Więc zamiast tego robić:

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

Powinieneś to zrobić:

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

1

Użyłem odpowiedzi Dereka, ale musiałem ją nieco zmienić. Podana odpowiedź uległa awarii, ponieważ wywołanie loadViewIfNeeded () miało miejsce przed zdefiniowaniem resultSearchController. (Moja deklaracja brzmiała

var resultSearchController: UISearchController!

). Więc po prostu go przeniosłem i zadziałało.

Jeśli całkowicie pominąłem połączenie, błąd pozostał, więc jestem pewien, że jest to istotna część odpowiedzi. Nie mogłem go przetestować na iOS 8.


1

Wygląda na to, że widok jest ładowany z opóźnieniem, jeśli przydzieliłeś kontroler i nigdy go nie pokazałeś, widok nie jest załadowany. W takim przypadku, jeśli kontroler zostanie zwolniony, otrzymasz to ostrzeżenie. możesz to raz pokazać, wywołać metodę loadViewIfNeed () lub użyć 'let _ = controller.view', aby wymusić załadowanie widoku, aby uniknąć tego ostrzeżenia.


w iOS 8 możesz użyć tylko „let _ = controller.view”.
david

0

Trochę spóźniłem się na imprezę, ale oto moje rozwiązanie:

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

Mam nadzieję, że to zadziała dla ciebie.


Czym się to różni od bezpośredniego inicjowania i konfigurowania; Mam na myśli bez użycia składni bloku?
code4latte
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.