Exemplo de subclasse UIView personalizado
Normalmente crio aplicativos iOS sem usar storyboards ou pontas. Vou compartilhar algumas técnicas que aprendi para responder às suas perguntas.
Escondendo initmétodos indesejados
Minha primeira sugestão é declarar uma base UIViewpara ocultar inicializadores indesejados. Eu discuti essa abordagem em detalhes em minha resposta a "Como ocultar inicializadores específicos de storyboard e Nib em subclasses de IU" . Observação: esta abordagem pressupõe que você não usará BaseViewseus descendentes em storyboards ou pontas, pois isso fará com que o aplicativo trave intencionalmente.
class BaseView: UIView {
// This initializer hides init(frame:) from subclasses
init() {
super.init(frame: CGRect.zero)
}
// This attribute hides `init(coder:)` from subclasses
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
}
Sua subclasse UIView personalizada deve ser herdada de BaseView. Ele deve chamar super.init () em seu inicializador. Não é necessário implementar init(coder:). Isso é demonstrado no exemplo abaixo.
Adicionando um UITextField
Eu crio propriedades armazenadas para subvisualizações referenciadas fora do initmétodo. Eu normalmente faria isso para um UITextField. Eu prefiro subviews instanciar dentro da declaração da propriedade subexibição assim: let textField = UITextField().
O UITextField não estará visível a menos que você o adicione à lista de subvisualização da visualização personalizada chamando addSubview(_:). Isso é demonstrado no exemplo abaixo.
Layout programático sem layout automático
O UITextField não ficará visível a menos que você defina seu tamanho e posição. Costumo fazer layout em código (não usando Auto Layout) dentro do método layoutSubviews . layoutSubviews()é chamado inicialmente e sempre que ocorre um evento de redimensionamento. Isso permite ajustar o layout dependendo do tamanho de CustomView. Por exemplo, se CustomView aparece na largura total em vários tamanhos de iPhones e iPads e se ajusta para rotação, ele precisa acomodar muitos tamanhos iniciais e redimensionar dinamicamente.
Você pode consultar frame.heighte frame.widthem layoutSubviews()para obter as dimensões do CustomView para referência. Isso é demonstrado no exemplo abaixo.
Exemplo de subclasse UIView
Uma subclasse UIView personalizada contendo um UITextField que não precisa ser implementado init?(coder:).
class CustomView: BaseView {
let textField = UITextField()
override init() {
super.init()
// configure and add textField as subview
textField.placeholder = "placeholder text"
textField.font = UIFont.systemFont(ofSize: 12)
addSubview(textField)
}
override func layoutSubviews() {
super.layoutSubviews()
// Set textField size and position
textField.frame.size = CGSize(width: frame.width - 20, height: 30)
textField.frame.origin = CGPoint(x: 10, y: 10)
}
}
Layout programático com layout automático
Você também pode implementar layout usando Layout automático no código. Como não faço isso com frequência, não mostrarei um exemplo. Você pode encontrar exemplos de implementação de Layout automático em código no Stack Overflow e em outros lugares na Internet.
Estruturas de layout programático
Existem estruturas de código aberto que implementam layout no código. Um no qual estou interessado, mas não tentei, é o LayoutKit . Foi escrito pela equipe de desenvolvimento do LinkedIn. Do repositório Github: "O LinkedIn criou o LayoutKit porque descobrimos que o Auto Layout não tem desempenho suficiente para hierarquias de visualização complicadas em visualizações com rolagem."
Por que colocar fatalErroreminit(coder:)
Ao criar subclasses de UIView que nunca serão usadas em um storyboard ou bico, você pode introduzir inicializadores com diferentes parâmetros e requisitos de inicialização que não podem ser chamados pelo init(coder:)método. Se você não reprovou o init (coder :) com um fatalError, isso poderia levar a problemas muito confusos no futuro se usado acidentalmente em um storyboard / bico. O fatalError afirma essas intenções.
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
Se você deseja executar algum código quando a subclasse é criada, independentemente de ser criado no código ou em um storyboard / nib, você pode fazer algo como o seguinte (com base na resposta de Jeff Gu Kang )
class CustomView: UIView {
override init (frame: CGRect) {
super.init(frame: frame)
initCommon()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initCommon()
}
func initCommon() {
// Your custom initialization code
}
}