Подтвердить что ты не робот

Подкласс 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")
}

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

4b9b3361

Ответ 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)
}
}