Я хочу использовать представление для нескольких контроллеров представления в раскадровке. Таким образом, я подумал о разработке представления во внешнем xib, чтобы изменения отражались в каждом контроллере представления. Но как можно загрузить представление из внешнего xib в раскадровку и возможно ли это вообще? Если это не так, какие еще альтернативы доступны для решения данной ситуации?
ios
uiview
storyboard
xib
Себастьян Хоффманн
источник
источник
Ответы:
Мой полный пример здесь , но я дам краткое изложение ниже.
раскладка
Добавьте в проект файлы .swift и .xib с одинаковыми именами. Файл .xib содержит ваш пользовательский макет представления (желательно с использованием ограничений автоматического макета).
Сделайте swift-файл владельцем xib-файла.
Код
Добавьте следующий код в файл .swift и подключите выходы и действия из файла .xib.
Используй это
Используйте свой собственный вид в любом месте раскадровки. Просто добавьте
UIView
и установите имя класса для своего собственного имени класса.источник
init(frame:)
. См. Это руководство для получения более подробной информации.Какое-то время подход Кристофера Сваси был лучшим подходом, который я нашел. Я спросил об этом пару старших разработчиков моей команды, и у одного из них было идеальное решение ! Он удовлетворяет все проблемы, которые так красноречиво высказал Кристофер Свази, и не требует шаблонного кода подкласса (моя главная проблема с его подходом). Есть одна проблема , но в остальном она довольно интуитивно понятна и проста в реализации.
MyCustomClass.swift
MyCustomClass.xib
File's Owner
для файла .xib свой собственный класс (MyCustomClass
)class
значение (подidentity Inspector
) для вашего пользовательского представления в файле .xib пустым. Таким образом, в вашем пользовательском представлении не будет определенного класса, но будет указан владелец файла.Assistant Editor
.Connections Inspector
то заметите, что ваши ссылающиеся точки не ссылаются на ваш собственный класс (т.е.MyCustomClass
), а скорее ссылаютсяFile's Owner
. ПосколькуFile's Owner
указан ваш настраиваемый класс, розетки будут подключаться и работать должным образом.NibLoadable
протоколу, указанному ниже..swift
имя файла настраиваемого класса отличается от.xib
имени файла, установите в качествеnibName
свойства имя.xib
файла.required init?(coder aDecoder: NSCoder)
иoverride init(frame: CGRect)
вызовите,setupFromNib()
как в примере ниже.MyCustomClass
).Вот протокол, на который вы захотите сослаться:
А вот пример
MyCustomClass
реализации протокола (с именем файла .xibMyCustomClass.xib
):ПРИМЕЧАНИЕ: Если вы пропустите ошибку и установите
class
значение внутри вашего .xib файла как собственный класс, то он не будет отображаться в раскадровке, и вы получите сообщениеEXC_BAD_ACCESS
об ошибке при запуске приложения, потому что оно застревает в бесконечном цикле пытается инициализировать класс из пера, используяinit?(coder aDecoder: NSCoder)
метод, который затем вызываетSelf.nib.instantiate
иinit
снова вызывает .источник
setupFromNib()
, похоже, исправляет некоторые странные проблемы с автоматической компоновкой с ячейками представления таблицы с автоматическим изменением размера, содержащими представления, созданные XIB.@IBDesignable
совместимость. Не могу поверить, почему Xcode или UIKit не предоставляют что-то подобное по умолчанию при добавлении файла UIView.Предполагая, что вы создали xib, который хотите использовать:
1) Создайте собственный подкласс UIView (вы можете перейти в File -> New -> File ... -> Cocoa Touch Class. Убедитесь, что «Subclass of:» - это «UIView»).
2) Добавьте представление, основанное на xib, в качестве подвида к этому представлению при инициализации.
В Obj-C
В Swift 2
В Swift 3
3) Где бы вы ни хотели использовать его в раскадровке, добавьте UIView, как обычно, выберите вновь добавленное представление, перейдите в Инспектор идентичности (третий значок в правом верхнем углу, который выглядит как прямоугольник с линиями в нем), и введите имя вашего подкласса в поле «Класс» в поле «Пользовательский класс».
источник
xibView.frame = self.frame;
должно бытьxibView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
, иначе xibView будет иметь смещение при добавлении представления в раскадровку.Я всегда находил решение «добавить его как подпредставление» неудовлетворительным, видя, как оно связано с (1) автоматическим раскладыванием, (2)
@IBInspectable
и (3) выходами. Вместо этого позвольте мне познакомить вас с магиейawakeAfter:
, вNSObject
методе.awakeAfter
позволяет вам полностью заменить объект, фактически проснувшийся из NIB / Storyboard, другим объектом. Затем этот объект проходит процесс гидратации, вызываетawakeFromNib
его, добавляется в качестве представления и т. Д.Мы можем использовать это в подклассе «картонный вырез» нашего представления, единственной целью которого будет загрузка представления из NIB и его возврат для использования в раскадровке. Встраиваемый подкласс затем указывается в инспекторе идентичности представления Storyboard, а не в исходном классе. На самом деле он не обязательно должен быть подклассом, чтобы это работало, но именно создание подкласса позволяет IB видеть любые свойства IBInspectable / IBOutlet.
Этот дополнительный шаблон может показаться неоптимальным - и в некотором смысле так оно и есть, потому что в идеале
UIStoryboard
он справился бы с этим без проблем, - но у него есть то преимущество, что исходный NIB иUIView
подкласс не изменяются. Роль, которую он играет, в основном является ролью класса адаптера или моста и вполне допустима с точки зрения дизайна как дополнительный класс, даже если это достойно сожаления. С другой стороны, если вы предпочитаете быть экономным в своих классах, решение @ BenPatch работает путем реализации протокола с некоторыми другими незначительными изменениями. Вопрос о том, какое решение лучше, сводится к вопросу стиля программиста: предпочитает ли он композицию объектов или множественное наследование.Примечание: класс, установленный для представления в файле NIB, остается прежним. Встраиваемый подкласс используется только в раскадровке. Подкласс нельзя использовать для создания экземпляра представления в коде, поэтому он не должен иметь никакой дополнительной логики. Он должен содержать только
awakeAfter
крючок.⚠️ Единственным существенным недостатком здесь является то, что если вы определяете ограничения ширины, высоты или соотношения сторон в раскадровке, которые не относятся к другому виду, их придется скопировать вручную. Ограничения, которые связывают два представления, устанавливаются на ближайшем общем предке, а представления пробуждаются из раскадровки изнутри, поэтому к тому времени, когда эти ограничения будут гидратированы в супервизоре, обмен уже произошел. Ограничения, которые связаны только с рассматриваемым представлением, устанавливаются непосредственно на это представление и, таким образом, сбрасываются при замене, если они не копируются.
Обратите внимание, что здесь происходит то, что ограничения, установленные для представления в раскадровке , копируются во вновь созданное представление , которое может уже иметь собственные ограничения, определенные в его файле пера. На них это не влияет.
instantiateViewFromNib
является типобезопасным расширениемUIView
. Все, что он делает, - это перебирает объекты NIB, пока не найдет тот, который соответствует типу. Обратите внимание, что универсальный тип - это возвращаемое значение, поэтому тип должен быть указан на месте вызова.источник
instantiateViewFromNib
ничего не вернет. В любом случае это не имеет большого значения, ИМО, подкласс - это просто приспособление для подключения к раскадровке, весь код должен быть в исходном классе.MyCustomView
. В моем xib левая внутренняя боковая панель по умолчанию отсутствовала; Чтобы включить его, есть кнопка рядом с элементом управления чертами «Просмотреть как: iPhone 7» в нижней / левой части.Лучшее решение в настоящее время - просто использовать настраиваемый контроллер представления с его представлением, определенным в xib, и просто удалить свойство «представления», которое Xcode создает внутри раскадровки при добавлении к нему контроллера представления (не забудьте установить имя хотя пользовательский класс).
Это заставит среду выполнения автоматически искать xib и загружать его. Вы можете использовать этот трюк для любых видов контейнерных представлений или представления содержимого.
источник
Я думаю об
alternative
использованииXIB views
для использованияView Controller
в отдельной раскадровке .Затем в главном раскадровки вместо использования пользовательского зрения
container view
сEmbed Segue
и иметьStoryboardReference
к этому пользовательскому виду контроллера , который вид должен быть размещен внутри другого вида в главном окне редактора.Затем мы можем настроить делегирование и связь между этим встроенным ViewController и основным контроллером представления посредством подготовки к переходу . Этот подход отличается от отображения UIView, но его можно использовать гораздо проще и эффективнее (с точки зрения программирования) для достижения той же цели, т.е. иметь многоразовое настраиваемое представление, которое отображается в основной раскадровке.
Дополнительным преимуществом является то, что вы можете реализовать свою логику в классе CustomViewController и там настроить все делегирование и подготовку просмотра без создания отдельных (труднее найти в проекте) классов контроллеров и без размещения шаблонного кода в основном UIViewController с помощью Component. Я думаю, это хорошо для многоразовых компонентов, например. Компонент музыкального проигрывателя (вроде виджета), который можно встраивать в другие представления.
источник
Хотя самые популярные ответы работают нормально, концептуально они неверны. Все они используются в
File's owner
качестве связи между выходами класса и компонентами пользовательского интерфейса.File's owner
предполагается использовать только для объектов верхнего уровня, а неUIView
s. Ознакомьтесь с документом разработчика Apple . Наличие UIView asFile's owner
приводит к этим нежелательным последствиям.contentView
там, где должны использоватьself
. Это не только некрасиво, но и структурно неверно, потому что промежуточное представление не позволяет структуре данных передавать структуру пользовательского интерфейса. Это как противоположность декларативного пользовательского интерфейса.Есть элегантный способ сделать это без использования
File's owner
. Пожалуйста, проверьте это сообщение в блоге . В нем объясняется, как это делать правильно.источник
Вот ответ, который вы хотели все это время. Вы можете просто создать свой
CustomView
класс, иметь его главный экземпляр в xib со всеми вложенными представлениями и выходами. Затем вы можете применить этот класс к любым экземплярам в ваших раскадровках или других xib.Нет необходимости возиться с владельцем файла, подключать выходы к прокси-серверу, изменять xib особым образом или добавлять экземпляр своего пользовательского представления в качестве подвида самого себя.
Просто сделай это:
UIView
наNibView
(или сUITableViewCell
наNibTableViewCell
)Это оно!
Он даже работает с IBDesignable, чтобы ссылаться на ваше настраиваемое представление (включая подпредставления из xib) во время разработки в раскадровке.
Подробнее об этом можно прочитать здесь: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
И вы можете получить фреймворк BFWControls с открытым исходным кодом здесь: https://github.com/BareFeetWare/BFWControls
А вот простой фрагмент
NibReplaceable
кода, который его запускает, на случай, если вам интересно: https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4Том 👣
источник
Это решение можно использовать, даже если ваш класс не имеет того же имени, что и XIB. Например, если у вас есть базовый класс контроллера представления controllerA, который имеет имя XIB controllerA.xib, и вы подклассифицировали его с помощью controllerB и хотите создать экземпляр controllerB в раскадровке, вы можете:
*
источник
Решение для Objective-C в соответствии с шагами, описанными в ответе Бена Патча .
Используйте расширение для UIView:
Создание файлов
MyView.h
,MyView.m
иMyView.xib
.Во- первых подготовить ваш ,
MyView.xib
как ответ Бен Патча так говорит набор классовMyView
для владельца файла , а не основной вид внутри этой XIB.MyView.h
:MyView.m
:А позже просто создайте свое представление программно:
Предупреждение! Предварительный просмотр этого представления не будет отображаться в раскадровке, если вы используете расширение WatchKit из-за этой ошибки в Xcode> = 9.2: https://forums.developer.apple.com/thread/95616
источник