Bagaimana cara membuat penginisialisasi khusus untuk subkelas UIViewController di Swift?

95

Mohon maaf jika ini telah ditanyakan sebelumnya, saya telah mencari-cari banyak dan banyak jawaban berasal dari beta Swift sebelumnya ketika semuanya berbeda. Saya tidak dapat menemukan jawaban yang pasti.

Saya ingin membuat subkelas UIViewControllerdan memiliki penginisialisasi khusus untuk memungkinkan saya mengaturnya dalam kode dengan mudah. Saya mengalami masalah saat melakukan ini di Swift.

Saya ingin sebuah init()fungsi yang dapat saya gunakan untuk melewati spesifik yang NSURLkemudian akan saya gunakan dengan pengontrol tampilan. Dalam pikiranku itu terlihat seperti init(withImageURL: NSURL). Jika saya menambahkan fungsi itu kemudian meminta saya untuk menambahkan init(coder: NSCoder)fungsi tersebut.

Saya percaya ini karena itu ditandai di superclass dengan requiredkata kunci? Jadi saya harus melakukannya di subclass? Saya menambahkannya:

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

Sekarang apa? Apakah penginisialisasi khusus saya dianggap sebagai penginisialisasi convenience? Yang ditunjuk? Apakah saya memanggil penginisialisasi super? Penginisialisasi dari kelas yang sama?

Bagaimana cara menambahkan penginisialisasi khusus saya ke UIViewControllersubkelas?

Doug Smith
sumber
Penginisialisasi bagus untuk viewControllers yang dibuat secara terprogram, tetapi untuk viewcontrollers yang dibuat melalui storyboard, Anda kurang beruntung dan harus mengatasinya
Honey

Jawaban:

161
class ViewController: UIViewController {

    var imageURL: NSURL?

    // this is a convenient way to create this view controller without a imageURL
    convenience init() {
        self.init(imageURL: nil)
    }

    init(imageURL: NSURL?) {
        self.imageURL = imageURL
        super.init(nibName: nil, bundle: nil)
    }

    // if this view controller is loaded from a storyboard, imageURL will be nil

    /* Xcode 6
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    */

    // Xcode 7 & 8
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}
ylin0x81
sumber
dapat imageURL menjadi konstanta? ("biarkan imageURL: NSURL?)
Van Du Tran
2
tidak berfungsi xCode 7, konstruktor yang diperlukan gagal untuk menginisialisasi properti tingkat kelas, dalam kasus saya Int, bukan NSURL?
Chris Hayes
1
@NikolaLukic - Kita bisa menciptakan contoh baru dari kelas dengan menelepon ViewController(), ViewController(imageURL: url)atau loading itu dari storyboard.
ylin0x81
3
Saya harus menulis milik saya karena required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }jika tidak super.init(coder: aDecoder)saya mendapatkan kesalahan Properti self.somePropertytidak diinisialisasi saat super.initdihubungi
Honey
1
Tidak yakin bagaimana itu terkait. Jika Anda melakukan kode murni, maka Anda tidak perlu mengisi properti Anda di dalam init(coder: aDecoder). The fatalErrorakan mencukupi
Madu
26

Bagi mereka yang menulis UI dalam kode

class Your_ViewController : UIViewController {

    let your_property : String

    init(your_property: String) {
        self.your_property = your_property
        super.init(nibName: nil, bundle: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) is not supported")
    }
}
Sasan Angel
sumber
12

Ini sangat mirip dengan jawaban lain, tetapi dengan beberapa penjelasan. Jawaban yang diterima menyesatkan karena propertinya bersifat opsional dan tidak mengungkapkan fakta bahwa init?(coder: NSCoder)HARUS Anda menginisialisasi setiap properti dan satu-satunya solusi untuk itu adalah memiliki fatalError(). Pada akhirnya, Anda bisa lolos dengan menjadikan properti Anda opsional, tetapi itu tidak benar-benar menjawab pertanyaan OP.

// Think more of a OnlyNibOrProgrammatic_NOTStoryboardViewController
class ViewController: UIViewController {

    let name: String

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // I don't have a nib. It's all through my code.
    init(name: String) {
        self.name = name

        super.init(nibName: nil, bundle: nil)
    }

    // I have a nib. I'd like to use my nib and also initialze the `name` property
    init(name: String, nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle? ) {
        self.name = name
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    // when you do storyboard.instantiateViewController(withIdentifier: "ViewController")
    // The SYSTEM will never call this!
    // it wants to call the required initializer!

    init?(name: String, coder aDecoder: NSCoder) {
        self.name = "name"
        super.init(coder: aDecoder)
    }

    // when you do storyboard.instantiateViewController(withIdentifier: "ViewController")
    // The SYSTEM WILL call this!
    // because this is its required initializer!
    // but what are you going to do for your `name` property?!
    // are you just going to do `self.name = "default Name" just to make it compile?!
    // Since you can't do anything then it's just best to leave it as `fatalError()`
    required init?(coder aDecoder: NSCoder) {
        fatalError("I WILL NEVER instantiate through storyboard! It's impossible to initialize super.init?(coder aDecoder: NSCoder) with any other parameter")
    }
}

Pada dasarnya Anda harus MENGABAIKAN memuatnya dari storyboard. Mengapa?

Karena ketika Anda memanggil viewController storyboard.instantiateViewController(withIdentifier: "viewController")maka UIKit akan melakukan tugasnya dan memanggilnya

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

Anda tidak pernah bisa mengalihkan panggilan itu ke metode init lain.

Dokumen tentang instantiateViewController(withIdentifier:):

Gunakan metode ini untuk membuat objek pengontrol tampilan untuk dipresentasikan secara terprogram. Setiap kali Anda memanggil metode ini, itu membuat instance baru dari pengontrol tampilan menggunakan init(coder:)metode.

Namun untuk viewController yang dibuat secara terprogram atau viewControllers yang dibuat nib, Anda dapat mengalihkan panggilan tersebut seperti yang ditunjukkan di atas.

Madu
sumber
5

Penginisialisasi kenyamanan bersifat sekunder, mendukung penginisialisasi untuk kelas. Anda dapat menentukan penginisialisasi praktis untuk memanggil penginisialisasi yang ditunjuk dari kelas yang sama sebagai penginisialisasi praktis dengan beberapa parameter penginisialisasi yang ditetapkan disetel ke nilai default. Anda juga dapat menentukan penginisialisasi praktis untuk membuat instance kelas tersebut untuk kasus penggunaan atau jenis nilai input tertentu.

Mereka didokumentasikan di sini .

Vatsal Manot
sumber
0

Jika Anda memerlukan init kustom untuk popover misalnya, Anda dapat menggunakan pendekatan berikut:

Buat init kustom yang menggunakan super init dengan nibName dan bundle dan setelah itu akses properti view untuk memaksa pemuatan hierarki tampilan.

Kemudian dalam fungsi viewDidLoad Anda dapat mengonfigurasi tampilan dengan parameter yang diteruskan dalam inisialisasi.

import UIKit

struct Player {
    let name: String
    let age: Int
}

class VC: UIViewController {


@IBOutlet weak var playerName: UILabel!

let player: Player

init(player: Player) {
    self.player = player
    super.init(nibName: "VC", bundle: Bundle.main)
    if let view = view, view.isHidden {}
}

override func viewDidLoad() {
    super.viewDidLoad()
    configure()
}

func configure() {
    playerName.text = player.name + "\(player.age)"
}
}

func showPlayerVC() {
    let foo = Player(name: "bar", age: 666)
    let vc = VC(player: foo)
    present(vc, animated: true, completion: nil)
}
rockdaswift
sumber