Скажем, у меня есть раскадровка, которая содержит UINavigationController
начальный контроллер представления. Его корневой контроллер представления является подклассом UITableViewController
, который есть BasicViewController
. Он IBAction
связан с правой кнопкой навигации на панели навигации.
Оттуда я хотел бы использовать раскадровку в качестве шаблона для других представлений без необходимости создавать дополнительные раскадровки. Скажем , эти представления будут иметь точно такой же интерфейс , но с видом на корневой контроллер класса SpecificViewController1
и SpecificViewController2
которые являются подклассами BasicViewController
.
Эти 2 контроллера представления будут иметь одинаковые функциональные возможности и интерфейс, за исключением IBAction
метода.
Это было бы примерно так:
@interface BasicViewController : UITableViewController
@interface SpecificViewController1 : BasicViewController
@interface SpecificViewController2 : BasicViewController
Могу я сделать что-нибудь подобное?
Могу ли я просто создать экземпляр раскадровки, BasicViewController
но иметь контроллер корневого представления для подкласса SpecificViewController1
и SpecificViewController2
?
Спасибо.
Ответы:
отличный вопрос - но, к сожалению, неубедительный ответ. Я не верю, что в настоящее время возможно сделать то, что вы предлагаете, потому что в UIStoryboard нет инициализаторов, которые позволяют переопределить контроллер представления, связанный с раскадровкой, как определено в деталях объекта в раскадровке при инициализации. Именно при инициализации все элементы пользовательского интерфейса в Stoaryboard связаны со своими свойствами в контроллере представления.
По умолчанию он инициализируется с помощью контроллера представления, указанного в определении раскадровки.
Если вы пытаетесь повторно использовать элементы пользовательского интерфейса, созданные вами в раскадровке, они по-прежнему должны быть связаны или связаны со свойствами, в которых когда-либо контроллер представления использует их, чтобы они могли «сообщать» контроллеру представления о событиях.
Копирование макета раскадровки не представляет особого труда, особенно если вам нужен аналогичный дизайн только для 3 представлений, однако, если вы это сделаете, вы должны убедиться, что все предыдущие ассоциации очищены, иначе он будет вылетать при попытке для связи с предыдущим контроллером представления. Вы сможете распознать их как сообщения об ошибках KVO в выводе журнала.
Вот несколько возможных подходов:
сохраните элементы пользовательского интерфейса в UIView - в файле xib, создайте его экземпляр из своего базового класса и добавьте его как вспомогательное представление в основном представлении, обычно self.view. Затем вы просто использовали бы макет раскадровки с в основном пустыми контроллерами представления, занимающими свое место в раскадровке, но с назначенным им подклассом правильного контроллера представления. Поскольку они унаследуют от базы, они получат это представление.
создайте макет в коде и установите его из контроллера базового представления. Очевидно, что такой подход лишает смысла использование раскадровки, но может быть подходящим вариантом в вашем случае. Если у вас есть другие части приложения, которым может быть полезен подход раскадровки, можно при необходимости отклоняться здесь и там. В этом случае, как и выше, вы должны просто использовать контроллеры банковского представления с назначенным подклассом и позволить контроллеру базового представления установить пользовательский интерфейс.
Было бы неплохо, если бы Apple придумала способ сделать то, что вы предлагаете, но проблема наличия графических элементов, предварительно связанных с подклассом контроллера, по-прежнему остается проблемой.
удачного Нового года !! всего хорошего
источник
Код строки, которую мы ищем:
В Storyboard -> добавьте UIViewController, дайте ему имя класса ParentVC.
источник
class func instantiate() -> SubClass { let instance = (UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("SuperClass") as? SuperClass)! object_setClass(instance, SubClass.self) return (instance as? SubClass)! }
self
не меняется. Таким образом, проверка объекта (например, чтение значений _ivar / property) послеobject_setClass
может вызвать сбои.Как говорится в принятом ответе, это не похоже на раскадровку.
Мое решение - использовать Nib - точно так же, как разработчики использовали их до раскадровки. Если вы хотите иметь многократно используемый подклассируемый контроллер представления (или даже представление), я рекомендую использовать перья.
Когда вы подключаете все свои выходы к «Владельцу файла»,
MyViewController.xib
вы НЕ указываете, какой класс должен быть загружен в Nib, вы просто указываете пары ключ-значение: « это представление должно быть связано с этим именем переменной экземпляра ». При вызове[SubclassMyViewController alloc] initWithNibName:
процесса инициализации указывается, какой контроллер представления будет использоваться для « управления » представлением, созданным вами в пике.источник
Можно использовать раскадровку для создания экземпляров различных подклассов настраиваемого контроллера представления, хотя для этого используется несколько неортодоксальный прием: переопределение
alloc
метода для контроллера представления. Когда создается настраиваемый контроллер представления, переопределенный метод выделения фактически возвращает результат работыalloc
в подклассе.Я должен предварять ответ с оговоркой, что, хотя я тестировал его в различных сценариях и не получил ошибок, я не могу гарантировать, что он справится с более сложными настройками (но я не вижу причин, по которым это не должно работать) , Кроме того, я не отправлял никаких приложений, использующих этот метод, поэтому существует вероятность того, что он может быть отклонен процессом проверки Apple (хотя опять же, я не вижу причин, по которым это должно быть).
В демонстрационных целях у меня есть подкласс
UIViewController
calledTestViewController
, в котором есть UILabel IBOutlet и IBAction. В моей раскадровке я добавил контроллер представления, изменил его классTestViewController
и подключил IBOutlet к UILabel, а IBAction - к UIButton. Я представляю TestViewController в виде модального перехода, запускаемого UIButton на предыдущем viewController.Чтобы контролировать, какой класс создается, я добавил статическую переменную и связанные методы класса, чтобы получить / установить подкласс, который будет использоваться (я думаю, можно было бы использовать другие способы определения, какой подкласс должен быть создан):
TestViewController.m:
Для моего теста у меня есть два подкласса
TestViewController
:RedTestViewController
иGreenTestViewController
. Каждый подкласс имеет дополнительные свойства и каждое переопределениеviewDidLoad
для изменения цвета фона представления и обновления текста UILabel IBOutlet:RedTestViewController.m:
GreenTestViewController.m:
В некоторых случаях я мог бы захотеть создать
TestViewController
сам, в других случаяхRedTestViewController
илиGreenTestViewController
. В предыдущем контроллере представления я делаю это случайным образом следующим образом:Обратите внимание, что
setClassForStoryBoard
метод проверяет, действительно ли запрошенное имя класса является подклассом TestViewController, чтобы избежать путаницы. Ссылка вышеBlueTestViewController
предназначена для тестирования этой функциональности.источник
попробуйте это после instantiateViewControllerWithIdentifier.
лайк :
источник
EXC_BAD_ACCESS
, поэтому не рекомендую это делать.init
тоже не назовут. Такие ограничения делают непригодным для использования любой подход.Основываясь, в частности, на ответах nickgzzjr и Jiří Zahálka плюс комментарий под вторым из CocoaBob, я подготовил короткий общий метод, делающий именно то, что нужно OP. Вам нужно только проверить имя раскадровки и идентификатор раскадровки View Controllers
Дополнительные параметры добавлены, чтобы избежать принудительного развертывания (предупреждения swiftlint), но метод возвращает правильные объекты.
источник
Хотя это не совсем подкласс, вы можете:
Вот пример из учебника по Bloc, который я написал,
ViewController
с подклассомWhiskeyViewController
:Это позволяет вам создавать подклассы подклассов контроллера представления в раскадровке. Затем вы можете использовать его
instantiateViewControllerWithIdentifier:
для создания определенных подклассов.Этот подход немного негибкий: более поздние изменения в раскадровке контроллера базового класса не распространяются на подкласс. Если у вас много подклассов, вам может быть лучше с одним из других решений, но в крайнем случае это подойдет.
источник
initWithCoder:
, не имеют унаследованных отношений. Этот тип отношений не поддерживается файлами раскадровки.Метод Objc_setclass не создает экземпляр childvc. Но при выходе из childvc вызывается deinit childvc. Поскольку для childvc не выделяется отдельная память, приложение вылетает. У Basecontroller есть экземпляр, а у дочернего vc нет.
источник
Если вы не слишком полагаетесь на раскадровки, вы можете создать отдельный файл .xib для контроллера.
Установите владельца и выходы соответствующего файла на
MainViewController
и переопределитеinit(nibName:bundle:)
в главном VC, чтобы его дочерние элементы могли получить доступ к тому же самому наконечнику и его выходам.Ваш код должен выглядеть так:
И ваш Дочерний VC сможет повторно использовать перо своего родителя:
источник
Взяв ответы оттуда и там, я придумал это изящное решение.
Создайте родительский контроллер представления с этой функцией.
Это позволяет компилятору гарантировать, что дочерний контроллер представления наследуется от контроллера родительского представления.
Затем, когда вы захотите перейти к этому контроллеру, используя подкласс, вы можете сделать:
Интересно то, что вы можете добавить ссылку на раскадровку на себя, а затем продолжать вызывать «следующий» дочерний контроллер представления.
источник
Вероятно, наиболее гибкий способ - использовать повторно используемые представления.
(Создайте представление в отдельном файле XIB или
Container view
добавьте его в каждую сцену контроллера представления подкласса в раскадровке)источник
Есть простое, очевидное повседневное решение.
Просто поместите существующую раскадровку / контроллер в новую раскадровку / контроллер. IE как контейнерное представление.
Это в точности аналогичная концепция «подкласса» для контроллеров представления.
Все работает точно так же, как в подклассе.
Так же, как вы обычно помещаете подвид представления в другое представление , естественно, вы обычно помещаете контроллер представления в другой контроллер представления .
Как еще ты мог это сделать?
Это базовая часть iOS, такая же простая, как понятие «subview».
Это так просто ...
Теперь вы, очевидно, должны
list
делать все, что хотите, си т. д. и т. д.
Представления контейнера «точно такие же» подклассы точно так же, как «подпредставления» «точно такие же» подклассы.
Конечно, очевидно, что нельзя «подклассифицировать макет» - что бы это вообще значило?
(«Создание подклассов» относится к объектно-ориентированному программному обеспечению и не имеет отношения к «макетам».)
Очевидно, что когда вы хотите повторно использовать представление, вы просто просматриваете его внутри другого представления.
Если вы хотите повторно использовать макет контроллера, вы просто просматриваете его внутри другого контроллера.
Это как самый простой механизм iOS !!
Примечание. В течение многих лет было тривиально динамически загружать другой контроллер представления в качестве представления контейнера. Объяснено в последнем разделе: https://stackoverflow.com/a/23403979/294884
Примечание. "_Sb" - это просто очевидный макрос, который мы используем для экономии ввода,
источник
Спасибо за вдохновляющий ответ @ Jiří Zahálka, я ответил на свое решение 4 года назад здесь , но @Sayka предложил мне опубликовать его в качестве ответа, так что вот оно.
Обычно в своих проектах, если я использую Storyboard для подкласса UIViewController, я всегда подготавливаю статический метод, вызываемый
instantiate()
в этом подклассе, чтобы легко создать экземпляр из Storyboard. Итак, для решения вопроса OP, если мы хотим использовать одну и ту же раскадровку для разных подклассов, мы можем просто использоватьsetClass()
этот экземпляр, прежде чем возвращать его.источник
Комментарий Cocoabob от ответа Йиржи Захальки помог мне получить это решение, и оно сработало.
источник