Bagaimana cara saya menulis init khusus untuk subkelas UIView di Swift?

125

Katakanlah saya ingin initsebuah UIViewsubclass dengan Stringdan Int.

Bagaimana saya melakukan ini di Swift jika saya hanya subklasifikasi UIView? Jika saya hanya membuat init()fungsi kustom tetapi parameternya adalah String dan Int, ini memberitahu saya bahwa "super.init () tidak dipanggil sebelum kembali dari initializer".

Dan jika saya menelepon super.init()saya diberitahu saya harus menggunakan inisialisasi yang ditunjuk. Apa yang harus saya gunakan di sana? Versi bingkai? Versi coder? Kedua? Mengapa?

Doug Smith
sumber

Jawaban:

207

The init(frame:)Versi adalah initializer default. Anda harus memanggilnya hanya setelah menginisialisasi variabel instan Anda. Jika tampilan ini sedang disusun kembali dari Nib maka penginisialisasi kustom Anda tidak akan dipanggil, dan sebaliknya init?(coder:)versi akan dipanggil. Karena Swift sekarang memerlukan implementasi yang diperlukan init?(coder:), saya telah memperbarui contoh di bawah ini dan mengubah letdeklarasi variabel menjadi vardan opsional. Dalam hal ini, Anda akan menginisialisasi mereka di awakeFromNib()atau nanti.

class TestView : UIView {
    var s: String?
    var i: Int?
    init(s: String, i: Int) {
        self.s = s
        self.i = i
        super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}
Serigala McNally
sumber
5
Kemudian dengan segala cara membuatnya var. Tetapi praktik terbaik default di Swift adalah mendeklarasikan variabel letkecuali ada alasan untuk mendeklarasikannya var. Karena itu, tidak ada alasan untuk melakukannya dalam contoh kode saya di atas let.
Wolf McNally
2
Kode ini tidak dikompilasi. Anda perlu menerapkan penginisialisasi yang diperlukan init(coder:).
Decade Moon
3
menarik bagaimana ini disusun tahun lalu. Saat ini ia mengeluh di bawah init (coder :) bahwa "Properti tidak diinisialisasi pada panggilan super.init"
mafiOSo
Memperbaiki contoh untuk Swift 3.1. Kompilasi di bawah taman bermain yang mengimpor UIKit.
Wolf McNally
1
@ Cahaya Malam yang saya buat sdan iOpsional untuk menjaga hal-hal sederhana di sini. Jika mereka bukan opsional, mereka juga perlu diinisialisasi dalam initializer yang diperlukan. Menjadikannya opsional berarti menjadi seperti nilsaat super.init()dipanggil. Jika mereka bukan opsional, mereka memang perlu ditugaskan sebelum memanggil super.init ().
Wolf McNally
32

Saya membuat init umum untuk yang ditunjuk dan diperlukan. Untuk kemudahan saya mendelegasikan init(frame:)dengan bingkai nol.

Memiliki nol frame bukanlah masalah karena biasanya view berada di dalam viewController's view; tampilan ubahsuaian Anda akan mendapatkan kesempatan yang baik dan aman untuk mengatur tampilan bawahnya saat panggilan superview layoutSubviews()atau updateConstraints(). Dua fungsi ini dipanggil oleh sistem secara rekursif di seluruh hierarki tampilan. Anda dapat menggunakan salah satu updateContstraints()atau layoutSubviews(). updateContstraints()disebut pertama, lalu layoutSubviews(). Dalam updateConstraints()make pastikan untuk panggilan super lalu . Di layoutSubviews(), panggil super dulu .

Inilah yang saya lakukan:

@IBDesignable
class MyView: UIView {

      convenience init(args: Whatever) {
          self.init(frame: CGRect.zero)
          //assign custom vars
      }

      override init(frame: CGRect) {
           super.init(frame: frame)
           commonInit()
      }

      required init?(coder aDecoder: NSCoder) {
           super.init(coder: aDecoder)
           commonInit()
      }

      override func prepareForInterfaceBuilder() {
           super.prepareForInterfaceBuilder()
           commonInit()
      }

      private func commonInit() {
           //custom initialization
      }

      override func updateConstraints() {
           //set subview constraints here
           super.updateConstraints()
      }

      override func layoutSubviews() {
           super.layoutSubviews()
           //manually set subview frames here
      }

}
MH175
sumber
1
Seharusnya tidak bekerja: Penggunaan 'diri' dalam panggilan metode 'commonInit' sebelum super.init menginisialisasi self
surfrider
1
Inisialisasi argumen khusus setelah panggilan self.init. Memperbarui jawaban saya.
MH175
1
Tetapi bagaimana jika Anda ingin menginisialisasi beberapa properti dalam commonInitmetode, tetapi Anda tidak dapat menempatkannya setelah superdalam kasus ini karena Anda harus menginisialisasi semua properti SEBELUM superpanggilan. Lol sepertinya loop mati.
surfrider
1
Beginilah inisialisasi Swift sering berfungsi: cari "inisialisasi dua fase". Anda dapat menggunakan opsional yang terbuka secara implisit, tetapi saya sarankan untuk tidak melakukannya. Arsitektur Anda, terutama ketika berhadapan dengan tampilan, harus menginisialisasi semua properti lokal. Saya telah menggunakan metode commonInit () ini untuk ratusan tampilan sekarang. Ia bekerja
MH175
17

Inilah cara saya melakukannya di iOS 9 di Swift -

import UIKit

class CustomView : UIView {

    init() {
        super.init(frame: UIScreen.mainScreen().bounds);

        //for debug validation
        self.backgroundColor = UIColor.blueColor();
        print("My Custom Init");

        return;
    }

    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented"); }
}

Berikut ini adalah proyek lengkap dengan contoh:

J-Dizzle
sumber
2
ini akan menggunakan seluruh tampilan untuk View
RaptoX
1
ya! Jika tertarik dengan subview sebagian, beri tahu saya dan saya akan memposting ini juga
J-Dizzle
1
Saya suka jawaban ini yang terbaik, karena memiliki fatalError berarti saya tidak harus memasukkan kode apa pun di init yang diperlukan.
Carter Medlin
1
@ J-Dizzle, saya ingin melihat solusi untuk tampilan sebagian.
Ari Lacenski
bukankah jawaban Anda mengatakan kebalikan dari jawaban yang diterima? Maksud saya Anda melakukan kustomisasi setelah super.init, tetapi dia mengatakan itu harus dilakukan sebelum super.init...
Sayang
11

Inilah cara saya melakukan Subview di iOS di Swift -

class CustomSubview : UIView {

    init() {
        super.init(frame: UIScreen.mainScreen().bounds);

        let windowHeight : CGFloat = 150;
        let windowWidth  : CGFloat = 360;

        self.backgroundColor = UIColor.whiteColor();
        self.frame = CGRectMake(0, 0, windowWidth, windowHeight);
        self.center = CGPoint(x: UIScreen.mainScreen().bounds.width/2, y: 375);

        //for debug validation
        self.backgroundColor = UIColor.grayColor();
        print("My Custom Init");

        return;
    }

    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented"); }
}
J-Dizzle
sumber
4
Penggunaan panggilan fatalError () bagus. Saya harus menggunakan opsional hanya untuk membungkam peringatan dari penginisialisasi yang bahkan tidak digunakan. Ini tutup itu! Terima kasih.
Mike Critchley