Swift에서 UIViewController 하위 클래스에 대한 사용자 정의 초기화를 어떻게 만듭니 까?


95

이전에이 질문을 받으 셨다면 죄송합니다. 저는 많은 곳을 검색했으며 상황이 달랐을 때 이전 Swift 베타에서 많은 답변을 얻었습니다. 확실한 답을 찾을 수없는 것 같습니다.

하위 클래스를 UIViewController만들고 코드에서 쉽게 설정할 수 있도록 사용자 지정 이니셜 라이저를 갖고 싶습니다 . Swift에서이 작업을 수행하는 데 문제가 있습니다.

뷰 컨트롤러와 함께 사용할 init()특정을 전달하는 데 사용할 수 있는 함수를 원합니다 NSURL. 내 마음에는 마치 init(withImageURL: NSURL). 해당 기능을 추가하면 init(coder: NSCoder)기능 을 추가하라는 메시지가 표시 됩니다.

required키워드로 수퍼 클래스에 표시되어 있기 때문이라고 생각 합니다. 그래서 서브 클래스에서해야하나요? 나는 그것을 추가합니다 :

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

이제 뭐? 내 특수 이니셜 라이저가 convenience하나로 간주 됩니까? 지정된 사람? 슈퍼 이니셜 라이저를 호출합니까? 같은 클래스의 이니셜 라이저?

특수 이니셜 라이저를 UIViewController서브 클래스 에 어떻게 추가 합니까?


이니셜 라이저는 프로그래밍 방식으로 생성 된 viewController에 적합하지만, 스토리 보드
Honey

답변:


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 2015

2
xCode 7이 작동하지 않습니다. 필요한 생성자가 클래스 수준 속성을 초기화하지 못합니다. 제 경우에는 NSURL이 아닌 Int입니까?
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
그게 어떤 관련이 있는지 잘 모르겠습니다. 순수 코드를 수행하는 경우 .NET Framework 내부에 속성을 채울 필요가 없습니다 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")
    }
}

기본적으로 스토리 보드에서로드하는 것을 포기해야합니다. 왜?

viewController storyboard.instantiateViewController(withIdentifier: "viewController")를 호출하면 UIKit 이 작업을 수행하고

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

해당 호출을 다른 init 메서드로 리디렉션 할 수 없습니다.

의 문서 instantiateViewController(withIdentifier:):

이 메서드를 사용하여 프로그래밍 방식으로 표시 할 뷰 컨트롤러 개체를 만듭니다. 이 메서드를 호출 할 때마다 다음을 사용하여 뷰 컨트롤러의 새 인스턴스를 만듭니다.init(coder:) .

그러나 프로그래밍 방식으로 생성 된 viewController 또는 nib 생성 된 viewController의 경우 위와 같이 해당 호출을 리디렉션 할 수 있습니다.


5

편의 이니셜 라이저는 보조 이니셜 라이저로 클래스에 대한 이니셜 라이저를 지원합니다. 지정된 이니셜 라이저의 일부 매개 변수를 기본값으로 설정하여 편의 이니셜 라이저와 동일한 클래스에서 지정된 이니셜 라이저를 호출하도록 편의 이니셜 라이저를 정의 할 수 있습니다. 또한 편의 이니셜 라이저를 정의하여 특정 사용 사례 또는 입력 값 유형에 대한 해당 클래스의 인스턴스를 만들 수 있습니다.

그들은 문서화되어 있습니다 있습니다 .


0

예를 들어 팝 오버에 대한 사용자 정의 초기화가 필요한 경우 다음 접근 방식을 사용할 수 있습니다.

nibName 및 bundle과 함께 super init를 사용하는 사용자 지정 초기화를 만들고 그 후에 view 속성에 액세스하여 뷰 계층 구조를 강제로로드합니다.

그런 다음 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.