Как использовать одну раскадровку uiviewcontroller для нескольких подклассов
Допустим, у меня есть раскадровка, которая содержит UINavigationController
в качестве начального контроллера вида. Его корневой контроллер вида является подклассом UITableViewController
, которая составляет BasicViewController
. Он имеет IBAction
который подключен к правой навигационной кнопке панели навигации
Оттуда я хотел бы использовать раскадровку в качестве шаблона для других представлений без необходимости создавать дополнительные раскадровки. Скажем, эти представления будут иметь точно такой же интерфейс, но с корневым контроллером представления класса SpecificViewController1
и SpecificViewController2
, которые являются подклассами BasicViewController
.
Эти 2 контроллера вида будут иметь ту же функциональность и интерфейс, за исключением IBAction
метод.
Это было бы похоже на следующее:
@interface BasicViewController : UITableViewController
@interface SpecificViewController1 : BasicViewController
@interface SpecificViewController2 : BasicViewController
могу ли я сделать что-то подобное?
Могу ли я просто создать экземпляр раскадровки BasicViewController
но есть корневой контроллер представления в подкласс SpecificViewController1
и SpecificViewController2
?
Спасибо.
9 ответов:
большой вопрос - но, к сожалению, только слабый ответ. Я не верю, что в настоящее время можно сделать то, что вы предлагаете, потому что в UIStoryboard нет инициализаторов, которые позволяют переопределить контроллер представления, связанный с раскадровкой, как определено в деталях объекта в раскадровке при инициализации. При инициализации все элементы пользовательского интерфейса в stoaryboard связаны с их свойствами в контроллере представления.
Он будет по умолчанию инициализируйте с помощью контроллера вида, указанного в определении раскадровки.
Если вы пытаетесь получить повторное использование элементов пользовательского интерфейса, созданных в раскадровке, они все равно должны быть связаны или связаны со свойствами, в которых когда-либо контроллер представления использует их, чтобы они могли "рассказать" контроллеру представления о событиях.
Это не так уж и важно, копируя макет раскадровки, особенно если вам нужен только аналогичный дизайн для 3 видов, однако если вы сделайте, вы должны убедиться, что все предыдущие ассоциации очищены, или он получит сбои при попытке связаться с предыдущим контроллером представления. Вы сможете распознать их как сообщения об ошибках KVO в выводе журнала.
пару подходов вы могли бы взять:
хранить элементы пользовательского интерфейса в UIView-в файле xib и создать его экземпляр из базового класса и добавить его в качестве вложенного представления в главном представлении, как правило, самостоятельно.вид. Тогда вы бы просто используйте макет раскадровки С в основном пустыми контроллерами вида, занимающими свое место в раскадровке, но с правильным подклассом контроллера вида, назначенным им. Поскольку они унаследуют от базы, они получат этот вид.
создайте макет в коде и установите его с помощью контроллера базового вида. Очевидно, что этот подход побеждает цель использования раскадровки, но может быть способ пойти в вашем случае. Если у вас есть другие части приложения, которые было бы полезно использовать подход раскадровки, это нормально отклоняться здесь и там, если это уместно. В этом случае, как и выше, вы просто используете контроллеры банковских представлений с назначенным подклассом и позволяете контроллеру базового представления устанавливать пользовательский интерфейс.
было бы неплохо, если бы Apple придумала способ сделать то, что вы предлагаете, но проблема наличия графических элементов, предварительно связанных с подклассом контроллера, все равно будет проблемой.
хорошего Нового года!! быть ну
код строки, которую мы ищем:
object_setClass(AnyObject!, AnyClass!)
в раскадровке - > добавить UIViewController дать ему имя класса ParentVC.
class ParentVC: UIViewController { var type: Int? override func awakeFromNib() { if type = 0 { object_setClass(self, ChildVC1.self) } if type = 1 { object_setClass(self, ChildVC2.self) } } override func viewDidLoad() { } } class ChildVC1: ParentVC { override func viewDidLoad() { super.viewDidLoad() println(type) // Console prints out 0 } } class ChildVC2: ParentVC { override func viewDidLoad() { super.viewDidLoad() println(type) // Console prints out 1 } }
как говорится в принятом ответе, это не похоже на то, что можно сделать с раскадровками.
мое решение-использовать Nib-так же, как разработчики использовали их до раскадровки. если вы хотите иметь многоразовый, подклассный контроллер представления (или даже представление), моя рекомендация-использовать Nibs.
SubclassMyViewController *myViewController = [[SubclassMyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
когда вы подключаете все свои розетки к "владельцу файла" в
MyViewController.xib
вы не указываете, какой класс перо должно быть загружено, вы просто указание пар ключ-значение:"это представление должно быть связано с именем переменной экземпляра."При звонке[SubclassMyViewController alloc] initWithNibName:
процесс инициализации определяет, какой контроллер представления будет использоваться для "управления" вид, который вы создали в Сиб.
можно иметь раскадровку инстанцировать различные подклассы пользовательского контроллера вида, хотя это включает в себя несколько неортодоксальный метод: переопределение
alloc
метод контроллера представления. При создании пользовательского контроллера представления переопределенный метод alloc фактически возвращает результат выполненияalloc
на подкласс.я должен предварить ответ с условием, что, хотя я проверил его в различных сценариях и не получил ошибок, я не могу гарантировать, что он справится с более сложными настройками (но я не вижу причин, почему он не должен работать). Кроме того, я не представил никаких приложений, использующих этот метод, поэтому есть внешний шанс, что он может быть отклонен процессом обзора Apple (хотя опять же я не вижу причин, почему это должно быть).
для демонстрационных целей, у меня есть подкласс
UIViewController
под названиемTestViewController
, который имеет UILabel IBOutlet и IBAction. В моей раскадровке я добавил контроллер вида и исправил его класс доTestViewController
, и подключил IBOutlet к UILabel и IBAction к UIButton. Я представляю TestViewController через модальный сегмент, вызванный UIButton на предыдущем viewController.чтобы контролировать, какой класс создается, я добавил статическую переменную и связанные с ней методы класса, чтобы получить / установить подкласс, который будет использоваться (я думаю, можно было бы принять другие способы определения того, какой подкласс должен быть instantiated):
TestViewController.м:
#import "TestViewController.h" @interface TestViewController () @end @implementation TestViewController static NSString *_classForStoryboard; +(NSString *)classForStoryboard { return [_classForStoryboard copy]; } +(void)setClassForStoryBoard:(NSString *)classString { if ([NSClassFromString(classString) isSubclassOfClass:[self class]]) { _classForStoryboard = [classString copy]; } else { NSLog(@"Warning: %@ is not a subclass of %@, reverting to base class", classString, NSStringFromClass([self class])); _classForStoryboard = nil; } } +(instancetype)alloc { if (_classForStoryboard == nil) { return [super alloc]; } else { if (NSClassFromString(_classForStoryboard) != [self class]) { TestViewController *subclassedVC = [NSClassFromString(_classForStoryboard) alloc]; return subclassedVC; } else { return [super alloc]; } } }
для моего теста у меня есть два подкласса
TestViewController
:RedTestViewController
иGreenTestViewController
. Каждый подкласс имеет дополнительные свойства и каждое переопределениеviewDidLoad
чтобы изменить цвет фона представления и обновить текст UILabel IBOutlet:RedTestViewController.м:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor redColor]; self.testLabel.text = @"Set by RedTestVC"; }
GreenTestViewController.м:
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor greenColor]; self.testLabel.text = @"Set by GreenTestVC"; }
в некоторых случаях я могу захотеть создать экземпляр
TestViewController
сам, в других случаяхRedTestViewController
илиGreenTestViewController
. В предыдущем контроллере представления я делаю это случайным образом следующим образом:NSInteger vcIndex = arc4random_uniform(4); if (vcIndex == 0) { NSLog(@"Chose TestVC"); [TestViewController setClassForStoryBoard:@"TestViewController"]; } else if (vcIndex == 1) { NSLog(@"Chose RedVC"); [TestViewController setClassForStoryBoard:@"RedTestViewController"]; } else if (vcIndex == 2) { NSLog(@"Chose BlueVC"); [TestViewController setClassForStoryBoard:@"BlueTestViewController"]; } else { NSLog(@"Chose GreenVC"); [TestViewController setClassForStoryBoard:@"GreenTestViewController"]; }
отметим, что
setClassForStoryBoard
метод проверяет, что запрошенное имя класса действительно является подклассом TestViewController, чтобы избежать каких-либо путаниц. Ссылка выше наBlueTestViewController
есть ли возможность проверить эту функциональность.
попробуйте это, после instantiateViewControllerWithIdentifier.
- (void)setClass:(Class)c { object_setClass(self, c); }
как :
SubViewController *vc = [sb instantiateViewControllerWithIdentifier:@"MainViewController"]; [vc setClass:[SubViewController class]];
хотя это не строго подкласс, вы можете:
- опции-перетащите контроллер представления базового класса в контур документа, чтобы сделать копию
- переместить новую копию контроллера вида в отдельное место на раскадровке
- изменить класс к контроллеру представления подкласса в Инспекторе идентификации
вот пример блок учебник я написал, подклассы
ViewController
сWhiskeyViewController
:Это позволяет создавать подклассы подклассы вид контроллера в раскадровке. Затем вы можете использовать
instantiateViewControllerWithIdentifier:
для создания конкретных подклассов.этот подход немного негибкий: более поздние модификации в раскадровке контроллера базового класса не распространяются на подкласс. Если у вас есть много подклассов вы можете быть лучше с одним из других решений, но это будет делать в крайнем случае.
Objc_setclass метод не создает экземпляр childvc. Но в то время как выскакивают из childvc, deinit childvc вызывается. Поскольку нет памяти, выделенной отдельно для childvc, приложение аварийно завершает работу. Basecontroller имеет экземпляр , в то время как дочерний vc не имеет.
вероятно, наиболее гибким способом является использование многоразовых представлений.
(создать представление в отдельном файле XIB или
Container view
и добавьте его в каждую сцену контроллера вида подкласса в раскадровке)
если вы не слишком полагаетесь на раскадровки, вы можете создать отдельный .xib файл для контроллера.
установите владельца соответствующего файла и выходы в
MainViewController
и заменитьinit(nibName:bundle:)
В основном VC, чтобы его дети могли получить доступ к одному и тому же наконечнику и его выходам.ваш код должен выглядеть так:
class MainViewController: UIViewController { @IBOutlet weak var button: UIButton! override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: "MainViewController", bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() button.tintColor = .red } }
и ваш ребенок VC сможет повторно использовать перо своего родителя:
class ChildViewController: MainViewController { override func viewDidLoad() { super.viewDidLoad() button.tintColor = .blue } }