Я хочу UIView
создать подкласс и показать вид входа в систему. Я создал это в Objective-C, но теперь хочу перенести его на Swift. Я не использую раскадровки, поэтому весь свой UI создаю в коде.
Но первая проблема в том, что я должен реализовать 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")
}
Мой вопрос в том, где мне создать текстовое поле и т. Д., И если я никогда не использую фрейм и кодер, как я могу «скрыть» это?
CGRectZero
я считаю, что рекомендуется использоватьCGRect.zeroRect
.Это проще.
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
. Он должен вызывать super.init () в своем инициализаторе. Это не нужно реализовыватьinit(coder:)
. Это показано в примере ниже.Добавление UITextField
Я создаю сохраненные свойства для подпредставлений, на которые есть ссылки вне
init
метода. Я обычно делал это для UITextField. Я предпочитаю , чтобы создать экземпляр подвиды в декларации имущества подвида , как это:let textField = UITextField()
.UITextField не будет отображаться, если вы не добавите его в список подпредставлений пользовательского представления, вызвав
addSubview(_:)
. Это показано в примере ниже.Программный макет без автоматического макета
UITextField не будет отображаться, пока вы не установите его размер и положение. Я часто делаю макет в коде (не используя Auto Layout) в методе layoutSubviews .
layoutSubviews()
вызывается изначально и всякий раз, когда происходит событие изменения размера. Это позволяет настраивать макет в зависимости от размера CustomView. Например, если CustomView отображается во всю ширину на iPhone и iPad разных размеров и регулируется для поворота, ему необходимо приспособиться к множеству исходных размеров и динамически изменять размер.Вы можете обратиться к
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) } }
Программный макет с автоматическим макетом
Вы также можете реализовать макет с помощью автоматического макета в коде. Поскольку я делаю это не часто, не буду приводить пример. Вы можете найти примеры реализации Auto Layout в коде на Stack Overflow и в других местах в Интернете.
Программные макеты
Существуют платформы с открытым исходным кодом, которые реализуют макет в коде. Меня интересует LayoutKit, но я не пробовал . Его написала команда разработчиков LinkedIn. Из репозитория Github: «LinkedIn создал LayoutKit, потому что мы обнаружили, что Auto Layout недостаточно эффективен для сложных иерархий представлений в прокручиваемых представлениях».
Зачем ставить
fatalError
вinit(coder:)
При создании подклассов UIView, которые никогда не будут использоваться в раскадровке или пере, вы можете ввести инициализаторы с другими параметрами и требованиями инициализации, которые не могут быть вызваны
init(coder:)
методом. Если вы не завершили init (coder :) с ошибкойfatalError
, это может привести к очень запутанным проблемам в будущем, если вы случайно будете использовать его в раскадровке / пере. FatalError подтверждает эти намерения.required init?(coder aDecoder: NSCoder) { fatalError("NSCoding not supported") }
Если вы хотите запустить какой-либо код при создании подкласса независимо от того, создан ли он в коде или раскадровке / пере, вы можете сделать что-то вроде следующего (на основе ответа Джеффа Гу Канга )
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 } }
источник
fatalError
вы запрещаете запускать это представление с помощью файлов xibfatalError
внутри метода dealloc и сообщите нам, что он не работает, потому что этот класс должен быть одноэлементным. Если вы предпочитаете создавать элементы пользовательского интерфейса в коде, не следует вручную запрещать все остальные способы. Наконец, вопрос в том, как создать «программно без раскадровки», но xibs / nibs не упоминаются. В моем случае мне нужно создать массив ячеек с помощью + xib программно и передать их,DropDownMenuKit
и этот способ не работает, потому что автор этой библиотеки также запрещает xibs.Важно, чтобы ваш UIView можно было создать с помощью конструктора интерфейсов / раскадровок или из кода. Я считаю полезным иметь
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, если вы хотите использовать представление из файла xib, то это для вас. Я создал класс CustomCalloutView Подкласс UIView. Я создал файл xib, и в IB просто выберите владельца файла, затем выберите инспектор атрибутов, установите имя класса на 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) } }
источник