Como usar um controlador de imagem de storyboard único para várias subclasss

Vamos dizer que eu tenho um storyboard que contém UINavigationController como controlador de exibição inicial. Seu controlador de visualização raiz é uma subclass de UITableViewController , que é BasicViewController . Tem IBAction que está ligado ao botão direito de navegação da barra de navegação

De lá, gostaria de usar o storyboard como um modelo para outras visualizações sem precisar criar storyboards adicionais. Digamos que essas exibições terão exatamente a mesma interface, mas com o controlador de visualização raiz da class SpecificViewController1 e SpecificViewController2 que são subclasss de BasicViewController .
Esses dois controladores de visualização teriam a mesma funcionalidade e interface, exceto pelo método IBAction .
Seria como o seguinte:

 @interface BasicViewController : UITableViewController @interface SpecificViewController1 : BasicViewController @interface SpecificViewController2 : BasicViewController 

Posso fazer algo assim?
Posso apenas instanciar o storyboard do BasicViewController mas tenho o controlador de visualização raiz para subclass SpecificViewController1 e SpecificViewController2 ?

Obrigado.

ótima pergunta – mas infelizmente apenas uma resposta fraca. Não acredito que seja atualmente possível fazer o que você propõe porque não há inicializadores no UIStoryboard que permitam replace o controlador de visualização associado ao storyboard, conforme definido nos detalhes do object no storyboard na boot. É na boot que todos os elementos da interface do usuário no stoaryboard são vinculados a suas propriedades no controlador de visualização.

Por padrão, ele será inicializado com o controlador de visualização especificado na definição do storyboard.

Se você estiver tentando obter a reutilização dos elementos da interface do usuário que criou no storyboard, eles ainda precisarão estar vinculados ou associados a propriedades nas quais o controlador de exibição sempre os está usando para poder “informar” o controlador de visualização sobre events.

Não é muito importante copiar um layout de storyboard, especialmente se você precisar apenas de um design semelhante para 3 visualizações, mas, se fizer isso, deverá certificar-se de que todas as associações anteriores estejam limpas ou haverá falhas quando tentar para se comunicar com o controlador de visualização anterior. Você será capaz de reconhecê-los como mensagens de erro KVO na saída do log.

Um par de abordagens que você poderia tomar:

  • armazenar os elementos da interface do usuário em um UIView – em um arquivo xib e instanciá-lo da sua class base e adicioná-lo como uma subvisualização na visualização principal, geralmente self.view. Então você simplesmente usaria o layout do storyboard com controladores de visualização em branco, mantendo seu lugar no storyboard, mas com a subclass de controlador de visualização correta atribuída a eles. Como herdariam da base, teriam essa visão.

  • crie o layout no código e instale-o a partir do controlador de visualização base. Obviamente, esta abordagem derrota o propósito de usar o storyboard, mas pode ser o caminho a percorrer no seu caso. Se você tiver outras partes do aplicativo que se beneficiariam da abordagem de storyboard, não há problema em desviar aqui e ali, se apropriado. Nesse caso, como acima, você usaria apenas os controladores de visualização de banco com sua subclass designada e permitiria que o controlador de visualização de base instalasse a interface do usuário.

Seria bom se a Apple tivesse uma maneira de fazer o que você propõe, mas a questão de ter os elementos charts pré-vinculados à subclass do controlador ainda seria um problema.

tenha um ótimo Ano Novo !! fique bem

O código de linha que estamos procurando é:

 object_setClass(AnyObject!, AnyClass!) 

No Storyboard -> adicione UIViewController, dê a ele um nome de class 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 } } 

Como afirma a resposta aceita, não parece que seja possível fazer com storyboards.

Minha solução é usar o Nib – assim como os desenvolvedores usaram antes dos storyboards. Se você deseja ter um controlador de visualização reutilizável e subclassável (ou mesmo uma visualização), minha recomendação é usar Nibs.

 SubclassMyViewController *myViewController = [[SubclassMyViewController alloc] initWithNibName:@"MyViewController" bundle:nil]; 

Quando você conecta todas as suas saídas ao “Proprietário do Arquivo” no MyViewController.xib você NÃO está especificando em qual class o NIB deve ser carregado, você está apenas especificando pares de valores-chave: ” esta visão deve ser conectada a este nome de variável de instância ” Ao chamar [SubclassMyViewController alloc] initWithNibName: o processo de boot especifica qual view controller será usado para ” controlar ” a view criada na ponta.

É possível fazer com que um storyboard instancie diferentes subclasss de um controlador de visualização personalizado, embora envolva uma técnica pouco ortodoxa: substituindo o método de alloc para o controlador de visualização. Quando o controlador de exibição personalizado é criado, o método de alocação substituído retorna o resultado da alloc em execução na subclass.

Eu deveria começar a resposta com a ressalva de que, embora eu tenha testado em vários cenários e não tenha recebido nenhum erro, não posso garantir que ele irá lidar com configurações mais complexas (mas não vejo razão para que não funcione) . Além disso, eu não enviei nenhum aplicativo usando esse método, então há a chance de que ele seja rejeitado pelo processo de revisão da Apple (embora, novamente, eu não veja nenhuma razão para isso).

Para fins de demonstração, eu tenho uma subclass de UIViewController chamada TestViewController , que tem um UILabel IBOutlet e um IBAction. No meu storyboard, adicionei um controlador de visualização e TestViewController sua class para TestViewController , e conectei o IBOutlet a um UILabel e o IBAction a um UIButton. Apresento o TestViewController por meio de um acompanhamento modal acionado por um UIButton no viewController anterior.

Imagem de storyboard

Para controlar qual class é instanciada, eu adicionei uma variável estática e methods de class associados para obter / definir a subclass a ser usada (eu acho que podemos adotar outras formas de determinar qual subclass será instanciada):

TestViewController.m:

 #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 *subclassdVC = [NSClassFromString(_classForStoryboard) alloc]; return subclassdVC; } else { return [super alloc]; } } } 

Para o meu teste eu tenho duas subclasss de TestViewController : RedTestViewController e GreenTestViewController . Cada uma das subclasss possui propriedades adicionais e cada uma substitui viewDidLoad para alterar a cor de fundo da visualização e atualizar o texto do IBOutlet da UILabel:

RedTestViewController.m:

 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor redColor]; self.testLabel.text = @"Set by RedTestVC"; } 

GreenTestViewController.m:

 - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor greenColor]; self.testLabel.text = @"Set by GreenTestVC"; } 

Em algumas ocasiões, talvez eu queira instanciar o próprio TestViewController , em outras ocasiões, RedTestViewController ou GreenTestViewController . No controlador de visualização anterior, faço isso aleatoriamente da seguinte maneira:

 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"]; } 

Observe que o método setClassForStoryBoard verifica se o nome da class solicitado é, de fato, uma subclass de TestViewController, para evitar misturas. A referência acima para o BlueTestViewController está lá para testar essa funcionalidade.

tente isso, após instanciadoControllerWithIdentifier.

 - (void)setClass:(Class)c { object_setClass(self, c); } 

gostar :

 SubViewController *vc = [sb instantiateViewControllerWithIdentifier:@"MainViewController"]; [vc setClass:[SubViewController class]]; 

Embora não seja estritamente uma subclass, você pode:

  1. opção -drag o controlador de visualização de class base no Document Outline para fazer uma cópia
  2. Mova a nova cópia do controlador de visualizações para um local separado no storyboard
  3. Alterar class para o controlador de subclass no Inspetor de identidade

Aqui está um exemplo de um tutorial do Bloc que eu escrevi, subclassificando o ViewController com o WhiskeyViewController :

animação dos três passos acima

Isso permite criar subclasss de subclasss do controlador de visualização no storyboard. Você pode então usar o instantiateViewControllerWithIdentifier: para criar subclasss específicas.

Essa abordagem é um pouco inflexível: modificações posteriores no storyboard para o controlador da class base não se propagam para a subclass. Se você tem muitas subclasss, você pode estar melhor com uma das outras soluções, mas isso será fácil.

O método Objc_setclass não cria uma instância de childvc. Mas apesar de sair de childvc, a deinit de childvc está sendo chamada. Como não há memory alocada separadamente para childvc, o aplicativo trava. Basecontroller tem uma instância, enquanto child vc não possui.

Provavelmente, a maneira mais flexível é usar modos de exibição reutilizáveis.

(Crie uma Visualização em um arquivo XIB separado ou uma Container view e adicione-a a cada cena de controlador de subclass no storyboard)

Se você não for muito dependente de storyboards, poderá criar um arquivo .xib separado para o controlador.

Defina o proprietário e as saídas do arquivo apropriado para o MainViewController e substitua o init(nibName:bundle:) no VC principal para que seus filhos possam acessar a mesma ponta e suas saídas.

Seu código deve ficar assim:

 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 } } 

E seu Child VC poderá reutilizar o bico de seus pais:

 class ChildViewController: MainViewController { override func viewDidLoad() { super.viewDidLoad() button.tintColor = .blue } }