Làm cách nào để tạo bộ khởi tạo tùy chỉnh cho lớp con UIViewController trong Swift?


95

Xin lỗi nếu điều này đã được hỏi trước đây, tôi đã tìm kiếm xung quanh rất nhiều và nhiều câu trả lời là từ các bản Swift beta trước đó khi mọi thứ đã khác. Tôi dường như không thể tìm thấy một câu trả lời dứt khoát.

Tôi muốn phân lớp UIViewControllervà có bộ khởi tạo tùy chỉnh để cho phép tôi thiết lập nó trong mã dễ dàng. Tôi đang gặp sự cố khi thực hiện việc này trong Swift.

Tôi muốn một init()hàm mà tôi có thể sử dụng để chuyển một hàm cụ thể NSURLsau đó tôi sẽ sử dụng với bộ điều khiển chế độ xem. Trong tâm trí của tôi, nó trông giống như một cái gì đó init(withImageURL: NSURL). Nếu tôi thêm chức năng đó thì nó sẽ yêu cầu tôi thêm init(coder: NSCoder)chức năng đó.

Tôi tin rằng điều này là do nó được đánh dấu trong lớp cha bằng requiredtừ khóa? Vì vậy, tôi phải làm điều đó trong lớp con? Tôi thêm nó:

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

Giờ thì sao? Trình khởi tạo đặc biệt của tôi có được coi là conveniencemột? Một cái được chỉ định? Tôi có gọi một trình khởi tạo siêu không? Một trình khởi tạo từ cùng một lớp?

Làm cách nào để thêm trình khởi tạo đặc biệt của tôi vào một UIViewControllerlớp con?


Initializers là tốt cho viewControllers lập trình tạo ra, nhưng đối với viewcontrollers tạo ra thông qua kịch bản bạn đang trên may mắn và phải làm việc theo cách của bạn xung quanh nó
Mật ong

Câu trả lời:


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

imageURL có thể là một hằng số không? ("let imageURL: NSURL?)
Van Du Tran

2
không hoạt động xCode 7, hàm tạo được yêu cầu không khởi tạo thuộc tính cấp lớp, trong trường hợp của tôi là Int, không phải NSURL?
Chris Hayes

1
@NikolaLukic - Chúng ta có thể tạo ra một thể hiện mới của lớp bằng cách gọi ViewController(), ViewController(imageURL: url)hoặc tải nó từ một kịch bản này.
ylin0x81 30/09/16

3
Tôi đã phải viết tôi như required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }khác với super.init(coder: aDecoder)tôi đã nhận được lỗi của tài sản self.somePropertykhông được khởi tạo tại super.initcuộc gọi
Mật ong

1
Không chắc nó liên quan như thế nào. Nếu bạn đang làm mã thuần túy, thì bạn không cần phải điền các thuộc tính của mình vào bên trong init(coder: aDecoder). Ý fatalErrorchí là đủ
Honey

26

Đối với những người viết giao diện người dùng trong mã

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

12

Điều này rất giống với các câu trả lời khác, nhưng với một số giải thích. Câu trả lời được chấp nhận là gây hiểu lầm vì thuộc tính của nó là tùy chọn và không cho thấy thực tế là bạn init?(coder: NSCoder)PHẢI khởi tạo từng thuộc tính và giải pháp duy nhất cho điều đó là có a fatalError(). Cuối cùng, bạn có thể thoát khỏi bằng cách làm cho các thuộc tính của bạn trở thành tùy chọn, nhưng điều đó không thực sự trả lời câu hỏi của 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")
    }
}

Về cơ bản, bạn phải ABANDON tải nó từ bảng phân cảnh. Tại sao?

Bởi vì khi bạn gọi một viewController storyboard.instantiateViewController(withIdentifier: "viewController")thì UIKit sẽ thực hiện nhiệm vụ của nó và gọi

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

Bạn không bao giờ có thể chuyển hướng cuộc gọi đó đến một phương thức init khác.

Tài liệu trên instantiateViewController(withIdentifier:):

Sử dụng phương pháp này để tạo một đối tượng bộ điều khiển chế độ xem để trình bày theo chương trình. Mỗi lần bạn gọi phương thức này, nó sẽ tạo ra một phiên bản mới của bộ điều khiển chế độ xem bằng cách sử dụng init(coder:)phương thức.

Tuy nhiên, đối với viewController được tạo lập trình hoặc viewControllers được tạo bằng nib, bạn có thể chuyển hướng cuộc gọi đó như được hiển thị ở trên.


5

Các trình khởi tạo tiện lợi là thứ yếu, hỗ trợ các trình khởi tạo cho một lớp. Bạn có thể xác định bộ khởi tạo tiện lợi để gọi bộ khởi tạo được chỉ định từ cùng lớp với bộ khởi tạo tiện lợi với một số tham số của bộ khởi tạo được chỉ định được đặt thành giá trị mặc định. Bạn cũng có thể xác định một trình khởi tạo tiện lợi để tạo một phiên bản của lớp đó cho một trường hợp sử dụng cụ thể hoặc kiểu giá trị đầu vào.

Chúng được ghi lại ở đây .


0

Nếu bạn cần một init tùy chỉnh cho một cửa sổ bật lên, chẳng hạn, bạn có thể sử dụng phương pháp sau:

Tạo một init tùy chỉnh sử dụng siêu init với nibName và gói và sau đó truy cập thuộc tính chế độ xem để buộc tải cấu trúc phân cấp chế độ xem.

Sau đó, trong hàm viewDidLoad, bạn có thể cấu hình các chế độ xem với các tham số được truyền trong quá trình khởi tạo.

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)
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.