IBOutlet равен нулю, но он связан в раскадровке, Swift

102

Использование Swift 1.1 и Xcode 6.2.

У меня есть доска UIStoryboard, содержащая единственный настраиваемый UIViewControllerподкласс. На нем у меня есть @IBOutletсоединение типа UIViewэтого контроллера с UIView подклассом в раскадровке. У меня также есть аналогичные выходы для подвидов этой точки зрения. Рисунок А.

Но во время выполнения эти свойства равны нулю (рисунок B). Хотя я заверил, что подключил розетки в Interface Builder.

Мысли :

  • Возможно ли, что из-за того, что я использую подкласс подкласса, что-то не так с инициализацией? Я не отменяю инициализаторы
  • awakeFromNib: по какой-то причине не звонят
  • Возможно, он не подключается к подпредставлениям на подпредставлениях

Вещи, которые я пробовал:

  • Точное соответствие @IBOutletи типы элементов раскадровки (вместо UIView)
  • Удаление собственности и торговой точки и их повторное добавление

Рисунок А

Рисунок A *

Рисунок B

Рисунок B

* Скрытый код Figure A:

@IBOutlet private var annotationOptionsView: UIView!
@IBOutlet private var arrivingLeavingSwitch: UISegmentedControl!

Спасибо.

Стефан Арамбашич
источник
Почему бы не изменить! к?
clearView равен нулю, потому что не связан с раскадровкой (см. кружок слева от кода с отверстием, что означает, что он не связан), на снимке экрана я не вижу объявления annotationOptionView.
Javier Flores Font
@JavierFloresFont: clearViewя ожидаю, что будет ноль. Это то, что мне еще предстоит рефакторинг. Также см. Сноску к рисунку A. @ShaanSingh Так и должно быть! потому что соединения из раскадровки (должны быть) установлены во время выполнения и не должны быть нулевыми.
Стефан Арамбашич
Как этот контроллер представления загружается? Покажите нам код, который его запрашивает, или опишите переход, который к нему подключается.
Роб Мэйофф
1
Получается правильная раскадровка: let vc = UIStoryboard(name: "LocationPickerModal", bundle: nil) .instantiateViewControllerWithIdentifier("LocationPickerModalViewController") as LocationPickerModalViewController
Стефан Арамбашич

Ответы:

146

Обычно это происходит из-за того, что ваш контроллер представления еще не загрузил свою иерархию представлений. Контроллер представления загружает свою иерархию представления только тогда, когда что-то отправляет ему viewсообщение. Система делает это, когда пора фактически поместить иерархию представлений на экран, что происходит после таких действий, как prepareForSegue:sender:иviewWillAppear: , вернулись.

Поскольку ваш VC еще не загрузил свою иерархию представлений, ваши выходы по-прежнему равны нулю.

Вы можете заставить VC загрузить свою иерархию представлений, сказав _ = self.view.

Роб Мэйофф
источник
4
Это происходит при viewWillAppear:вызове, когда я впервые что-либо делаю с этой розеткой. Доступ к представлению ничего не делает для меня. По-прежнему ноль.
Стефан Арамбашич
Спасибо! Это сработало и для меня. var v = viewcontroller.view; принудительно загружает иерархию представлений. Маленькая уловка! ;)
Йорди Уйтерспрот
@YordiUytersprot, где бы вы поместили эту строку?
Async
4
Что ж, если вы создаете экземпляр контроллера представления: let vc = self.storyboard? .InstantianteViewControllerWithIdentifier ("StoryboardIDfromVC") as? CustomVC; пусть view = vc.view; // Прямо сейчас вы можете получить доступ к IBOutlets из CustomVC здесь .. vc.customTextField.text = "Lalala"; Надеюсь, это вам поможет! :)
Йорди Уйтерспрот 08
4
UIViewcontroller.loadViewIfNeeded()- правильный метод здесь.
orkoden 05
27

Вы пробовали запустить Продукт> Очистить. Решил для меня очень похожую проблему.

ThottChief
источник
Я сделал. Извините, хотел отметить свой ответ. Он заработал только после того, как я удалил DerivedDataпапку с проектом.
Стефан Арамбашич
3
Только два из моих торговых точек в середине моей иерархии просмотров были нулевыми. Продукт -> Чистый сработал для меня!
Rikco
1
obj c, просто очистка проекта у меня сработала ... не могу поверить, потратил больше часа и даже не подумал очистить проект: / но спасибо !!
теплица
Просто продукт -> Очистить у меня не сработало, но я запустил другой симулятор, и он сработал, так что в папке DerivedData тоже должно быть что-то не так.
endavid
22

Вы создали экземпляр своего контроллера представления из раскадровки или NIB, или вы создали его напрямую через инициализатор?

Если вы создали экземпляр своего класса напрямую с помощью инициализатора, выходы не будут подключены. Интерфейсный Разработчик создает настроенные экземпляры ваших классов и кодирует эти экземпляры в NIB и Раскадровки для повторного декодирования, он не определяет сами классы. Если это была ваша проблема, вам просто нужно изменить код, в котором вы создаете контроллер, чтобы вместо этого использовать методы на UIStoryboard или UINib.

Джон Хесс
источник
2
Я создаю UIStoryboardэкземпляр и вызываю instantiateViewControllerWithIdentifierего.
Стефан Арамбашич
16

Раскадровка не распознавала какие-либо дополнительные элементы пользовательского интерфейса, которые я добавил к ней. Во время выполнения все ссылки были нулевыми. Итак, я очистил папку с производными данными, и затем эти соединения снова заработали.

Стефан Арамбашич
источник
2
Это ответ. Я довольно долго боролся с этим, очищая папку сборки и все такое. В моем случае viewDidLoad()все подключения были сделаны, но viewWillAppear()почти все были nil(иногда одно или два все же были подключены). Я все перепробовал и, наконец, удалил папку с производными данными, перестроил, и все было хорошо.
ruttopia
6
Даже через 3 года в Xcode 9.2 все еще есть чертова ошибка. Огромное спасибо.
Кемаль Джан Кайнак
1
Здесь, чтобы сообщить, что это все еще существует в Xcode 11. :(
потрачено
13

Это происходило со мной с моей пользовательской ячейкой представления коллекции. Оказывается, мне пришлось заменить мой метод registerClassforReuseIdentifier на registerNib. Это исправило это для меня.

рунак
источник
Ты прав. Я подробно прочитал содержание подсчета ARC в Swift Programming Language. Я не могу найти причину, по которой свойство weak IBOutlet все еще равно нулю. Наконец, я ищу в Google «@IBOutlet swift nil» и нахожу ваш ответ в SO. Затем я выделил строку, в которой шаблон Xcode добавил self.collectionView! .RegisterClass в viewDidLoad. Тогда это работает. Ты спас мне жизнь.
charles.cc.hsu
12

Это произошло для меня, потому что я случайно создавал экземпляр своего контроллера представления напрямую, вместо того, чтобы создавать его через раскадровку. Если вы создаете экземпляр напрямую через, MyViewController()выходы не будут подключены.

Эрик Коннер
источник
Если вы используете файл XIB, создание экземпляра напрямую по-прежнему работает успешно.
Луат Ву Динь
11

В моем случае это произошло из-за того, что я переопределил loadViewметод в своем подклассе ViewController, но забыл добавить[super loadView]

-(void)loadView {
// blank
}

Когда вы отменяете loadView метод, ответственность за инициирование вложенных представлений. Поскольку вы его переопределяете, представления из построителя интерфейса не имеют возможности преобразоваться в объекты какао, и, следовательно, выходы остаются нулевыми.

Если вы реализуете loadView в своем подклассе контроллера представления, тогда вы несете ответственность за загрузку элементов пользовательского интерфейса из раскадровки / xib в код.

Или просто позвони

[super loadView];

Чтобы суперкласс получил возможность загрузить раскадровку / xib в код.

циннурие
источник
8

Вы можете вызвать controller.viewпринудительную загрузку представления для инициализации IBOutlets, после чего вы сможете назначать значения.

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    if (segue.identifier == "identifier") {
        let controller = segue.destinationViewController as! YourController
        let _ = controller.view //force to load the view to initialize the IBOutlets

        controller.your_IBOutlet_property = xxx
        ...
        controller.delegate = self
    }
}
zs2020
источник
7

Если вы создаете экземпляр view controllerпрограммно. Затем попробуйте создать его, как показано ниже

let initialVC = self.storyboard?.instantiateViewController(withIdentifier: "InitialVC") as! InitialVC

вместо прямого

let initialVC = InitialVC()

Это сработало для меня.

Винот Вино
источник
6

Для меня это произошло, когда я случайно объявил класс своего контроллера представления как

class XYZViewController: UINavigationController {
}

(т.е. как UINavigationControllerне а UIViewController).

Xcode не улавливает эту ошибку, класс, похоже, строится нормально, и функции переопределения, такие как viewDidLoad, viewWillAppearи т. Д., Работают правильно. Но ни один из них IBOutletне связан.

Изменение декларации на

class XYZViewController: UIViewController {
}

исправил это полностью.

ХьюХьюТеотл
источник
5

Я недавно столкнулся с этой проблемой! Вот моя мысль. Проблема не в вашей раскадровке или проблемах со ссылками. Это о том, как вы запускаете свой ViewController. Особенно, когда вы используете Swift. (Когда вы создаете файл класса, в редакторе почти ничего нет)

Просто используя init()суперкласс from, вы не можете инициировать что-либо, что вы работали с раскадровкой. Итак, что вам нужно сделать, это изменить инициализацию ViewController. Replace let XXViewController = XXViewController() by let XXViewController = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("XXViewController") as! XXViewController This сообщает программе перейти к раскадровке, найти XXViewController и инициировать все IBOutletв вашей раскадровке.

Надеюсь на эту помощь ~ GL

user3882355
источник
Проблема была не в этом
Стефан Арамбашич
4

2019, ОДНА ВОЗМОЖНОСТЬ ЭТОЙ УЖАСНОЙ ПРОБЛЕМЫ:

  • Допустим, у вас есть представление контейнера, в котором отображаются какие-то часы. Так что у тебя есть

    класс Часы: UIViewController

Фактически вы используете его во многих местах приложения .

На главном экране, на экране сведений, на экране редактирования.

У вас сложное современное приложение, похожее на Snapchat.

На самом деле, Clockможет быть на самом деле загружен более чем один раз в то же время где - то на одном экране . (Возможно, в некоторых случаях это скрыто.)

Вы начинаете работать над одним экземпляром Clockодной из множества раскадровок.

На этой раскадровке вы добавляете метку NewLabel.

Естественно, вы добавляете розетку в код. Все должно работать. Все остальные розетки работают отлично.

Вы точно подключили розетку.

Но приложение вылетает с NewLabel как nil.

Xcode четко сообщает вам, что «вы забыли подключить розетку».

Причина в том, что .......... у вас есть "NewLabel" только в одном из вариантов раскадровки Clock!

На самом деле авария произошла из >>> другого места <<<< вы используете Clock !!!!

Xcode не сообщает вам, что сбой произошел совсем из другого места, а не из того места, где вы работаете!

На самом деле сбой происходит не из того места, где вы работаете, а из другого раскадровки, где нет элемента «NewLabel» на этой раскадровке !!!

Разочарование.

Толстяк
источник
3

Для Swift 3.

func configureView() {
    let _  = self.view
}
prog_24
источник
2

Вам необходимо сначала загрузить иерархию представлений, чтобы создать экземпляры выходов в раскадровке. Для этого вы можете вручную вызвать методы loadView или loadViewIfNeeded.

maven25
источник
2

В моем случае приложение внезапно начало вылетать. Отладка показала, что все розетки были еще nilна моментviewDidLoad() .

Мое приложение по-прежнему использует перья (а не раскадровки) для большинства контроллеров представлений. Все было на месте, все розетки подключены правильно. Я перепроверил.

Обычно мы создаем экземпляры наших контроллеров представления как

let newVC = MYCustomViewController()

... который по какой-то причине, кажется, работает, пока .xib назван так же, как класс контроллера представления (хотя не уверен, как это работает. Мы не вызываем init(nibName:bundle:)с нулевыми аргументами или переопределяем, init()чтобы сделать это, selfкак это обычно предлагается ...).

Поэтому я попытался явно вызвать

 let newVC = MYCustomViewController(nibName: "MYCustomViewController", bundle: .main)

... только для того, чтобы встретить ошибку исключения времени выполнения:

*** Завершение работы приложения из-за неперехваченного исключения «NSInternalInconsistencyException», причина: «Не удалось загрузить NIB в комплекте:» NSBundle </ Users / nicolasmiari / Library / Developer / CoreSimulator / Devices / 3DA3CF21-108D-498F-9649-C4FC9E3C1A8D / data /Containers/Bundle/Application/C543DDC1-AE86-4D29-988C-9CCE89E23543/MyApp.app> (загружено) 'с именем' MYCustomViewController '

А потом я увидел это:

Флажок «Целевое членство» файла .xib был снят.


Должно быть, это произошло при разрешении одного из частых конфликтов слияния, касающихся файла проекта Xcode.

Apple определенно нужно придумать формат файла проекта, более удобный для SCM.

Николас Миари
источник
2

100% рабочее решение для создания ViewControllers из XIB без StoryBoards

  1. Создать класс CustomViewController: UIViewController
  2. Создать представление CustomViewControllerView.xib
  3. В CustomViewControllerView.xib в Интерфейсном Разработчике выберите Заполнители -> Владелец файла
  4. В «Инспекторе атрибутов» установите для класса значение CustomViewController.
  5. В «Инспекторе подключений» подключите «представление» к представлению верхнего уровня xib (убедитесь, что класс представления верхнего уровня не указывает на CustomViewController)
  6. В «Инспекторе подключений» подключите другие розетки (если нужны / есть)
  7. Создайте экземпляр CustomViewController в родительском контроллере представления / делегате приложения

7.1.

// creating instance
let controller = CustomViewController()

7.2.

// connecting view/xib with controller instance
let bundle = Bundle(for: type(of: controller))
bundle.loadNibNamed("CustomViewControllerView", owner: controller, options: nil)

7.3.

// get/set outlets
controller.labelOutlet.text = "title"
controller.imageOutlet.image = UIImage(named: "image1")
Роман М
источник
Фантастический ответ, спасибо! Как бы то ни было, вы также можете переопределить init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)и вызвать шаг 7.2 selfнепосредственно в классе CustomViewController.
brandonscript
1

Проверьте ваше соединение IBOutlet, если оно связано с владельцем файла или представлением. Возможны ошибки.

Ахд Радван
источник
1

Другой случай:

Ваши выходы не будут настроены до тех пор, пока не будет фактически создан экземпляр представления контроллера представления, что в вашем случае, вероятно, происходит вскоре после initWithNibName: bundle: - в этот момент они все равно будут равны нулю. Любая настройка, которая включает эти выходы, должна происходить в методе -viewDidLoad вашего контроллера представления.

93sauu
источник
1

Для меня у меня была такая же ошибка на локализованной раскадровке, элемент был добавлен в одном языковом стандарте, а не в другом, поэтому у меня была пустая ссылка для этого элемента при переключении на языковой стандарт отсутствующего элемента, мне пришлось удалить (избыточную) локализацию для эта раскадровка с использованием https://stackoverflow.com/a/42256341/1356559 .

Амр Лотфи
источник
1

Для меня это было сбоем, потому что containerViewбыло равно нулю.

Вот мой код с Crash .

@IBOutlet private var containerView: UIView!  // Connected to Storyboard
override open func loadView() {
    containerView.addSubview(anotherView)
}

Недостающее звено называлось super.loadView(). Итак, добавление решило проблему для меня.

Фиксированный код :

@IBOutlet private var containerView: UIView!
override open func loadView() {
    super.loadView()
    containerView.addSubview(anotherView)
}
BLC
источник
вы никогда не должны вызывать loadView () напрямую ( developer.apple.com/documentation/uikit/uiviewcontroller/… ), вместо этого вызывайте self.view
Лио,
1

У меня была аналогичная проблема, когда я ранее добавил регистр (_: forCellReuseIdentifier :) для настраиваемой ячейки после того, как я уже определил идентификатор в раскадровке. Был этот код в функции viewDidLoad (). Как только я его удалил, все заработало.

Gthoma2
источник
1

Еще один случай, с которым я только что столкнулся. Я изменил имя своего класса для UIViewController, но забыл изменить имя файла .xib, в котором был построен интерфейс.

Как только я уловил это и заставил имена файлов отражать имя класса, все стало хорошо!

Надеюсь, это кому-то поможет.

Брайан Трзупек
источник
1

Получил еще один ...

Если у вас есть собственный класс для UITableViewCell, но вы забыли указать Custom в стиле ячейки.

Фантом59
источник
1

Вы можете проверить, загружен ли вид.

if isViewLoaded && view.window != nil {
 //self.annotationOptionsView.
}
А. Трехо
источник
0
  1. выберите оба .hи.m просмотрите файлы контроллера
  2. удалить ссылку на эти файлы
  3. повторно добавьте файлы в дерево проекта
  4. открыть раскадровку, в конечном итоге пересобрать проект
Сандип Патель - SM
источник
0

Случайно я разделил свой контроллер представления на подклассы AVPlayerViewControllerвместо UIViewController. Воспроизведя его, чтобы UIViewControllerвсе стало нормально. Это должно помочь.

У меня не работала очистка сборки (обычная и полная), удаление папок с производными данными и выход из Xcode.

Хеманг
источник
0

У меня была такая же проблема после копирования класса (связанного с xib), чтобы повторно использовать его с другим классом viewcontroller (связанным с раскадровкой). Я забыл удалить

override var nibName

и

override var nibBundle

методы.

После их снятия заработали мои розетки.

Смукамука
источник
0

Я вижу, вы пользуетесь ViewController!? в ViewControllerклассе вы должны использовать -viewDidLoad, не -awakeFromNib , -awakeFromNibиспользовать для UIViewкласса

Евгений Ильин
источник
0

Проверьте, нет ли у вас отсутствующих или отключенных розеток.

введите описание изображения здесь

Исмаил Хасбулатов
источник
0

main.storyboardsЭто может произойти, если у вас их два и вы вносите изменения не в тот. Это может произойти в любое время, когда вы подключаете розетку к неустановленной сети storyboard.

СкоттиБлейдс
источник