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")
}


мой вопрос в том, где я должен создать свое текстовое поле и т. д... и если я никогда не реализовываю фрейм и кодер, как я могу" скрыть " это?

676   6  

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

    Ничего не найдено.