Спроба завантажити вигляд контролера перегляду під час його вивільнення ... UISearchController


80

У мене є код, який створює 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

    })()

Одразу після завершення закриття на консолі з’являється таке попередження:

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

Я не розумію, що роблю неправильно. Це подібне питання насправді не є моєю ситуацією (принаймні, я не думаю). Що відбувається?


xkcd.com/583 Працює нормально, якщо я вкину це у свою таблицю VC viewDidLoad(). Рекомендуйте a) включаючи весь список джерел VC та b) переконуючись, що помилка дійсно трапляється там, де і коли ви думаєте, що вона є.
BaseZen

Крім того,
проведіть

Отже, я зробив швидкий проект, зробив все програматично, без розкадрувань, і у мене не виникає проблем. Це проблема раскадровки, яка у вас виникає, можливо, я не знаю, але я припускаю, що ви використовуєте розкадровки, так? Коли я кажу програмно, я маю на увазі відсутність маніпуляторів, розкадрувань, весь код, і це працює нормально
Ларрі Піклз,

@BaseZen Я встановив точку зупинки до })()і після })(). Помилка видається після закінчення закриття. У мене є, а UIViewControllerне a tableViewController.
MortalMan

@Larcerax У мене є одна розкадровка. Він містить лише контролер навігації та контролер UIViewController (вони підключені.)
MortalMan

Відповіді:


119

Погляд UISearchController перед видаленням звільнення потрібно вилучити з його попереднього перегляду. (припустимо, це помилка)

Завдання-C ...

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

Свіфт 3 ...

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

Я боровся з цим питанням пару тижнів. ^^


1
Це справді дивно ... Але це зробило трюк і для мене. Я думаю, це справді помилка.
Mihai Fratu,

22
У мене була та ж проблема з тим, що UISearchControlerя виділяв -viewDidLoad. Це, безумовно, помилка - якщо файл UISearchControllerзвільнено до того, як його вигляд буде завантажений, з’явиться це попередження. Якщо я натискаю на поле пошуку (тим самим завантажуючи подання), воно не відображається. Тож у своєму dealloc, я дзвоню [self.searchController loadViewIfNeeded](новий у iOS 9),
Leehro

4
Коментар @ Leehro - це відповідь для мене. це вийшло якif #available(iOS 9.0, *) { self.searchController?.loadViewIfNeeded() }
Тім

6
Я додав би до коментаря @ Leehro, що це не потрібно робити в dealloc, замість цього ви можете зробити loadViewIfNeeded у viewDidLoad.
Клафу

Завдяки @Leehro та @Clafou ... додавання виклику [self.searchController loadViewIfNeeded];(Obj-C) - це відповідь, яка вирішила проблему в моєму коді.
andrewbuilder

36

Вирішено! Це було просте виправлення. Я змінив цей код

class ViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {

    var resultSearchController = UISearchController()

до цього:

 class ViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {

    var resultSearchController: UISearchController!

Це вирішує проблему.


1
Я змінив його на те, що ви мали на увазі. :-) У будь-якому випадку, тому включити ціле джерело до запитання краще, але найкраще знайти його самостійно ;-)
BaseZen

У мене теж було це надокучливе попередження, і, слідуючи вашій відповіді, я все виправив. Але я не знаю чому? потрібен замість виділення нового UISearchController ... Ви б мені пояснили?
Стрілець,

Не впевнений, я б теж хотів пояснити.
MortalMan

Якщо я перейду з var resultSearchController = UISearchController () на var resultSearchController: UISearchController! Я отримую фатальну помилку: несподівано знайшов нуль під час розгортання необов’язкового значення у noOfRowInSection. У мене велика кількість масиву, це помилка створення? запропонуйте мені.
Pawriwes

Я зрозумів, замість того, щоб робити делегат і джерело даних з розкадрування, я писав у коді, і проблема була вирішена.
Pawriwes

20

Ось версія Swift, яка працювала для мене (подібно до відповіді JJHs):

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

@alex Я помістив його в кінець контролера подання, який ініціалізує resultSearchController.
нійм

2
Ви насправді НЕ ПОТРІБНІ, if letоскільки .removeFromSuperView()не зробите нічого, якщоsuperview == 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

Зібравши кілька рішень, мені вдалося змусити мене працювати, додавши рядки до viewDidLoad, перш ніж повністю налаштувати 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()

}

Я також спробував все іншими рішеннями наведених тут, і ніхто не зробив переважна попередження (хоча контролер пошуку робить роботу під час виконання); це зробило це. Дякую!
Ніколас Міарі,

Це допомогло і мені, але мені довелося додати цей рядок після ініціалізації 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 })() потім self.searchController.loadViewIfNeeded()
юзер

Чому нам потрібно ініціалізувати в блоці?
code4latte

7

У Swift2 я отримав те саме повідомлення про помилку через очевидну помилку:

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)

Через дурну помилку від мого копіювання, я не включив рядок self.presentViewController. Це спричинило ту саму помилку.


7

У версії Swift 2.2, яка працювала у мене

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

Я думаю, це корисно!


2

Це не помилка. Здається, вам слід уникати створення ViewControllers, не представляючи їх. Тож після SomeViewController()або let variable: SomeViewControllerвам доведеться зателефонувати щось подібне self.presentViewController(yourViewController ...etc). Якщо ви цього не зробите, ви отримаєте це попередження, коли цей контролер подання буде знесилено.


2

Шахта працює так

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 () вирішує проблему, але вам потрібно викликати її після ініціалізації searchController


2

Створення контролера пошуку viewDidLoad()та встановлення його панелі пошуку як подання заголовка елемента навігації не створює чіткого посилання на контролер пошуку, тому його звільнено.

Отже, замість цього:

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

Ви повинні зробити це:

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

Я використав відповідь Дерека, але довелося її трохи змінити. Надана відповідь зірвалася для мене, оскільки виклик loadViewIfNeeded () відбувся до того, як був визначений resultSearchController. (Моя декларація була

var resultSearchController: UISearchController!

). Тож я просто перемістив його згодом, і це спрацювало.

Якщо я повністю пропустив дзвінок, помилка залишилася, тому я впевнений, що це є важливою частиною відповіді. Мені не вдалося протестувати його на iOS 8.


1

Здається, подання завантажується ліниво, якщо ви виділили контролер і ніколи його не показуєте, вигляд не завантажується. У цьому випадку, якщо контролер звільнено, ви отримаєте це попередження. Ви можете показати його один раз, або викликати метод loadViewIfNeed (), або використовувати 'let _ = controller.view', щоб змусити завантажити подання, щоб уникнути цього попередження.


на iOS 8 ви можете використовувати лише 'let _ = controller.view'.
Девід

0

Я трохи запізнився на вечірку, але ось моє рішення:

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

Я сподіваюся, це працює для вас.


Чим це відрізняється від безпосередньої ініціалізації та налаштування; Я маю на увазі без використання синтаксису блоку?
code4latte
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.