Подкласс 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")
}
Мой вопрос в том, где я должен создать свое текстовое поле и т.д.... и если я никогда не буду использовать фрейм и кодировщик, как я могу "скрыть" это?
Ответы
Ответ 1
Я обычно делаю что-то вроде этого, это немного подробный.
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: я отредактировал свой ответ так, что связь между инициализаторами более понятна)
Ответ 2
Это проще.
override init (frame : CGRect) {
super.init(frame : frame)
// Do what you want.
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
Ответ 3
Пример подкласса 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. Я предпочитаю создавать экземпляры subview в объявлении свойства 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)
}
}
Программный макет с автоматической компоновкой
Вы также можете реализовать макет, используя Auto Layout в коде. Поскольку я не часто это делаю, я не буду показывать пример. Вы можете найти примеры реализации Auto Layout в коде на Qaru и в других местах в Интернете.
Программные схемы компоновки
Существуют фреймы с открытым исходным кодом, которые реализуют макет кода. Один из меня интересует, но не пробовал LayoutKit. Это было написано командой разработчиков LinkedIn. Из репозитория Github: "LinkedIn создал LayoutKit, потому что мы обнаружили, что Auto Layout недостаточно эффективен для сложных иерархий представлений в прокручиваемых представлениях".
Зачем ставить fatalError
в init(coder:)
При создании подклассов UIView, которые никогда не будут использоваться в раскадровке или nib, вы можете ввести инициализаторы с различными параметрами и требованиями к инициализации, которые нельзя было бы вызвать методом init(coder:)
. Если вы не сработали init (coder:) с fatalError
, это может привести к очень запутывающим проблемам по линии, если они случайно используются в раскадровке /nib. FatalError утверждает эти намерения.
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
Если вы хотите запустить некоторый код, когда подкласс создается независимо от того, создан он в коде или раскадровке /nib, вы можете сделать что-то вроде следующего (на основе Jeff Ответ Гу Канга)
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
}
}
Ответ 4
Swift 4.0. Если вы хотите использовать представление из xib файла, то это для вас. Я создал класс SubCile класса UIView класса CustomCalloutView. Я создал файл xib, а в IB просто выберите владельца файла, затем выберите Attribute inspector set name класса для 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
// custom your view properties here
self.addSubview(contentView)
}
}
Ответ 5
Важно, чтобы ваш 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
}
}
Ответ 6
Вот пример того, как я обычно создаю свои подклассы (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)
}
}