Swift подкласс UIView
Я только начал использовать Swift. Я прочитал книгу, и я лучше учусь, делая это, я начал что-то простое.
Я хочу, чтобы подкласс UIView и показать логин, как посмотреть. Я создал это в Objective-C, но теперь я хочу перенести его в Swift. Я не использую раскадровки, поэтому я создаю весь свой пользовательский интерфейс в коде.
но первая проблема заключается в том, что я должен реализовать initWithCoder. Я дал ему реализацию по умолчанию, так как он не будет называться. Теперь, когда я запускаю программу, она падает потому что я должен реализовать initWithFrame как хорошо. Теперь я получил это:
override init() {
super.init()
println("Default init")
}
override init(frame: CGRect) {
super.init(frame: frame)
println("Frame init")
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
println("Coder init")
}
мой вопрос в том, где я должен создать свое текстовое поле и т. д... и если я никогда не реализовываю фрейм и кодер, как я могу" скрыть " это?
6 ответов:
Я обычно делаю что-то вроде этого, его немного многословно.
class MyView: UIView { override init(frame: CGRect) { super.init(frame: frame) addBehavior() } convenience init() { self.init(frame: CGRect.zero) } required init(coder aDecoder: NSCoder) { fatalError("This class does not support NSCoding") } func addBehavior() { print("Add all the behavior here") } } let u = MyView(frame: CGRect.zero) let v = MyView()(Edit: я отредактировал свой ответ, чтобы связь между инициализаторами была более ясной)
Это более простой.
override init (frame : CGRect) { super.init(frame : frame) // Do what you want. } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
Пользовательский Пример Подкласса UIView
я обычно создаю приложения iOS без использования раскадровок или перьев. Я поделюсь некоторыми методами, которые я научился отвечать на ваши вопросы.
Скрывая Излишние
initметодымое первое предложение-объявить базовый
UIViewчтобы скрыть нежелательные инициализаторы. Я подробно обсуждал этот подход в мой ответ на "Как скрыть раскадровку и кончик конкретных инициализаторов в пользовательском интерфейсе Подклассы". Примечание: этот подход предполагает, что вы не будете использоватьBaseViewили его потомки в раскадровках или перьях, так как это намеренно приведет к сбою приложения.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") } }ваш пользовательский подкласс UIView должен наследовать от
BaseView. Его надо назвать супер.метод init() в инициализаторе. Это не нужно реализовыватьinit(coder:). Это показано в примере ниже.добавление UITextField
я создаю сохраненные свойства для подвиды, на которые ссылаются за пределами
initметод. Я обычно делаю это для UITextField. Я предпочитаю создавать экземпляры subviews в объявлении свойства subview следующим образом:let textField = UITextField().поле UITextField не будет отображаться, если вы не добавите его в список подвидов пользовательского представления, вызвав
addSubview(_:). Это показано в примере ниже.Программная Компоновка Без Автоматической Компоновки
UITextField не будет видно, если вы не установите его размер и положение. Я часто делаю макет в коде (не используя Auto Layout) в пределах метод layoutSubviews.
layoutSubviews()вызывается изначально и всякий раз, когда происходит событие изменения размера. Это позволяет настроить макет в зависимости от размера CustomView. Например, если CustomView отображается во всю ширину на различных размерах iPhones и iPads и настраивается для вращения, ему необходимо разместить много начальных размеров и динамически изменять размер.вы можете обратиться к
frame.heightиframe.widthвнутриlayoutSubviews()чтобы получить размеры CustomView для справки. Это показано в примере ниже.Пример Подкласса UIView
пользовательский подкласс UIView, содержащий UITextField, который не нужно реализовывать
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) } }программная компоновка с автоматической компоновкой
вы также можете реализовать макет с помощью автоматического макета в коде. Так как я не часто это делаю это, я не буду показывать пример. Вы можете найти примеры реализации автоматического макета в коде при переполнении стека и в других местах в Интернете.
Рамки Программного Макета
есть систем с открытым исходным кодом, которые реализуют макет в код. Один я заинтересован, но не пробовал это LayoutKit. Он был написан командой разработчиков LinkedIn. Из репозитория Github: "LinkedIn создал LayoutKit, потому что мы нашли этот автоматический макет недостаточно эффективен для сложных иерархий представлений в прокручиваемых представлениях."
зачем ставить
fatalErrorнаinit(coder:)при создании подклассов UIView, которые никогда не будут использоваться в раскадровке или nib, вы можете ввести инициализаторы с различными параметрами и требованиями к инициализации, которые не могут быть вызваны
init(coder:)метод. Если вы не провалили init (coder:) с помощьюfatalError, это может привести к очень запутанной проблемы вниз по линии, если случайно используется в раскадровке/Сиб. Фатальная ошибка утверждает эти намерения.required init?(coder aDecoder: NSCoder) { fatalError("NSCoding not supported") }если вы хотите запустить некоторый код при создании подкласса независимо от того, создается ли он в коде или раскадровке / nib, вы можете сделать что-то вроде следующего (на основе ответ Джеффа ГУ Кана)
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 } }
важно, что ваш UIView может быть создан с помощью interface builder/storyboards или из кода. Я считаю, что это полезно иметь
setupметод для уменьшения дублирования кода установки. например,class RedView: UIView { override init (frame: CGRect) { super.init(frame: frame) setup() } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder)! setup() } func setup () { backgroundColor = .red } }
Swift 4.0, если вы хотите использовать view from xib file, то это для вас. Я создал подкласс класса CustomCalloutView UIView. Я создал xib-файл и в IB просто выберите владельца файла, затем выберите Attribute inspector set class name to CustomCalloutView, а затем создайте выход в своем классе.
import UIKit class CustomCalloutView: UIView { @IBOutlet var viewCallout: UIView! // This is main view @IBOutlet weak var btnCall: UIButton! // subview of viewCallout @IBOutlet weak var btnDirection: UIButton! // subview of viewCallout @IBOutlet weak var btnFavourite: UIButton! // subview of viewCallout // let nibName = "CustomCalloutView" this is name of xib file override init(frame: CGRect) { super.init(frame: frame) nibSetup() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) nibSetup() } func nibSetup() { Bundle.main.loadNibNamed(String(describing: CustomCalloutView.self), owner: self, options: nil) guard let contentView = viewCallout else { return } // adding main view contentView.frame = self.bounds //Comment this line it take default frame of nib view // custom your view properties here self.addSubview(contentView) } }// теперь добавляем его
let viewCustom = CustomCalloutView.init(frame: CGRect.init(x: 120, y: 120, 50, height: 50)) self.view.addSubview(viewCustom)
вот пример того, как я обычно строю свои подклассы(UIView). У меня есть содержимое в качестве переменных, поэтому они могут быть доступны и изменены, возможно, позже в каком-то другом классе. Я также показал, как я использую автоматический макет и добавление контента.
например, в ViewController у меня есть это представление, инициализированное в ViewDidLoad (), так как это вызывается только один раз, когда виден вид. Тогда я использую эти функции, которые я делаю здесь
addContentToView()а тоactivateConstraints()для построения контента и установки ограничений. Если Я позже в ViewController хочу, чтобы цвет, скажем, кнопки был красным, я просто делаю это в этой конкретной функции в этом ViewController. Что-то вроде:func tweaksome(){ self.customView.someButton.color = UIColor.red}class SomeView: UIView { var leading: NSLayoutConstraint! var trailing: NSLayoutConstraint! var bottom: NSLayoutConstraint! var height: NSLayoutConstraint! var someButton: UIButton = { var btn: UIButton = UIButton(type: UIButtonType.system) btn.setImage(UIImage(named: "someImage"), for: .normal) btn.translatesAutoresizingMaskIntoConstraints = false return btn }() var btnLeading: NSLayoutConstraint! var btnBottom: NSLayoutConstraint! var btnTop: NSLayoutConstraint! var btnWidth: NSLayoutConstraint! var textfield: UITextField = { var tf: UITextField = UITextField() tf.adjustsFontSizeToFitWidth = true tf.placeholder = "Cool placeholder" tf.translatesAutoresizingMaskIntoConstraints = false tf.backgroundColor = UIColor.white tf.textColor = UIColor.black return tf }() var txtfieldLeading: NSLayoutConstraint! var txtfieldTrailing: NSLayoutConstraint! var txtfieldCenterY: NSLayoutConstraint! override init(frame: CGRect){ super.init(frame: frame) self.translatesAutoresizingMaskIntoConstraints = false } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) //fatalError("init(coder:) has not been implemented") } /* // Only override draw() if you perform custom drawing. // An empty implementation adversely affects performance during animation. override func draw(_ rect: CGRect) { // Drawing code } */ func activateConstraints(){ NSLayoutConstraint.activate([self.btnLeading, self.btnBottom, self.btnTop, self.btnWidth]) NSLayoutConstraint.activate([self.txtfieldCenterY, self.txtfieldLeading, self.txtfieldTrailing]) } func addContentToView(){ //setting the sizes self.addSubview(self.userLocationBtn) self.btnLeading = NSLayoutConstraint( item: someButton, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1.0, constant: 5.0) self.btnBottom = NSLayoutConstraint( item: someButton, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0.0) self.btnTop = NSLayoutConstraint( item: someButton, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0.0) self.btnWidth = NSLayoutConstraint( item: someButton, attribute: .width, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 1.0, constant: 0.0) self.addSubview(self.textfield) self.txtfieldLeading = NSLayoutConstraint( item: self.textfield, attribute: .leading, relatedBy: .equal, toItem: someButton, attribute: .trailing, multiplier: 1.0, constant: 5) self.txtfieldTrailing = NSLayoutConstraint( item: self.textfield, attribute: .trailing, relatedBy: .equal, toItem: self.doneButton, attribute: .leading, multiplier: 1.0, constant: -5) self.txtfieldCenterY = NSLayoutConstraint( item: self.textfield, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0.0) } }
Comments