2016-02-10 4 views
33

UIViewControllerのサブクラス用のカスタムinitの作成に問題があります。基本的には、プロパティを直接設定するのではなく、viewControllerのinitメソッドを使用します。viewControllerB.property = valueスウィフトのUIViewControllerのカスタム初期設定

は、だから私は、私のViewControllerのカスタム初期化を行い、ビューコントローラ・インタフェースは、ストーリーボードに常駐

init(meme: Meme?) { 
     self.meme = meme 
     super.init(nibName: nil, bundle: nil) 
    } 

スーパー指定のinitを呼び出して、私はまた、カスタムクラスのためのインターフェースが私のビューコントローラであることを行いました。 Swiftでは、このメソッド内で何もしていない場合でも、このinitメソッドを呼び出す必要があります。そうしないと、コンパイラは...私はそれがすべての私のViewControllerにプロパティを初期化しませんMyViewController(meme: meme)とにinit私のカスタムを呼び出すようにしようとすると問題がある...私がしようとしていた

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

文句を言うだろうデバッグ、私は私のviewControllerで、init(coder aDecoder: NSCoder)が最初に呼び出され、私のカスタムのinitが後で呼び出されるのが見つかりました。しかし、これらの2つのinitメソッドは異なるselfメモリアドレスを返します。

私のviewControllerのinitに何か間違っていると思われていますが、init?(coder aDecoder: NSCoder)で常にselfが返されます。これには実装がありません。

誰でもあなたのviewControllerのためのカスタムのinitを作る方法を知っていますか? 注:私のViewControllerのインターフェースはストーリーボード

ここ

に設定されている私のViewControllerコードです:牛た回答のカップルは、もともとあった

class MemeDetailVC : UIViewController { 

    var meme : Meme! 

    @IBOutlet weak var editedImage: UIImageView! 

    // TODO: incorrect init 
    init(meme: Meme?) { 
     self.meme = meme 
     super.init(nibName: nil, bundle: nil) 
    } 

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

    override func viewDidLoad() { 
     /// setup nav title 
     title = "Detail Meme" 

     super.viewDidLoad() 
    } 

    override func viewWillAppear(animated: Bool) { 
     super.viewWillAppear(animated) 
     editedImage = UIImageView(image: meme.editedImage) 
    } 

} 
+0

あなたは、このためのソリューションを手に入れましたか? – user481610

答えて

5

彼らは基本的に正しかったにもかかわらず投票して削除。答えは、あなたはできません。

ストーリーボードの定義から作業する場合、ビューコントローラーのインスタンスはすべてアーカイブされます。したがって、それらを初期化するには、init?(coder...を使用する必要があります。 coderは、すべての設定/表示情報がどこから来るかを示します。

この場合、カスタムパラメータで他のinit関数を呼び出すこともできません。 segueを準備するときにプロパティとして設定するか、seguesを削除してストーリーボードからインスタンスを直接ロードして設定する必要があります(基本的にはストーリーボードを使用したファクトリパターン)。

SDKの必須init関数を使用し、後で追加パラメータを渡すことがあります。

public protocol NSCoding { 

    public func encode(with aCoder: NSCoder) 

    public init?(coder aDecoder: NSCoder) // NS_DESIGNATED_INITIALIZER 
}  

だからUIViewControllerは、2つの指定されたイニシャライザinit?(coder aDecoder: NSCoder)init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)

1

UIViewControllerクラスは、として定義されるNSCodingプロトコルに準拠します。

ストーリーボルダーは、とUIViewに直接init?(coder aDecoder: NSCoder)を呼び出します。パラメーターを渡す余地はありません。

一つ面倒な回避策は一時的なキャッシュを使用することです:

class TempCache{ 
    static let sharedInstance = TempCache() 

    var meme: Meme? 
} 

TempCache.sharedInstance.meme = meme // call this before init your ViewController  

required init?(coder aDecoder: NSCoder) { 
    super.init(coder: aDecoder); 
    self.meme = TempCache.sharedInstance.meme 
} 
12

あなたはストーリーボードから初期化するときは、init?(coder aDecoder: NSCoder)を使用して、カスタムの初期化子を使用することはできませんが、Appleは、コントローラを初期化するためにストーリーボードを設計された方法です。ただし、UIViewControllerにデータを送信する方法があります。

あなたのビューコントローラの名前にはdetailが含まれているので、別のコントローラから取得したとします。この場合、あなたは、詳細にデータを送信するためにprepareForSegueメソッドを使用することができます(これはスウィフト3):

override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) { 
    if segue.identifier == "identifier" { 
     if let controller = segue.destinationViewController as? MemeDetailVC { 
      controller.meme = "Meme" 
     } 
    } 
} 

私はちょうどタイプStringの代わりに、テスト目的のためにMemeのプロパティを使用していました。また、正しいセグの識別子("identifier"は単にプレースホルダだった)を渡してください。

8

私がこれをやった一つの方法は、便利なイニシャライザです。

class MemeDetailVC : UIViewController { 

    convenience init(meme: Meme) { 
     self.init() 
     self.meme = meme 
    } 
} 

は、その後、あなたがlet memeDetailVC = MemeDetailVC(theMeme)

Apple's documentation on initializersであなたのMemeDetailVCを初期化することはかなり良いですが、私の個人的な好みは、あなたの様々な初期化オプションの説明/例の多くを与え、「適切なはずですRay Wenderlich: Initialization in Depthチュートリアルシリーズです"物事を行う方法。


EDIT:カスタムビューコントローラ上の利便性の初期化子を使用できますが、誰もがストーリーボードからか、ストーリーボードセグエて初期化するときは、カスタム初期化子を使用できないことを示すで正しいです。

インターフェイスがストーリーボードに設定されていて、コントローラをプログラムで完全に作成している場合、対処する必要がないため、簡単なイニシャライザがおそらく最も簡単な方法ですNSCoderで必要なinit(これは私がまだ理解していない)です。

ストーリーボード経由でビューコントローラを取得する場合は、@Caleb Kleveter's answerに従い、ビューコントローラを目的のサブクラスにキャストしてプロパティを手動で設定する必要があります。

+4

stay ponyboy – thexande

11

上記の回答のいずれかで指定されているように、カスタムinitメソッドとストーリーボードの両方を使用することはできません。 しかし、静的メソッドを使用して、ストーリーボードからViewControllerをインスタンス化し、それに対する追加設定を実行することはできます。 それは次のようになります。

class MemeDetailVC : UIViewController { 

    var meme : Meme! 

    static func makeMemeDetailVC(meme: Meme) -> MemeDetailVC { 
     let newViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("IdentifierOfYouViewController") as! MemeDetailVC 

     newViewController.meme = meme 

     return newViewController 
    } 
} 

はストーリーボードのビューコントローラ識別子としてIdentifierOfYouViewControllerを指定するのを忘れないでください。 また、viewDidLoadはまだその時点で初期化されていないため、meme varをviewDidLoadで使用しないでください。 上記のコードでストーリーボードの名前を変更する必要がある場合もあります。

+0

MemeDetailVCを初期化した後、プロパティ「meme」が存在します。次に、依存性注入が行われ、 "meme"に値が割り当てられます。現時点では、loadView()とviewDidLoadは呼び出されていません。これらの2つのメソッドは、階層を表示するためにMemeDetailVC add/pushの後に呼び出されます。 –

2

@Caleb Kleveterが指摘しているように、ストーリーボードから初期化するときにカスタムイニシャライザを使用することはできません。

しかし、ストーリーボードからビューコントローラオブジェクトをインスタンス化してビューコントローラオブジェクトを返すファクトリ/クラスメソッドを使用して問題を解決できます。 これはかなりクールな方法だと思います。

注:これは、問題を解決するための回避策ではなく、正確な回答ではありません。

メイククラスメソッドは、MemeDetailVCクラスで、次のように:

// Considering your view controller resides in Main.storyboard and it's identifier is set to "MemeDetailVC" 
class func `init`(meme: Meme) -> MemeDetailVC { 
    let storyboard = UIStoryboard(name: "Main", bundle: nil) 
    let vc = storyboard.instantiateViewController(withIdentifier: "MemeDetailVC") as? MemeDetailVC 
    vc.meme = meme 
    return vc 
} 

使用法:

let memeDetailVC = MemeDetailVC.init(meme: Meme()) 
関連する問題