SwiftでUIViewControllerサブクラスのカスタム初期化子を作成するにはどうすればよいですか?


93

これが以前に尋ねられたことがある場合はお詫びします。私は何度も検索しましたが、多くの答えは、物事が異なっていた初期のSwiftベータ版からのものです。決定的な答えを見つけることができないようです。

UIViewControllerコードで簡単に設定できるように、サブクラス化し、カスタム初期化子を用意します。Swiftでこれを行うのに問題があります。

ビューコントローラーで使用init()する特定の関数を渡すために使用できる関数が必要ですNSURL。私の心の中でそれは次のように見えますinit(withImageURL: NSURL)。その関数を追加すると、init(coder: NSCoder)関数を追加するように求められます。

これは、スーパークラスでrequiredキーワードが付けられているためだと思いますか?だから私はサブクラスでそれをしなければならないのですか?私はそれを追加します:

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

それで?私の特別な初期化子はconvenience1つと見なされますか?指定されたもの?スーパー初期化子を呼び出しますか?同じクラスのイニシャライザ?

特別な初期化子をUIViewControllerサブクラスに追加するにはどうすればよいですか?


イニシャライザは、プログラムで作成されたviewControllersには適していますが、ストーリーボード
Honey

回答:


157
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を定数にできますか?(「聞かせてimageURLに:?NSURL)
ヴァン・ドゥ・トラン

2
xCode 7が機能しない場合、必要なコンストラクターがクラスレベルのプロパティを初期化できません。私の場合、NSURLではなくIntですか?
クリスヘイズ

1
@NikolaLukic -私たちは呼び出すことで、クラスの新しいインスタンスを作成することができViewController()ViewController(imageURL: url)またはストーリーボードからそれをロードします。
ylin0x81 2016

3
プロパティ呼び出し時に初期化されていないというエラーが発生するので、required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }他の方法でsuper.init(coder: aDecoder)私のものを書か なければなりませんでしたself.somePropertysuper.init
Honey

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

その呼び出しを別のinitメソッドにリダイレクトすることはできません。

上のドキュメントinstantiateViewController(withIdentifier:)

このメソッドを使用して、プログラムで表示するビューコントローラーオブジェクトを作成します。このメソッドを呼び出すたびに、このメソッドを使用してビューコントローラの新しいインスタンスが作成されますinit(coder:)

しかし、プログラムで作成されたviewControllerまたはnibで作成されたviewControllersの場合、上記のようにその呼び出しをリダイレクトできます。


5

便利な初期化子は二次的なもので、クラスの初期化子をサポートします。指定された初期化子を定義して、指定された初期化子のパラメーターの一部をデフォルト値に設定して、指定された初期化子を同じクラスから呼び出すことができます。また、特定のユースケースまたは入力値タイプのそのクラスのインスタンスを作成するための便利な初期化子を定義することもできます。

それらはここに文書化されています


0

たとえば、ポップオーバーのカスタム初期化が必要な場合は、次のアプローチを使用できます。

スーパーinitをnibNameとバンドルで使用するカスタムinitを作成し、その後ビュープロパティにアクセスしてビュー階層のロードを強制します。

次に、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.