Eu quero usar uma exibição em vários controladores de exibição em um storyboard. Assim, pensei em projetar a visualização em um xib externo para que as mudanças sejam refletidas em todos os controladores de visualização. Mas como carregar uma visualização de um xib externo em um storyboard e isso é possível? Se não for esse o caso, que outras alternativas estão disponíveis para se adequar à situação acima?
ios
uiview
storyboard
xib
Sebastian Hoffmann
fonte
fonte
Respostas:
Meu exemplo completo está aqui , mas fornecerei um resumo abaixo.
Layout
Adicione um arquivo .swift e .xib, cada um com o mesmo nome ao seu projeto. O arquivo .xib contém seu layout de exibição personalizado (usando restrições de layout automático, de preferência).
Torne o arquivo swift o proprietário do arquivo xib.
Código
Adicione o seguinte código ao arquivo .swift e conecte as saídas e ações do arquivo .xib.
Use-o
Use sua visualização personalizada em qualquer lugar do seu storyboard. Basta adicionar a
UIView
e definir o nome da classe como seu nome personalizado.fonte
init(frame:)
. Veja este tutorial para mais detalhes.Por um tempo, a abordagem de Christopher Swasey foi a melhor abordagem que eu havia encontrado. Perguntei a alguns dos desenvolvedores seniores da minha equipe sobre isso e um deles tinha a solução perfeita ! Satisfaz todas as preocupações que Christopher Swasey abordou de forma tão eloquente e não exige código de subclasse padrão (minha principal preocupação com sua abordagem). Há uma pegadinha , mas, além disso, é bastante intuitiva e fácil de implementar.
MyCustomClass.swift
MyCustomClass.xib
File's Owner
arquivo .xib como sua classe personalizada (MyCustomClass
)class
valor (sobidentity Inspector
) para sua visualização personalizada no arquivo .xib em branco. Portanto, sua visualização personalizada não terá classe especificada, mas terá o Proprietário de um arquivo especificado.Assistant Editor
.Connections Inspector
, notará que suas Referências não fazem referência à sua classe personalizada (ieMyCustomClass
), mas sim à referênciaFile's Owner
. ComoFile's Owner
é especificada como sua classe personalizada, as tomadas serão conectadas e funcionarão.NibLoadable
protocolo mencionado abaixo..swift
nome do arquivo de classe personalizado for diferente do.xib
nome do arquivo, defina anibName
propriedade como o nome do.xib
arquivo.required init?(coder aDecoder: NSCoder)
eoverride init(frame: CGRect)
liguesetupFromNib()
como no exemplo abaixo.MyCustomClass
).Aqui está o protocolo que você deseja referenciar:
E aqui está um exemplo
MyCustomClass
disso que implementa o protocolo (com o nome do arquivo .xibMyCustomClass.xib
):OBSERVAÇÃO: se você errar o Gotcha e definir o
class
valor no arquivo .xib como sua classe personalizada, ele não será desenhado no storyboard e você receberá umEXC_BAD_ACCESS
erro ao executar o aplicativo, pois ele fica preso em um loop infinito de tentando inicializar a classe da ponta usando oinit?(coder aDecoder: NSCoder)
método que chamaSelf.nib.instantiate
e chamainit
novamente.fonte
setupFromNib()
, parece corrigir alguns problemas estranhos de layout automático com células de exibição de tabela de dimensionamento automático que contêm exibições criadas por XIB.@IBDesignable
compatibilidade. Não consigo acreditar por que o Xcode ou o UIKit não fornece algo como esse por padrão ao adicionar um arquivo UIView.Supondo que você tenha criado um xib que deseja usar:
1) Crie uma subclasse personalizada do UIView (você pode ir para Arquivo -> Novo -> Arquivo ... -> Cocoa Touch Class. Verifique se "Subclasse de:" é "UIView").
2) Adicione uma visualização baseada no xib como uma subvisualização a esta visualização na inicialização.
No Obj-C
Em Swift 2
Em Swift 3
3) Onde você quiser usá-lo em seu storyboard, adicione uma UIView como faria normalmente, selecione a visualização recém-adicionada, vá para o Identity Inspector (o terceiro ícone no canto superior direito que parece um retângulo com linhas), e digite o nome da sua subclasse como "Classe" em "Classe personalizada".
fonte
xibView.frame = self.frame;
deve serxibView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
, caso contrário, o xibView terá um deslocamento quando a visualização for adicionada ao storyboard.Eu sempre achei a solução "adicione-a como uma subvisão" insatisfatória, pois ela se aperta com (1) autolayout, (2)
@IBInspectable
e (3) tomadas. Em vez disso, deixe-me apresentar-lhe a magia deawakeAfter:
umNSObject
método.awakeAfter
permite trocar o objeto realmente acordado de um NIB / Storyboard por um objeto completamente diferente. Esse objeto é então submetido ao processo de hidratação,awakeFromNib
chamado, adicionado como uma vista, etc.Podemos usar isso em uma subclasse "recorte de papelão" de nossa visão, cujo único objetivo será carregar a visão do NIB e devolvê-la para uso no Storyboard. A subclasse incorporável é especificada no inspetor de identidade da exibição Storyboard, em vez da classe original. Na verdade, ele não precisa ser uma subclasse para que isso funcione, mas torná-la uma subclasse é o que permite ao IB ver quaisquer propriedades do IBInspectable / IBOutlet.
Esse padrão adicional pode parecer abaixo do ideal - e, em certo sentido, é, porque o ideal
UIStoryboard
seria lidar com isso sem problemas -, mas tem a vantagem de deixar o NIB original e aUIView
subclasse completamente inalterada. O papel que desempenha é basicamente o de uma classe de adaptador ou ponte e é perfeitamente válido, em termos de design, como uma classe adicional, mesmo que seja lamentável. Por outro lado, se você preferir ser parcimonioso com suas classes, a solução do @ BenPatch funciona implementando um protocolo com algumas outras pequenas alterações. A questão de qual solução é melhor se resume a uma questão de estilo de programador: se prefere a composição do objeto ou a herança múltipla.Nota: a classe configurada na visualização no arquivo NIB permanece a mesma. A subclasse incorporável é usada apenas no storyboard. A subclasse não pode ser usada para instanciar a visualização no código; portanto, ela não deve ter nenhuma lógica adicional. Deve conter apenas o
awakeAfter
gancho.⚠️ A única desvantagem aqui é que, se você definir restrições de largura, altura ou proporção no storyboard que não se relacionam a outra vista, elas deverão ser copiadas manualmente. As restrições que relacionam duas visualizações são instaladas no ancestral comum mais próximo e as visualizações são despertadas do storyboard de dentro para fora; portanto, quando essas restrições são hidratadas na superview, a troca já ocorreu. As restrições que envolvem apenas a visualização em questão são instaladas diretamente nessa visualização e, portanto, são lançadas quando a troca ocorre, a menos que sejam copiadas.
Observe que o que está acontecendo aqui é que as restrições instaladas na exibição no storyboard são copiadas para a exibição recém-instanciada , que já pode ter restrições próprias, definidas em seu arquivo de ponta. Aqueles não são afetados.
instantiateViewFromNib
é uma extensão de tipo seguro paraUIView
. Tudo o que faz é percorrer os objetos do NIB até encontrar um que corresponda ao tipo. Observe que o tipo genérico é o valor de retorno , portanto, o tipo deve ser especificado no site de chamada.fonte
instantiateViewFromNib
não vai retornar nada. Não é grande coisa de qualquer maneira, como a IMO, a subclasse é apenas um artifício para se conectar ao storyboard, todo o código deve estar na classe original.MyCustomView
. No meu xib, a barra lateral esquerda estava ausente por padrão; para ativá-lo, há um botão ao lado do controle "Visualizar como: iPhone 7" próximo ao lado inferior / esquerdo.Atualmente, a melhor solução é usar apenas um controlador de visualização personalizado com sua visualização definida em um xib e simplesmente excluir a propriedade "view" que o Xcode cria dentro do storyboard ao adicionar o controlador de visualização (não se esqueça de definir o nome de a classe personalizada).
Isso fará com que o tempo de execução procure automaticamente o xib e o carregue. Você pode usar esse truque para qualquer tipo de exibição de contêiner ou exibição de conteúdo.
fonte
Penso
alternative
em usarXIB views
para usarView Controller
em storyboard separado .Então, em storyboard principal no lugar de uso exibição personalizada
container view
comEmbed Segue
e terStoryboardReference
a este controlador de exibição personalizada que vista deve ser colocado dentro de outro ponto de vista em storyboard principal.Em seguida, podemos configurar a delegação e a comunicação entre esse ViewController incorporado e o controlador de exibição principal por meio de preparar para segue . Essa abordagem é diferente da exibição do UIView, mas muito mais simples e mais eficiente (da perspectiva da programação) pode ser utilizada para atingir o mesmo objetivo, ou seja, ter uma visualização personalizada reutilizável que é visível no storyboard principal
A vantagem adicional é que você pode implementar sua lógica na classe CustomViewController e configurar toda a delegação e exibir a preparação sem criar classes de controlador separadas (mais difíceis de encontrar no projeto) e sem colocar o código padrão no UIViewController principal usando Component. Eu acho que isso é bom para componentes reutilizáveis ex. Componente Music Player (como widget) que pode ser incorporado em outras visualizações.
fonte
Embora as respostas mais populares funcionem bem, elas estão conceitualmente erradas. Todos eles usam
File's owner
como conexão entre as saídas da classe e os componentes da interface do usuário.File's owner
deve ser usado apenas para objetos de nível superior e nãoUIView
s. Confira o documento de desenvolvedor da Apple . Ter o UIView como resultadoFile's owner
dessas consequências indesejáveis.contentView
onde deve usarself
. Não é apenas feio, mas também estruturalmente errado, porque a visualização intermediária impede a estrutura de dados de transmitir sua estrutura de interface do usuário. É como o oposto da interface do usuário declarativa.Há uma maneira elegante de fazer isso sem usar
File's owner
. Por favor, verifique esta postagem do blog . Explica como fazê-lo da maneira certa.fonte
Aqui está a resposta que você sempre quis. Você pode apenas criar sua
CustomView
classe, ter a instância principal dela em um xib com todas as subvisões e saídas. Em seguida, você pode aplicar essa classe a quaisquer instâncias em seus storyboards ou outros xibs.Não há necessidade de mexer com o proprietário do arquivo, ou conectar tomadas a um proxy ou modificar o xib de uma maneira peculiar ou adicionar uma instância de sua visualização personalizada como uma sub-visualização própria.
Apenas faça o seguinte:
UIView
paraNibView
(ou deUITableViewCell
paraNibTableViewCell
)É isso aí!
Até funciona com o IBDesignable para referenciar sua visualização personalizada (incluindo as subvisões do xib) em tempo de design no storyboard.
Você pode ler mais sobre isso aqui: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
E você pode obter a estrutura BFWControls de código aberto aqui: https://github.com/BareFeetWare/BFWControls
E aqui está uma simples extração do
NibReplaceable
código que o move, caso você esteja curioso: https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4Tom 👣
fonte
Esta solução pode ser usada mesmo que sua classe não tenha o mesmo nome que o XIB. Por exemplo, se você tiver uma classe de controlador de exibição de base controllerA que possui um nome XIB controllerA.xib e a subclassificou com controllerB e deseja criar uma instância de controllerB em um storyboard, poderá:
*
fonte
Solução para Objective-C de acordo com as etapas descritas na resposta de Ben Patch .
Use a extensão para UIView:
Crie arquivos
MyView.h
,MyView.m
eMyView.xib
.Primeiro prepare sua resposta
MyView.xib
como Ben Patch diz, então defina a classeMyView
para o proprietário do arquivo em vez da visualização principal dentro deste XIB.MyView.h
:MyView.m
:E mais tarde, basta criar sua exibição programaticamente:
Aviso! A visualização dessa exibição não será mostrada no Storyboard se você usar a Extensão WatchKit devido a esse bug no Xcode> = 9.2: https://forums.developer.apple.com/thread/95616
fonte