リブートキャンプ by Swift 目次

 

 というわけで、画面をUIViewControllerごと切り替えるのが、iOSアプリの流儀なわけですが…

 切り替えた先でユーザーが何かを指定した場合に、どうやってそれを切り替え元に知らせるのか?

 例えば今回なら、表示した地図でユーザーに移動先を選択してもらうって利用形態(格好つけてユースケースと言ってもいい)が考えられるけど

注意)あんま普段からカッコつけてると、自分でも気づかないうちに「EC、ソーシャルメディアとリアルを融合させたオムニチャネル活用により、個々の顧客のニーズに合わせた新たなカスタマー・ エクスペリエンス…」とか喋り出してしまうようになる。

 

 その場合、地図表示担当のMapViewControllerから戻ってきたViewController側は、MapViewControllerコントロール下でユーザーが選んだ目的地をどうやって知ればいいのか?

 

 

 MapViewController側でユーザーがおこなった操作を、ある程度ViewController側も把握したいわけで、これは前に紹介したターゲットアクションデザインパターンと同じくオブジェクト間の連携問題となります。

注意)ターゲットアクションデザインパターンがわからん人は「タップしてドン」を読みなさい。

 こういったときにAppleが推奨してる作法が、デリゲートデザインパターンっす。

 

デリゲートデザインパターン

 ターゲットアクションデザインパターンが、イベントが発生した時用の

 

  連携オブジェクト(ターゲット)

  連携メソッド(アクション)

 

の2つを連携先に登録するやり方だったのに対し、デリゲートデザインパターンは、最初に

 

 イベントA発生時は、連携オブジェクトのメソッドAを呼び出す

 イベントB発生時は、連携オブジェクトのメソッドBを呼び出す

   ・・・

 

といった取り決めをしておいて

 

  連携オブジェクト(今回ならViewController)

 

だけを連携先(今回ならMapViewController)に登録するやり方です。

 この連携オブジェクトのことをデリゲート(delegate:イベントに対する動作を委譲する相手)と呼びます。

 なので、デリゲートとなる連携オブジェクトはあらかじめ取り決められたメソッドを装備してないダメってことになります。

 

 

 

 例えば次のような取り決めをしたならば

 

取り決め:

 MapViewControllerは地図画面でユーザーが目的地をタップしたら

 

  func selectDistination(name:String);

 

 というメソッドで、連携先に目的地の名前を知らせる。

 

MapViewController側から見てデリゲートとなるViewController側ではselectDestination(name:)メソッドを用意しておく必要があるわけです。

class ViewController: UIViewController {
   ・・・
  func selectDestination(name:String) {・・・}
}

 

 でもってMapViewControllerでは、デリゲートを特定するためにViewController型のプロパティを用意し外部から指定可能にしておく。プロパティの名前はなんでもいいんだけど、わざわざ関係のない名前にしてもしょうがないので普通はdelegateって名前にします。

class MapViewController: UIViewController {
    var delegate:ViewController?
  ・・・
}

注意)ViewController後ろの?の意味がわからん人は「アンラップしてチン♪」を読みましょう。

 

 こうしておいてMapViewController作った時に、次のようにViewControllerをdelegateプロパティに設定してやれば

class ViewController: UIViewController {
    ・・・
    @objc func showAsView() {
        let mapViewController = MapViewController()
        mapViewController.delegate = self
        self.present(mapViewController, //  mapViewControllerに切り替え
            animated: true)    //  切り替えはアニメーションさせる
    }

 あとはMapViewController側で地図画面タップされた時に、目的地名を決めてselectDestination(name:)を呼び出してやればいいわけですよ。

class MapViewController: UIViewController {
    var delegate:ViewController?
  ・・・
    @objc func tap(tapGestureRecognizer:UITapGestureRecognizer) {
        let location = tapGestureRecognizer.location(in: self.view)
                   ↓とりあえず、タップ位置を名前にする
        self.delegate?.selectDestination(name:"\(location)") 
    }

 

注意)UITapGestureRecognizerが何かわからん人は「タップしてドン」を読みましょう。

 

 こうするとViewControllerは、MapViewControllerの地図画面上でユーザーが指定した目的地を知らせてもらえることになるわけです。

class ViewController: UIViewController {
   ・・・
    func selectDestination(name:String) {
        print(name)  ←コンソールに名前を出力
    }
}

サンプル:

http://tetera.jp/xcc/book-sample/delegate-pre.zip

 

 

 

 ちなみに

 

     "\(location)"


というように、文字列中に  \( (バックスラッシュとオープンバーレン)と ) (クローズバーレン)で挟んでプロパティ名を書くと、そのプロパティの値が文字列として設定されます。結果として、例えばlocationのx、yに[100, 200]が設定されていたら

 

     "(100.0, 200.0)"

 

て感じの文字列になり、これがコンソールに出力されます。\(プロパティ名)は、どこでも使えるので、こんな感じでもいい

 

     "location は \(location) です"

 

この場合、

 

     "location は(100.0, 200.0)です"

 

という文字列になる。

 

 ただ〜、これだとまるで汎用性がないわけでして…

 MapViewControllerのdelegateプロパティに指定できるのはViewControllerオブジェクトか、その派生オブジェクトしかできないってことになっちゃうんですな。

 で、そこらへんに汎用性を持たせるために、Appleが提唱するデリゲートデザインパターンでは「これこれのイベントが発生したら、連携先オブジェクトの、このメソッドを呼び出す」という取り決めを、プロトコルとして表現しておき

protocol MapViewControllerDelegate {
    func selectDistination(name:String)
}

注意)プロトコルがわからん人は「重箱の隅をつつくようにネチネチと進めてみる」を読みましょう。

 

 連携先は、このプロトコルを採用するものとする。

class ViewController: UIViewController, MapViewControllerDelegate {
   ・・・
    func selectDistination(name:String) {
        print(name)
    }
}

 ということにしたんですな。

 こうすると、MapViewController側はViewControllerではdelegateプロパティとして、ViewControllerじゃなくプロトコルであるMapViewControllerDelegate型を指定することができ

class MapViewController: UIViewController {
    var delegate:MapViewControllerDelegate?
   ・・・

サンプル:

http://tetera.jp/xcc/book-sample/delegate.zip

 

 

 MapViewControllerDelegateを採用したオブジェクトなら、どれでも指定可能になるわけです。

 なので、例えば次のようにして目的地選択専用のオブジェクトを用意することだってできる。

//  MapViewControllerDelegateを採用した別クラス
class AltDelegate : MapViewControllerDelegate {
    func selectDestination(name:String) {
        print("タップ位置は" + name + "です")
    }
}

class ViewController: UIViewController, MapViewControllerDelegate {
   ・・・
    //  別のデリゲートオブジェクト
    let altDelegate = AltDelegate()
    
    @objc func showAsView() {
        let mapViewController = MapViewController()
        mapViewController.delegate = altDelegate
        self.present(mapViewController, //  mapViewControllerに切り替え
            animated: true)    //  切り替えはアニメーションさせる
    }

 self、altDelegate、どちらでもdelegateに設定可能。

 

サンプル:

http://tetera.jp/xcc/book-sample/delegate-alt.zip

 

 

 こんな感じでプロトコルとその採用を導入することで、かなり汎用性が出てくるわけです。これがデリゲートデザインパターン。

 

 あと、慣例としてデリゲート側プロパティにはweakをつけます。

class MapViewController: UIViewController {
    weak var delegate:MapViewControllerDelegate?

 これは、自分はdelegateに指定されたオブジェクトを所有しないという意思表示です。逆に言えばweakをつけてない場合は、delegateに指定されたオブジェクトはMapViewControllerオブジェクトに所有されることになり、MapViewControllerオブジェクトが存在する限り、勝手に消滅したりはできなくなります。

 これ自体は、MapViewControllerにとってありがたい事なんだけど、一点だけ懸念事項があって…

 それはdelegateで指定されるオブジェクトが、MapViewControllerオブジェクト側を同じようにプロパティで所有してた場合です。

 こうなると互いに所有しあって、いつまでもオブジェクトが消滅できなくなる可能性が出てくるんですな。

 

 

 オブジェクトは用が済めば静かに退場するのが礼儀というものなんですが、互いに所有しあってると、どちらも消滅しようとしても消滅できなくなるんですよ。

 

 

 

 オブジェクトを用意できる最大数はiPhoneの機種によって違うけど、いずれにせよ限界はあるわけで、古参がいつまでも居座り続けるのは良くないです。

 ここら辺を防ぐためにweakをつけます。

 

 

 で、weakをつけたプロパティは設定されているオブジェクトが消滅するときはnilが設定されることになるので、利用するときに毎回アンラップして使うのが安全ってことになります。

 これはすでに実装済み。

class MapViewController: UIViewController {
   ・・・
    @objc func tap(tapGestureRecognizer:UITapGestureRecognizer) {
        let location = tapGestureRecognizer.location(in: self.view)
        self.delegate?.selectDestination(name:"\(location)")
    }

 最後に、weakをつけられるプロパティには条件があって、オブジェクトでないとダメなんですな。そのためプロトコルを定義するときにAnyObjectを採用します。

protocol MapViewControllerDelegate : AnyObject {
    func selectDistination(name:String)
}

 これでプロトコルMapViewControllerDelegateはクラスでしか採用できなくなり、結果、MapViewControllerDelegate型は必ずオブジェクトが設定されることが保証されweakがつけられるようになります。

 もっとも、このプロトコルをObjective-Cでも利用できるようにしたいなら、その点を保証するためのキーワード@objcをつけることになり、このキーワードには暗黙裡にAnyObjectを継承させる指定も含まれるので

@objc protocol MapViewControllerDelegate {
    func selectDistination(name:String)
}

と書くのがよく見られる書き方ってことになります。

 

サンプル:

http://tetera.jp/xcc/book-sample/delegate-weak.zip

 

というわけで、これでAppleの3大オブジェクト連携手法が出揃ったわけですな。

 

 ターゲットアクションデザインパターン

 通知

 デリゲートデザインパターン

 

 ここら辺を理解できたら、大概のサンプルは何やってるかわかるんじゃないかと思われ。あとはストーリーボードってことになるけど、これはまた次回。

 

 

リブートキャンプ by Swift 目次

 

 てなわけで、UIViewを派生させたMapViewクラスを定義しておいて、画面タップされたら、ViewControllerのself.viewの上に表示させてるのが前々回のサンプルってわけなんですが

 

 ↓こいつね

サンプル:

http://tetera.jp/xcc/book-sample/modalview.zip

 

 これだとMapViewオブジェクト作るとき、必ず"map"って名前の画像がいるよね。

override init(frame: CGRect) {
        super.init(frame:frame)
        let subview = UIImageView(image:UIImage(named:"map"))
         ・・・

 画像を使う使わないや、どの画像を使うかは、使う側で好きに変更できる方が汎用性があっていいよね〜、て話になるわけですよ。

 そうしておくとMapViewに別のアプリで使いまわせるかもしれない。

 それ以前に、使う画像の用意の仕方がわからん人は「スクールカーストとかヒエラルキとか」を読みなさい。

 

 考え方自体は正しいし、技術的にも難しいことじゃなくて、subviewを外から触れるようにプロパティにしちゃうだけで解決しちゃう問題なんだけど…

    let subview = UIImageView() ←これで解決
    override init(frame: CGRect) {
        super.init(frame:frame)
        let subview = UIImageView(image:UIImage(named:"map"))
        subview.frame = self.bounds ← ま、それと画像を設定しないので、矩形を
                   明示的に設定する必要もあるわけだが
        subview.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    ↑ 親UIViewのリサイズに合わせて自分もリサイズする指定もしておく
     この2点は元々の処理でもつけておくべきだった
         ・・・

autoresizingMaskプロパティの意味がわからん人は「重箱の隅をつつくようにネチネチと進めてみる」を読みましょう。

 

 いやしかし、そうするとViewController側でsubviewのimageに画像設定する必要あるよねって話になるわけで…

    @objc func showAsView() {
        let mapView = MapView(frame:self.view.bounds)
        mapView.subview.image = UIImage(named:"map") ←追加作業
        mapView.frame.origin.y = mapView.frame.maxY

 いや、いいんすけどね。でもViewController側で作業増やしたくないってことでMapViewクラス用意したんじゃん、ブツブツ…

 

 という葛藤が発生するわけですな。

 実際の凝ったゲームだとUIImageを1個設定するくらいじゃ済まなんだろうし…

class MapView: UIView {
    override init(frame: CGRect) {
        super.init(frame:frame)
                ・  ← MapView側でやらせようとしてたいろいろな作業が…
                ・
                ・
 
class ViewController: UIViewController {
   ・・・
    @objc func showAsView() {
        let mapView = MapView(frame:self.view.bounds)
    ↓ ViewController側でやることになっちゃう…、って、それじゃ元の木阿弥
        mapView.subview.image = UIImage(named:"map")
                ・  
                ・
                ・

 MapViewに汎用性持たせつつ、ViewController側での作業は増やしたくないとか、どんだけわがまま…

 

 これに対しAppleが提案してるのが「UIViewControllerとUIViewのセットもう一個用意して、UIViewControllerごと切り替えちゃえば良くってよ」ていうもんです。

 

 

 

 例えばMapViewを設定する側のUIViewControllerを、MapViewControllerとして用意して、そこのviewDidLoadで、MapViewは設定(subviewのimageとか)しちゃう。

class MapViewController: UIViewController {
 
    override func viewDidLoad() {
        super.viewDidLoad()
        let mapView = MapView(frame:self.view.bounds)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.view.addSubview(mapView)
        ・・・

 こうすると、MapViewControllerクラスを余分に定義することになるけど、そのviewDidLoadでやってることは、結局MapViewクラス内でやる予定の、今回のアプリに依存する部分を分離してるだけなんで、作業的にはそう変わるもんじゃない。

 

 

 で、アプリに依存する部分を分離した結果、MapViewは汎用性を持つことになって、別のアプリでも使える可能性が出てくる。別のアプリではやっぱり同じようにそのアプリ専用のMapViewControllerクラスを作ることにはなるんだけど、MapView部分は使いまわせる。

 

 

 こういった使い回しができることは、MapViewがスクロール可能になったり、ズーム可能になったりと多機能になっていくほど、ありがたみが出てくるわけです。

 

 とまあ、そういった理由でAppleはUIViewControllerごとの切り替えを推奨してて、そのための便利な機能が、UIViewControllerにいろいろ提供されてもいるわけです。

 その1つがUIViewControllerをまるごろ切り替えるためのメソッド

 

 func present(_ viewControllerToPresent: UIViewController, 

  animated flag: Bool, completion: (() -> Swift.Void)? = nil)

 

  viewControllerToPresent:切り替え先のUIViewControllerオブジェクトを指定

  animated:切り替え時にアニメーションするならtrue

  completion:切り替えアニメーション完了直後に何かしたければクロージャを設定

  クロージャがわからん人は「タップしてドン」を読みなさい。

 

 completion部分は何も指定しないならnilを指定なんだけど、その場合、省略もできて、例えばViewControllerでMapViewController作って切り替えるならこんな感じ

class ViewController: UIViewController {
  ・・・
    @objc func showAsView() {
        let mapViewController = MapViewController()
        self.present(mapViewController, //  mapViewControllerに切り替え
            animated: true)    //  切り替えはアニメーションさせる
    }

 でもって、切り替えられたUIViewControllerから、切り替え元に戻るためのメソッド

 

 func dismiss(animated flag: Bool, 

  completion: (() -> Swift.Void)? = nil)

 

  animated:切り替え時にアニメーションするならtrue

  completion:切り替えアニメーション完了直後に何かしたければクロージャを設定

 

なわけです。

 

で、MapViewController側ではUISwipeGestureRecognizerをMapViewからこっちに持ってきてスワイプされたらdismissメッセージを自分に送る。

class MapViewController: UIViewController {
   ・・・
    @objc func swipe() {
        self.dismiss(animated: true, completion: nil)
    }

 こんな感じでcompletion:を省略せずにnilを指定でもいい。

 ま、詳細はサンプルソース見てください。

 Git使ってMapView単独版からの変化を比較できるようにしてます。

 なんスカそれ、Gitって美味しいの?な人は「キャッチして」を読みましょう。

 

サンプル:

http://tetera.jp/xcc/book-sample/modalviewcontroller.zip

 

 ちなみに、自分でもUIViewControllerを派生させてMapViewControllerクラス定義してみようと思う人。

 MapViewController.swftをプロジェクトに登録するのにはMapView.swiftを登録したやり方を使うんだけど、UIView派生の時と違いUIViewControllerの派生ではXIBファイル作るか聞かれます。

 

 Also create XIB file

 

 てチェックボックスなんだけど、今回は作らないのでチェック外しでよろしく。XIBについてはいずれ。

 

 

ではでは。

 

リブートキャンプ by Swift 目次

 

 まずは、UIViewの派生について。

 クラスの派生自体は「こんにちは世界」で説明したとおりで、普段はUIViewとして振る舞うオブジェクトなんだけど、所々で俺流に振る舞うオブジェクトを作りたい時に使うもんです。

 すでにUIViewContorollerを派生させたViewContorollerを使ってるんで、メソッドのオーバーライドもある程度把握してると思うんだけど、今回のMapViewのように作成時に独自の振る舞いをさせたい場合は、どのメソッドをオーバーライドするのか?

 

 それがMapView.swiftでやってるinitメソッドのオーバーライドっす。

 C++言語みたいに

 

 MapView(frame:CGRect) { ・・・ }

 

とかがあると思ってた?

 残念init(frame:)ちゃんでした。

 initはイニシャライザと呼ばれている特殊なメソッドで、どのクラスでもオブジェクト作成時にはこのinitメソッドが使われる決まりになっています。

 注)クラスオブジェクトのメソッドっぽいつーか、クラスオブジェクトにinitメッセージ送ってオブジェクト作ることもできるんで、多分そうなんだけど、そこんとこの詳細は不明。Appleのドキュメントには、ぱっと見「インスタンスメソッドに似ている」としか書いてないんでインスタンス側のメソッドではないってことは確か。

 

で、それをやってるのが前回のサンプル、MapView.swiftの

 

 ↓こいつね

サンプル:

http://tetera.jp/xcc/book-sample/modalview.zip

 

    override init(frame: CGRect) {

        super.init(frame:frame)

   ・・・

    }

 

てとこなわけですが、イニシャライザは普通のメソッドのオーバーライドほどフリーダムじゃなくて、いくつかの制約がある点には注意が必要っす。
 例えば
 
 1)必ず派生元のイニシャライザを呼び出す必要がある
 2)指定済のイニシャライザしか呼び出せない
 
なんてのがそうで、このうち、1)の制約は派生の初期化メソッドである以上、当たり前の話だけど、2)の方の指定済のイニシャライザって何ですかって話です。
 これは、例えばMapViewクラスに引数なしのイニシャライザ作っちゃおーという風に
 

 class MapView: UIView {

  init() {

   super.init()

  }

 
なんて書くと
 
 
てな風に注意されちゃう制約です。
 指定済のイニシャライザ使えって注意されてるわけでして、UIViewの場合だと
 
 init(frame: CGRect)
 
が指定済のイニシャライザてことになってて、クイックヘルプでも説明(designatedてとこがそう)されてます。
 
 クイックヘルプわからん人は「または私は如何にして心配するのを止めて…」を読むように
 
 ただ、正直、何が指定済のイニシャライザなのか判断する方法は微妙で…
 例えばUIImageViewクラスなんかは
 
  init(image: UIImage?)
 
なんてイニシャライザが追加定義されてるけど、クイックヘルプにdesignatedの単語は見当たらないし、かといってUIImageViewの派生クラスでinit(frame: CGRect)の代わりにinit(image: UIImage?)を呼んでも注意されないし…
 指定済のイニシャライザは、派生元のクラス定義で
 

  convenience … init(…

 

ってな風にconvenienceが付いてないイニシャライザってことで安定なのかな〜と思ったらUIViewの大元の派生クラスのNSObjectには

 

  init()
 
が定義されてるのに、super.init()呼ぶと怒られるし…、どうしろと…
 まあ、直接の派生元でconvenienceが付いてないイニシャライザは大丈夫だろうと…、クイックヘルプにdesignatedの単語があればなおよしって、ことでいいでしょう。
 
 ちなみにconvenienceてのが付くイニシャライザは、内部でself.init(…て感じで、自身の別のイニシャライザを呼ぶイニシャライザです。
 引数の省略なんかで用意したりする。お手軽(convenience:コンビニエンス)ツーわけですな。
 
 
 で、この仕組みは派生クラスでイニシャライザ用意するときに、convenienceじゃないイニシャライザをオーバーライドすれば、convenience側イニシャライザも自動的に派生に対応できちゃう点で優れものなんですが…
 
 
 このsuper.init(…じゃなくて、self.init(…なところが、まさに問題でして、このconvenience側イニシャライザを派生側で、super.init(…、派生元のイニシャライザとして呼んじゃうと、swiftの仕組みでは、派生元のイニシャライザを呼んだつもりが、回り回って派生先のイニシャライザを呼ぶことになるんですよ。
 
 これを避けるために派生先でconvenience側のイニシャライザをsuperで呼び出すのは禁止されてるみたいっす。
 ま、全部完全に防止するできてるわけじゃなく、次のように書いても注意されない点は要注意。
class LoopView : UIView {
    override convenience init(frame: CGRect) {
        self.init()
    }
}
 上の説明が理解できてる人は、このLoopViewを作っちゃうと無限ループになるってのがわかるはず。
 
 let v = LoopView(frame:CGRect(x:0, y:0, width:100, height:100))
 
なんてやると、いつまでもLoopView作成から帰ってこなくなるわけだ。
 それと、UIViewの派生クラスでイニシャライザを用意した場合は

 

 init?(coder aDecoder: NSCoder)
 
のオーバーライドが義務付けられてます。しかもoverrideじゃなく
 

    required init?(coder aDecoder: NSCoder) {

 }
 
という形で書かないといけないというね。
 こいつはUIViewが採用しているNSCodingプロトコルで要求されてるもので、実装が義務付けられてるイニシャライザ。
 プロトコルの採用がわからん人は「重箱の隅をつつくようにネチネチと進めてみる」を読みましょう。
 なので、UIViewの定義ではoverrideにはならないんだけど、派生先(今回ならMapView)でのオーバーライドの義務付けと、そのまた派生先でのオーバーライドの義務付けを指定するためにrequiredのキーワードが必須になるみたいっす。
 本来ならUIViewクラスの定義でもrequiredキーワードを付けるべきなんじゃないかと思うんだけど付いてない。でも、init?(coder aDecoder: NSCoder)を書き忘れるとXcodeに怒られるというね、なぜだ…
 あと、このinit?(coder aDecoder: NSCoder)の?nullを戻す可能性のあるイニシャライザという意味です。この場合のイニシャライザは
 
  return null
 
で戻れるようになってます。
 init?(coder aDecoder: NSCoder)はNSCoderから自分に必要な設定値(今回ならframe:で受け取るCGRectの値など)を読み込んで、自分をイニシャライズするというもので、このNSCoderからの情報取り出しに失敗した時などにnullを返すようになってます。
 なので、このイニシャライザを使う方はオプショナル型としての対応が必要になる。
 
 if let v = UIView(coder:coder) {
 
 }
 
ちゅー感じですね。
 オプショナル型わからん人は「アンラップしてチン♪」を読みなさい。
 というわけでMapViewクラスでもinit?(coder aDecoder: NSCoder)を定義してますが、今の所MapViewクラスでNSCoderからの情報取り出す予定はないので
 

        fatalError("init(coder:) has not been implemented")

 

ってして、致命的なエラーとして「init(coder:)持ってないねん」と報告だけしてアプリが死ぬようにしてます。

 fatalError関数はユーザーから見るとアプリが突然死する状態なんで、使うのは開発中だけにするのが無難でしょう。今回のように絶対MapViewはNSCoderから作成されることは無い、呼び出されたら腹を切りますってくらい自信があるなら残しててもいいのかな?

 私はAppleにアプリを申請するときは、機械的なメッセージ・関数呼び出しのチェックもあるかと思ってinit?(coder aDecoder: NSCoder)側も実装してfatalError関数は取っ払うようにしてるんで、残したままで審査に通るかどうかは知りません。

 init?(coder aDecoder: NSCoder)に付いてはストーリーボードの話の時にでも。

 ちなみにMapViewの派生先でオーバーライドする時もoverride requiredと書く必要はなく
 

    required init?(coder aDecoder: NSCoder) 

 
でOK。requiredは必ずoverrideでもあるんでこれでOKみたいっす。
 
 以上でUIViewクラスの派生先でイニシャライザ実装するのは結構大変だよって話はおしまい。
 
 ゆーても、UIViewクラスの派生先でイニシャライザ実装すると、次のようにXcodeがinit?(coder aDecoder: NSCoder) 抜けてますよって感じで、注意してもくれるし、注意文の先頭にクリックできそうな赤いマークがあれば解決策も見せてくれるようにもなってます。
 
 
そうすっと、こんなん出ます。
 
 
で、ここで出てるFixってボタンクリックすると、とりあえずの実装を用意もしてくれる。
 
 ↓ こんな感じの実装
 

    required init?(coder aDecoder: NSCoder) {

        fatalError("init(coder:) has not been implemented")

    }

 
 
 困ったときは、エラーメッセージの先頭がクリック可能かチェックせよ!
てなわけで、今回はおしまい。