Как создать файл xib

Обновлено: 06.07.2024

  • нет отдельной UIViewController - полностью автономный класс
  • розетки в классе, чтобы позволить мне установить/получить свойства вид

мой текущий подход к этому:

создать программно с помощью -(id)initWithFrame: на мой взгляд контроллер

это прекрасно работает (хотя и не называя [super init] и просто установка объекта с использованием содержимого загруженного пера кажется немного подозрительной – здесь есть совет добавить подвид в этом случае, который также отлично работает). Тем не менее, я хотел бы иметь возможность создать экземпляр представления из раскадровки также. Так Что Я может:

  1. место UIView на родительском представлении в раскадровке
  2. Установите свой пользовательский класс в MyCustomView

переопределить -(id)initWithCoder: – код, который я видел чаще всего соответствует шаблону, например:

конечно, это не работает, Как ли я использую подход выше, или ли я создаю экземпляр программно, оба в конечном итоге рекурсивно вызова -(id)initWithCoder: при входе в -(void)initializeSubviews и загрузка перо из файла.

  • общее предложение, похоже, заключается в том, чтобы встроить весь класс В UIViewController и выполнить загрузку пера там, но это кажется мне неоптимальным, поскольку для этого требуется добавить еще один файл как обертку

может кто-нибудь дать совет о том, как решить эту проблему, и получить рабочие розетки в обычае UIView с минимальной суетой / без тонкой оболочки контроллера? Или есть альтернативный, более чистый способ делать вещи с минимальным шаблонным кодом?

ваша проблема-это вызов loadNibNamed: у (потомок) initWithCoder: . loadNibNamed: внутренне называет initWithCoder: . Если вы хотите переопределить кодер раскадровки и всегда загружать свою реализацию xib, я предлагаю следующий метод. Добавьте свойство в класс представления и в файле xib установите для него заданное значение (в пользовательских атрибутах среды выполнения). Теперь, после вызова [super initWithCoder:aDecoder]; проверьте значение свойства. Если это заданное значение, не вызывайте [self initializeSubviews]; .

так, что-то вроде этого:

enter image description here

обратите внимание, что это QA (как и многие) действительно просто исторический интерес.

В настоящее время в течение многих лет и лет теперь в iOS все просто вид контейнера. полный учебник здесь

(действительно, Apple, наконец, добавил Раскадровка Ссылок, некоторое время назад, что делает его гораздо легче.)

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

(как любопытство, ответ Кенка показывает точно, как это было сделано, чтобы загрузить xib в вид оболочки, так как вы не можете действительно "назначить себе".)

Setting the file owner property of the custom view

я добавляю это как отдельный пост, чтобы обновить ситуацию с выпуском Swift. Подход, описанный Леонатаном, отлично работает в Objective-C. Однако более строгие проверки времени компиляции предотвращают self присваивается при загрузке из файла xib в Swift.

в результате нет никакой возможности, кроме как добавить представление, загруженное из файла xib в качестве подвида пользовательского подкласса UIView, а не полностью заменить self. Это аналогично второму подходу изложено в исходном вопросе. Грубая схема класса в Swift с использованием этого подхода выглядит следующим образом:

недостатком этого подхода является введение дополнительного избыточного слоя в иерархию представлений, которого не существует при использовании подхода, описанного Леонатаном в Objective-C. Однако это может быть принято как необходимое зло и продукт фундаментального способа проектирования в Xcode (мне все еще кажется сумасшедшим, что так трудно связать a пользовательский класс UIView с макетом пользовательского интерфейса таким образом, чтобы он работал последовательно как над раскадровками, так и из кода) – замена self Оптовая торговля в инициализаторе раньше никогда не казалась особенно интерпретируемым способом делать вещи, хотя наличие по существу двух классов представления на представление тоже не кажется таким уж большим.

тем не менее, одним из счастливых результатов этого подхода является то, что нам больше не нужно устанавливать пользовательский класс представления в наш файл класса в interface builder, чтобы обеспечить правильность поведение при назначении self , и так рекурсивный вызов init(coder aDecoder: NSCoder) при оформлении loadNibNamed() сломан (не устанавливая пользовательский класс в файле xib, init(coder aDecoder: NSCoder) из простой ванили UIView, а не наша пользовательская версия будет называться вместо этого).

даже если мы не можем сделать настройки класса для представления, хранящегося в xib напрямую, мы все еще можем связать представление с нашим "родительским" подклассом UIView, используя выходы/действия и т. д. после установки владельца файла представления на наш пользовательский класс:

видео, демонстрирующее реализацию такого класса представления шаг за шагом, используя этот подход, можно найти в следующем видео.

Шаг1. Замена self из раскадровки

замена self на initWithCoder: метод завершится со следующей ошибкой.

вместо этого вы можете заменить декодированный объект на awakeAfterUsingCoder: (не awakeFromNib ). например:

ШАГ2. Предотвращение рекурсивного вызова

конечно, это также вызывает проблемы рекурсивного вызова. (раскадровка раскадровки -> awakeAfterUsingCoder: -> loadNibNamed: -> awakeAfterUsingCoder: -> loadNibNamed: -> . )
Так что вы должны проверить ток awakeAfterUsingCoder: вызывается в процессе декодирования раскадровки или процессе декодирования XIB. У вас есть несколько способов сделать это:

a) используйте private @property который установлен только в NIB.

  • нет
  • просто не работает: setXib: будет называться после awakeAfterUsingCoder:

b) проверьте, если self имеет какие-либо подвиды

  • нет трюк в Interface Builder.
  • вы не можете иметь подвиды в раскадровке.

c) установите статический флаг в loadNibNamed: звоните

  • простой
  • нет трюк в Interface Builder.
  • небезопасно: статический общий флаг опасен

d) использовать частный подкласс в XIB

  • нет явного if in MyCustomView
  • начинаются _NIB_ трюк в xib Interface Builder
  • относительно больше кодов

e) использовать подкласс в качестве заполнителя в раскадровке

  • очень безопасный
  • чистый; без дополнительного кода в MyCustomView .
  • нет явного if проверьте так же, как d)
  • нужно использовать подкласс в раскадровке.

ШАГ3. Копировать свойства

после loadNibNamed: в ' awakeAfterUsingCoder:', вы должны скопировать несколько свойств из self который декодируется экземпляр F раскадровки. frame и Autolayout/свойства автоматического изменения размеров особенно важны.

ОКОНЧАТЕЛЬНОЕ РЕШЕНИЕ

как вы можете видеть, это немного шаблонный код. Мы можем реализовать их как "категорию". Здесь я расширяю обычно используемый UIView+loadFromNib код.

используя это, вы можете объявить MyCustomViewProto как:

XIB:

раскадровка:

результат:

В настоящее время я создаю приложение, которое будет иметь несколько таймеров, которые в основном все одинаковы.

Я хочу создать пользовательский класс, который использует весь код для таймеров, а также макет/анимацию, поэтому я могу иметь 5 одинаковых таймеров, которые работают независимо друг от друга.

Я создал макет с использованием IB (xcode 4.2), и весь код для таймеров в настоящее время находится только в классе viewcontroller.

У меня возникли трудности с переносом моего мозга вокруг того, как инкапсулировать все в пользовательский класс, а затем добавить его в viewcontroller, любая помощь будет высоко оценена.

ОТВЕТЫ

Ответ 1

Чтобы ответить концептуально, ваш таймер должен скорее быть подклассом UIView вместо NSObject .

Чтобы создать экземпляр вашего таймера в IB, просто перетащите его UIView на его представление контроллера просмотра и установите его классом в имя своего таймера.

enter image description here

Изменить: для дизайна IB (для создания экземпляра кода см. историю изменений)

Я не очень хорошо разбираюсь в раскадровке, но я знаю, что вы можете построить свой интерфейс в IB, используя файл .xib , который почти идентичен использованию версии раскадровки; Вы даже сможете копировать и вставлять свои представления в целом из существующего интерфейса в файл .xib .

Чтобы проверить это, я создал новый пустой .xib с именем "MyCustomTimerView.xib". Затем я добавил представление, и к нему добавлена ​​метка и две кнопки. Как и так:

enter image description here

Я создал новый класс objective-C подкласса UIView с именем "MyCustomTimer". В моем .xib я установил класс File Owner MyCustomTimer. Теперь я могу подключать действия и выходы, как и любой другой вид/контроллер. Полученный файл .h выглядит следующим образом:

Единственное препятствие для перехода - получить этот .xib в моем подклассе UIView . Использование .xib резко сокращает требуемую настройку. И поскольку вы используете раскадровку для загрузки таймеров, мы знаем, что -(id)initWithCoder: - единственный инициализатор, который будет вызываться. Итак, вот как выглядит файл реализации:

Метод с именем loadNibNamed:owner:options: делает именно то, что он звучит так, как будто он. Он загружает Nib и устанавливает свойство "File Owner" для себя. Мы извлекаем первый объект в массиве и являемся корневым представлением Nib. Мы добавляем представление как подвью и Voila на экран.

Очевидно, что это просто меняет цвет фона метки при нажатии кнопок, но этот пример должен хорошо вам помочь.

Примечания, основанные на комментариях:

Стоит отметить, что если вы получаете бесконечные проблемы с рекурсией, вы, вероятно, пропустили тонкий трюк этого решения. Это не делает то, что вы думаете, что это делает. Представление, которое помещается в раскадровку, не видно, но вместо этого загружает другое представление в качестве подвью. Это представление, которое он загружает, представляет собой представление, которое определено в nib. "Владелец файла" в банке - это невидимое представление. Классная часть состоит в том, что это невидимое представление все еще является классом objective-C, который может использоваться как контроллер вида для вида, который он вносит из ниба. Например, методы IBAction в классе MyCustomTimer - это то, чего вы ожидаете больше в контроллере представления, чем в представлении.

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

Также стоит отметить, что этот ответ должен был дать очень конкретное решение; создайте один наконечник, который может быть создан несколько раз на том же изображении, что и на раскадровке. Например, вы можете легко представить шесть из этих таймеров на экране iPad за один раз. Если вам нужно только указать представление для контроллера вида, который будет использоваться несколько раз в вашем приложении, тогда решение, предоставленное jyavenard для этого вопроса, почти наверняка будет лучше решение для вас.

Ответ 2

Обновлено для Xcode 10 и Swift 4

Вот базовая прогулка. Я изначально узнал об этом, посмотрев эту серию видео Youtube. Позже я обновил свой ответ на основе этой статьи.

Следующие два файла формируют ваш пользовательский вид:

  • .xib файл для размещения макета
  • .swift как подкласс UIView

Подробности для их добавления приведены ниже.

Файл Xib

Добавьте в проект файл.xib (Файл> Создать> Файл. > Пользовательский интерфейс> Вид). Я вызываю мой ReusableCustomView.xib .

Создайте макет, который требуется для вашего пользовательского представления. В качестве примера я сделаю макет с UILabel и UIButton . Рекомендуется использовать автоматическую компоновку, чтобы вещи автоматически изменялись независимо от того, какой размер вы задали позже. (Я использовал Freeform для размера xib в инспекторе атрибутов, чтобы я мог настраивать моделируемые показатели, но это необязательно.)

enter image description here

Быстрый файл

Добавьте в проект файл.swift (Файл> Создать> Файл. > Источник> Файл Swift). Это подкласс UIView и я UIView ReusableCustomView.swift .

Вернитесь в ваш .xib файл и нажмите "Владелец файла" в документе. В Identity Inspector напишите имя вашего .swift файла как имя пользовательского класса.

enter image description here

Замените содержимое файла ReusableCustomView.swift следующим кодом:

Обязательно получите правописание для имени вашего.xib файла.

Подключите выходы и действия, перетащив управление с метки и кнопки в маке xib на быстрый код пользовательского вида.

Теперь ваш пользовательский вид завершен. Все, что вам нужно сделать, это добавить UIView везде, где вы хотите, в свою основную раскадровку. Задайте имя класса вида ReusableCustomView в Identity Inspector.

enter image description here

Ответ 3

Существует более простой способ загрузить xib из раскадровки. Скажем, ваш контроллер имеет тип MyClassController, который наследует от UIViewController .

Вы добавляете UIViewController с помощью IB в свою раскадровку; измените тип класса на MyClassController. Удалите представление, которое было автоматически добавлено в раскадровку.

Убедитесь, что XIB, который вы хотите вызвать, называется MyClassController.xib.

Когда класс будет создан при загрузке раскадровки, xib будет автоматически загружен. Причина этого связана с реализацией по умолчанию UIViewController , которая вызывает XIB с именем класса.

Ответ 4

На самом деле это не ответ, но я думаю, что полезно использовать этот подход.

Objective-C

  • Импорт CustomViewWithXib.h и CustomViewWithXib.m на ваш проект
  • Создайте пользовательские файлы с тем же именем (.h/.m/ .xib)
  • Наследовать свой пользовательский класс из CustomViewWiспасибоib

Swift

  • Импортируйте CustomViewWithXib.swift в свой проект
  • Создайте пользовательские файлы с тем же именем (.swift и .xib)
  • Наследовать свой пользовательский класс из CustomViewWiспасибоib
  1. Перейдите к вашему xib файлу, задайте владельцу имя вашего класса, если вы необходимо подключить некоторые элементы (подробнее см. в разделе Make файл Swift - владелец ответа @Suragch)

Все, теперь вы можете добавить свой собственный вид в свою раскадровку, и это будет показано:)

Для начала создадим новый проект в Xсode. Для этого запустим программу Xсode и выберем Create a new Xcode project. В появившемся окне нужно выбрать платформу, под которую мы хотим написать приложение. Выбираем iOS и справа выбирает тип приложения Single View Application и нажимаем Next.

01-01

Вводим имя проекта, например я назову его BrainForceLesson01. Выбираем тип устройства iPhone и нажимаем Next.

01-02

Выбираем место где сохранится новый проект и нажимаем кнопку Create.

Перед нами откроется созданый проект Xcode. Выглядит это следующим образом:

01-03

Но в нем нет xib-файла. Его нужно будет создать вручную. Для этого нажмем меню File -> New -> New File. (или быстрое сочитание клавиш + N). В открывшемся окне снова выбираем платформу iOS, справа выбираем значок Application и нажимаем кнопку Next.

01-04

Обычно такой файл называют MainWindow.xib. Поэтому я так его и назову.


Нажимаем кнопку Create и у нас откроется созданный нами xib-файл, в котором мы уже сможем создавать свой интерфейс будущей программы, написанной под iPhone.

01-06

Но этого недостаточно для того чтобы сделанный нами интерфейс заработал. Для начала нужно в главном файле проекта указать созданный нами файл MainWindow.xib. Для этого выберем самый верхний файл проекта BrainForceLesson01 и во вкладке iPhon / iPod Deployment info в поле Main Interface в выпадающем списке выбрать MainWindow.

01-07

Теперь чтобы проверить работает ли наш проект правильно давайте создадим кнопку и метку. При нажатии на эту кнопку текст в метке будет меняться на другой. Этот будет рассмотрено в следующем уроке "Изменение текста надписи (элемент Label) после нажатия на кнопку".

Swift - Как создать собственный viewForHeaderInSection, используя файл XIB?

Я могу создать простой пользовательский viewForHeaderInSection программно, как показано ниже. Но я хочу делать гораздо более сложные вещи, возможно, подключаться к другому классу и достигать их свойств, таких как ячейка tableView. Просто я хочу видеть, что делаю.

Кто-нибудь может объяснить, как я могу создать собственное представление заголовка tableView с помощью xib? Я сталкивался со старыми темами Obj-C, но я новичок в языке Swift. Если кто-то объяснит максимально подробно, было бы здорово.

1. проблема: Кнопка @IBAction не связана с моим ViewController. (Исправлена)

Решено с помощью владельца файла, базового класса ViewController (щелкнул левое меню контура).

2. проблема: Проблема с высотой заголовка (Исправлена)

Решенное добавление headerView.clipsToBounds = true в viewForHeaderInSection: метод.

Для предупреждений об ограничениях этот ответ решил мои проблемы:

Когда я добавил ImageView даже такое же ограничение по высоте с этим методом в viewController, он обтекает строки tableView, как на картинке.

Если я использую, automaticAdjustsScrollViewInsets в viewDidLoad, в этом случае изображение перемещается под navigationBar. -фиксированный-

3. проблема: Если кнопка под View (Исправлена)

Типичный процесс для заголовков на основе NIB:

Создайте UITableViewHeaderFooterView подкласс, по крайней мере, с выходом для вашего лейбла. Вы также можете присвоить ему некоторый идентификатор, с помощью которого вы сможете реконструировать, какому разделу соответствует этот заголовок. Точно так же вы можете указать протокол, с помощью которого заголовок может информировать контроллер представления о событиях (например, при нажатии кнопки). Таким образом, в Swift 3 и новее:

Создать СИБ. Лично я даю NIB то же имя, что и базовый класс, чтобы упростить управление моими файлами в моем проекте и избежать путаницы. В любом случае, ключевые шаги включают:

Создайте NIB представления или, если вы начали с пустого NIB, добавьте представление в NIB;

Установите базовый класс представления на любой ваш UITableViewHeaderFooterView подкласс был (в моем примере, CustomHeader );

Добавьте свои элементы управления и ограничения в IB;

Монтировать @IBOutlet ссылки на торговые точки в вашем Swift-коде;

Подключите кнопку к @IBAction ; а также

в viewDidLoad в контроллере представления зарегистрируйте NIB. В Swift 3 и новее:

В viewForHeaderInSection удалите из очереди повторно используемое представление, используя тот же идентификатор, который вы указали на предыдущем шаге. Сделав это, теперь вы можете использовать свою розетку, вам не нужно ничего делать с программно созданными ограничениями и т. Д. Единственное, что вам нужно сделать (чтобы протокол для кнопки работал) - это указать ее делегата. Например, в Swift 3:

Очевидно, если вы собираетесь указать контроллер представления как delegate для кнопки в представлении заголовка вы должны соответствовать этому протоколу:

Все это звучит запутанно, когда я перечисляю все задействованные шаги, но на самом деле это довольно просто, если вы проделаете это один или два раза. Я считаю, что это проще, чем программно построить представление заголовка.

В ответ Мэтт возражает:

Проблема в том, что вы не можете волшебным образом повернуть UIView в перо в UITableViewHeaderFooterView просто заявив об этом в Инспекторе удостоверений.

Это просто неправильно. Если вы используете вышеупомянутый подход на основе NIB, класс, который создается для корневого представления этого представления заголовка является а UITableViewHeaderFooterView подкласс, а не UIView . Он создает экземпляр любого класса, который вы указываете для базового класса для корневого представления NIB.

Однако верно то, что некоторые свойства этого класса (в частности, contentView ) не используются в этом подходе на основе NIB. Это действительно должно быть необязательное свойство, как и textLabel а также detailTextLabel (или, лучше, они должны добавить надлежащую поддержку для UITableViewHeaderFooterView в ИБ). Я согласен с тем, что это плохой дизайн со стороны Apple, но он кажется мне неряшливым, идиосинкразическим, но незначительной проблемой, учитывая все проблемы в представлениях таблиц. Например, удивительно, что по прошествии всех этих лет мы все еще не можем создавать прототипы представлений верхнего / нижнего колонтитула в раскадровках и должны вообще полагаться на эти NIB и методы регистрации классов.

Но неверно делать вывод, что нельзя использовать register(_:forHeaderFooterViewReuseIdentifier:) , метод API, который активно используется с iOS 6. Давайте не выбрасываем ребенка вместе с водой из ванны.

См. Предыдущую версию этого ответа для представлений Swift 2.

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

UITableViewHeaderFooterView имеет contentView , и все ваши пользовательские подпредставления должны быть добавлены к этому, а не к UITableViewHeaderFooterView.

Установка цвета фона в UITableViewHeaderFooterView устарела. Вместо этого установите пользовательский UIView с желаемым цветом фона для свойства backgroundView.

Итак, каков правильный ответ на вопрос? Есть невозможно ответ. Apple сделала здесь огромный шутник. Они предоставили метод, который позволяет вам зарегистрировать перо в качестве источника вашего UITableViewHeaderFooterView, но есть нет UITableViewHeaderFooterView в библиотеке объектов. Поэтому этот метод бесполезный. это невозможно чтобы правильно спроектировать UITableViewHeaderFooterView в перо.

Что ты может do, тем не менее, разрабатывает обычный UIView в пере, а затем в коде (в вашей реализации viewForHeaderInSection ), загрузите представление вручную из пера и вставьте его в contentView вашего заголовка.

Например, предположим, что мы хотим создать наш заголовок в наконечнике, и у нас есть метка в заголовке, к которой мы хотим подключить выход. lab . Затем нам понадобятся как настраиваемый класс заголовка, так и настраиваемый класс представления:

Мы регистрируем заголовки нашего представления класс, а не перо:

В представлении xib файла, мы объявляем наше представление MyHeaderViewContent не MyHeaderView.

В viewForHeaderInSection , мы вырываем вид из пера, вставляем его в contentView заголовка и настройте ссылку на него:

Это ужасно и раздражает, но это лучшее, что вы можете сделать.

У меня недостаточно репутации, чтобы добавить комментарий к ответу Мэтта.

В любом случае, единственное, чего здесь не хватает, - это удалить все подпредставления из UITableViewHeaderFooterView.contentView перед добавлением новых представлений. Это вернет повторно используемую ячейку в исходное состояние и предотвратит утечку памяти.

Создайте UITableViewHeaderFooterView и соответствующий ему файл xib.

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

Удалите из очереди и используйте аналогично ячейке. Идентификатор - это имя файла.

Читайте также: