ฉันจะสร้าง initializer แบบกำหนดเองสำหรับคลาสย่อย UIViewController ใน Swift ได้อย่างไร


95

ขออภัยหากมีการถามก่อนหน้านี้ฉันได้ค้นหาหลายรอบและคำตอบมากมายมาจาก Swift betas ก่อนหน้านี้เมื่อสิ่งต่างๆแตกต่างออกไป ดูเหมือนจะหาคำตอบที่ชัดเจนไม่ได้

ฉันต้องการคลาสย่อยUIViewControllerและมีตัวเริ่มต้นที่กำหนดเองเพื่อให้ฉันตั้งค่าในโค้ดได้อย่างง่ายดาย ฉันประสบปัญหาในการทำสิ่งนี้ใน Swift

ฉันต้องการinit()ฟังก์ชั่นที่ฉันสามารถใช้เพื่อส่งผ่านเฉพาะที่NSURLฉันจะใช้กับตัวควบคุมมุมมอง init(withImageURL: NSURL)ในใจของฉันมันมีลักษณะคล้าย ถ้าฉันเพิ่มฟังก์ชันนั้นจะขอให้ฉันเพิ่มinit(coder: NSCoder)ฟังก์ชันนั้น

ฉันเชื่อว่าเป็นเพราะมันถูกทำเครื่องหมายในซูเปอร์คลาสด้วยrequiredคีย์เวิร์ด? ดังนั้นฉันต้องทำใน subclass? ฉันเพิ่มมัน:

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

ตอนนี้เป็นอย่างไร เริ่มต้นพิเศษของฉันถือเป็นconvenienceหนึ่งหรือไม่? หนึ่งที่กำหนด? ฉันเรียก super initializer หรือไม่? initializer จากคลาสเดียวกัน?

ฉันจะเพิ่มโปรแกรมเริ่มต้นพิเศษของฉันลงในUIViewControllerคลาสย่อยได้อย่างไร


initializers ที่ดีสำหรับการสร้างโปรแกรม viewControllers แต่สำหรับ viewcontrollers สร้างขึ้นผ่านกระดานที่คุณจะออกจากโชคและต้องทำงานทางของรอบมัน
น้ำผึ้ง

คำตอบ:


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 เป็นค่าคงที่แทนได้หรือไม่ ("let imageURL: NSURL?)
Van Du Tran

2
ไม่ทำงาน xCode 7 ตัวสร้างที่จำเป็นล้มเหลวในการเตรียมใช้งานคุณสมบัติระดับคลาสในกรณีของฉันเป็น Int ไม่ใช่ NSURL?
Chris Hayes

1
@NikolaLukic - เราสามารถสร้างตัวอย่างใหม่ของการเรียนโดยการโทรViewController(), ViewController(imageURL: url)หรือโหลดได้จากสตอรี่บอร์ด
ylin0x81

3
ฉันได้เขียนเหมืองเป็น required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }อย่างอื่นที่มีsuper.init(coder: aDecoder)ผมได้รับข้อผิดพลาดของสถานที่ให้บริการself.somePropertyไม่ได้เริ่มต้นที่super.initโทร
น้ำผึ้ง

1
ไม่แน่ใจว่าเกี่ยวข้องกันอย่างไร หากคุณกำลังทำรหัสบริสุทธิ์คุณไม่จำเป็นต้องเติมคุณสมบัติของคุณภายในไฟล์init(coder: aDecoder). fatalErrorพอเพียงประสงค์
น้ำผึ้ง

26

สำหรับผู้ที่เขียน UI ในโค้ด

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

คล้ายกับคำตอบอื่น ๆ มาก แต่มีคำอธิบายบางส่วน คำตอบที่ได้รับการยอมรับนั้นทำให้เข้าใจผิดเนื่องจากคุณสมบัติเป็นทางเลือกและไม่เปิดเผยความจริงที่ว่าคุณinit?(coder: NSCoder)ต้องเริ่มต้นคุณสมบัติแต่ละรายการและวิธีแก้ปัญหาเดียวที่มีคือfatalError(). ในที่สุดคุณสามารถหลีกเลี่ยงได้โดยการสร้างตัวเลือกคุณสมบัติของคุณ แต่นั่นไม่ได้ตอบคำถามของ 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")
    }
}

โดยทั่วไปคุณต้อง ABANDON โหลดจากสตอรี่บอร์ด ทำไม?

เพราะเมื่อคุณเรียก viewController storyboard.instantiateViewController(withIdentifier: "viewController")แล้วUIKitจะทำสิ่งนั้นและโทร

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

คุณไม่สามารถเปลี่ยนเส้นทางการเรียกนั้นไปยังวิธีการเริ่มต้นอื่นได้

เอกสารเมื่อinstantiateViewController(withIdentifier:):

ใช้วิธีนี้เพื่อสร้างออบเจ็กต์ตัวควบคุมมุมมองเพื่อนำเสนอทางโปรแกรม ทุกครั้งที่คุณเรียกใช้เมธอดนี้ระบบจะสร้างอินสแตนซ์ใหม่ของตัวควบคุมมุมมองโดยใช้init(coder:)เมธอด

แต่สำหรับ viewController ที่สร้างโดยโปรแกรมหรือ viewControllers ที่สร้างขึ้นโดยใช้ปลายปากกาคุณสามารถเปลี่ยนเส้นทางการเรียกดังกล่าวได้


5

ตัวเริ่มต้นแบบสะดวกสบายเป็นตัวรองซึ่งสนับสนุนตัวเริ่มต้นสำหรับคลาส คุณสามารถกำหนดตัวเริ่มต้นอำนวยความสะดวกเพื่อเรียกตัวเริ่มต้นที่กำหนดจากคลาสเดียวกันกับตัวเริ่มต้นความสะดวกโดยมีพารามิเตอร์บางตัวของตัวเริ่มต้นที่กำหนดไว้ซึ่งตั้งค่าเป็นค่าเริ่มต้น คุณยังสามารถกำหนดตัวเริ่มต้นอำนวยความสะดวกเพื่อสร้างอินสแตนซ์ของคลาสนั้นสำหรับกรณีการใช้งานเฉพาะหรือประเภทค่าอินพุต

พวกเขามีเอกสารที่นี่


0

หากคุณต้องการเริ่มต้นที่กำหนดเองสำหรับป๊อปโอเวอร์ตัวอย่างเช่นคุณสามารถใช้แนวทางต่อไปนี้:

สร้างการเริ่มต้นแบบกำหนดเองที่ใช้ super init ด้วย nibName และบันเดิลและหลังจากนั้นเข้าถึงคุณสมบัติมุมมองเพื่อบังคับให้โหลดลำดับชั้นของมุมมอง

จากนั้นในฟังก์ชัน viewDidLoad คุณสามารถกำหนดค่ามุมมองด้วยพารามิเตอร์ที่ส่งผ่านในการเริ่มต้น

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)
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.