SwiftでUIViewサブクラスのカスタムinitを作成するにはどうすればよいですか?


125

私がしたいとして、サブクラスと。initUIViewStringInt

サブクラス化しているだけの場合、Swiftでこれをどのように実行しUIViewますか?カスタムinit()関数を作成するだけで、パラメーターがStringとIntの場合、「初期化子から戻る前にsuper.init()が呼び出されない」ことがわかります。

呼び出すsuper.init()と、指定された初期化子を使用する必要があると言われます。そこで何を使うべきですか?フレーム版?コーダーバージョン?両方とも?どうして?

回答:


207

init(frame:)バージョンは、デフォルトの初期化子です。インスタンス変数を初期化した後でのみ呼び出す必要があります。このビューがNibから再構成されている場合、カスタム初期化子は呼び出されず、代わりにinit?(coder:)バージョンが呼び出されます。Swiftはrequiredの実装を必要とするようになったためinit?(coder:)、以下の例を更新し、let変数宣言をvarおよびに変更しました。この場合、awakeFromNib()後で、または後で初期化します。

class TestView : UIView {
    var s: String?
    var i: Int?
    init(s: String, i: Int) {
        self.s = s
        self.i = i
        super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    }

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

5
それなら是非作ってくださいvar。しかし、Swiftのデフォルトのベストプラクティスletは、変数を宣言する理由がない限り、変数を宣言することvarです。上記の私のコード例では、そうする理由はありませんでしたlet
ウルフマクナリー2014年

2
このコードはコンパイルされません。必要なイニシャライザを実装する必要がありますinit(coder:)
Decade Moon

3
これが何年も前にどのようにコンパイルされたか興味深い。今日では、init(coder :)で「super.init呼び出しでプロパティself.sが初期化されていません」というメッセージが表示されます
mafiOSo

Swift 3.1の修正例。UIKitをインポートするプレイグラウンドでコンパイルします。
Wolf McNally

1
私が作成した@LightNightとsiここでは物事をシンプルに保つためのオプション。オプションでなかった場合は、必要なイニシャライザで初期化する必要もあります。それらをオプションにすることはnilsuper.init()が呼び出されたときにそれらが存在することを意味します。それらがオプションでなければ、super.init()を呼び出す前に割り当てる必要があります。
Wolf McNally

32

指定された必須の共通initを作成します。便宜上、私init(frame:)はフレーム0で委任します。

フレームがゼロであっても問題はありません。通常、ビューはViewControllerのビュー内にあるためです。カスタムビューは、スーパービューがlayoutSubviews()またはを呼び出したときに、サブビューをレイアウトするための適切で安全な機会を得ますupdateConstraints()。これらの2つの関数は、ビュー階層全体でシステムによって再帰的に呼び出されます。updateContstraints()またはを使用できますlayoutSubviews()updateContstraints()が最初に呼び出され、次にが呼び出されlayoutSubviews()ます。でupdateConstraints()確認してくださいスーパー呼び出すことが最後。ではlayoutSubviews()最初に superを呼び出します。

これが私がすることです:

@IBDesignable
class MyView: UIView {

      convenience init(args: Whatever) {
          self.init(frame: CGRect.zero)
          //assign custom vars
      }

      override init(frame: CGRect) {
           super.init(frame: frame)
           commonInit()
      }

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

      override func prepareForInterfaceBuilder() {
           super.prepareForInterfaceBuilder()
           commonInit()
      }

      private func commonInit() {
           //custom initialization
      }

      override func updateConstraints() {
           //set subview constraints here
           super.updateConstraints()
      }

      override func layoutSubviews() {
           super.layoutSubviews()
           //manually set subview frames here
      }

}

1
動作しないはずです:super.initがselfを初期化する前に、メソッド呼び出し 'commonInit'で 'self'を使用します
surfrider

1
self.init呼び出しの後にカスタム引数を初期化します。私の答えを更新しました。
MH175 2017

1
しかし、commonInitメソッド内のいくつかのプロパティを初期化したいがsuper、この場合、それを配置することはできませんsuper。呼び出しの前にすべてのプロパティを初期化する必要があるためです。笑デッドループのようです。
サーフライダー2017

1
これがSwiftの初期化がよく機能する方法です。「2フェーズ初期化」を検索してください。暗黙的にアンラップされたオプションを使用できますが、お勧めしません。アーキテクチャは、特にビューを扱う場合、すべてのローカルプロパティを初期化する必要があります。このcommonInit()メソッドを何百ものビューに使用しました。動作します
MH175

17

これが私がSwiftのiOS 9でそれを行う方法です-

import UIKit

class CustomView : UIView {

    init() {
        super.init(frame: UIScreen.mainScreen().bounds);

        //for debug validation
        self.backgroundColor = UIColor.blueColor();
        print("My Custom Init");

        return;
    }

    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented"); }
}

ここに例を含む完全なプロジェクトがあります:


2
これはビュー全体のディスプレイを使用します
RaptoX

1
うん!部分的なサブビューに興味がある場合は、私に知らせてください。これも投稿します
J-Dizzle

1
fatalErrorがあることは、必要なinitにコードを配置する必要がないことを意味するため、私はこの答えが一番好きです。
カーターメドリン

1
@ J-Dizzle、部分的なビューの解決策を見たいのですが。
Ari Lacenski、2016

あなたの答えは受け入れられた答えの反対を言っていませんか?つまり、の後super.initにカスタマイズを行うということですが、彼はそれを前に行う必要があると述べましたsuper.init...
ハニー

11

これがSwiftのiOSでサブビューを行う方法です-

class CustomSubview : UIView {

    init() {
        super.init(frame: UIScreen.mainScreen().bounds);

        let windowHeight : CGFloat = 150;
        let windowWidth  : CGFloat = 360;

        self.backgroundColor = UIColor.whiteColor();
        self.frame = CGRectMake(0, 0, windowWidth, windowHeight);
        self.center = CGPoint(x: UIScreen.mainScreen().bounds.width/2, y: 375);

        //for debug validation
        self.backgroundColor = UIColor.grayColor();
        print("My Custom Init");

        return;
    }

    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented"); }
}

4
fatalError()呼び出しの適切な使用。使用されていないイニシャライザからの警告を止めるために、オプションを使用する必要がありました。これはそれを閉じ込めた!ありがとう。
Mike Critchley 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.