iOSプログラミング with Swift 2


2016.06.30: created by

Swiftでdelegateを持つUIViewControllerを作る (前の画面に情報を返す)

遷移元、遷移先の画面を管理する UIViewController をそれぞれ VIewController クラスのインスタンス vc1、 ViewController2 クラスのインスタンス v2 で表すものとします。

vc2を実行中にvc1に情報を書き戻すには次のようにします。

  1. vc1に、受け取る情報を仮引数とするメソッド (ここでは func doFromChild(AnyObject info) -> void {...})を定義する。
  2. vc2 にUIViewController型の delegate プロパティ変数を用意しておく。
  3. vc1 から vc2 に画面を遷移するとき、vc2 中の delegate プロパティ変数にself(= vc1) を代入する。
  4. vc2で情報をvc1に書き戻したいタイミングで delegateから vc1 を取り出し、 その中のdoFromChild()メソッドを呼び出します。渡したい情報はメソッドの実引数として与えます。
    1. delegate がnilでないことを確認する。
    2. if delegate == nil { ... }
    3. delegate を ViewController にキャストできることを確認する。
    4. let vc1: ViewController = delegate as! ViewController 
    5. メソッドを呼び出す
    6. vc1.doFromChild(情報)

  1. Xcode を起動して "Create a new Xcode project" で "Single View Application" として新しいプロジェクトを開きます。 ここではプロジェクト名を SwiftDelegate としています。



  2. ViewController の新しいサブクラスを作ります。
  3. project navigator のプロジェクト名の上でマウスを右ドラッグして "New File..." を選択します。 iOS の Source で Cocoa Touch Class を選んで Next をクリックします。 Class: には "ViewController2", Subclass of: には "UIViewController" Language: には "Swift" を選びます(クラス名は自由に選んで構いません)。 ViewController2.swift がプロジェクトに追加されます。













  4. Main.storyboard上の ViewController の右隣に、ウィンドウ右下の "Object library" から "View Controller" をドラッグして配置します。 さらに、新しいView Controller が選択されている状態でウィドウ右上の identity inspector からCustom cluss: に今作成した ViewController2 を設定します。 また、Identity の Storyboard IDに ViewController2 と名前をつけます(この名前は自由につけて構いません)。






  5. Storyboard上の左側の元の ViewController 上に Button とLabel を配置します。



  6. Main.storyboard 上の左側の古い ViewController 上に配置した "Go" Button と Label を、ViewController.swift 中にリンクします。
  7. >
    "Go" Button Action (Touch Up Inside) goBack()関数
    Label Outlet myLabel変数



  8. Storyboard上の右側の新しい ViewController2 上に Button を配置し、表示を"Back"に変更します。



  9. Storyboard上の右側の新しい ViewController2 上の "Back" Button を、 ViewController2.swift 中に Action (Touch Up Inside) で tapBack関数にconnectします。



  10. ViewController.swift を変更します。
  11. delegateで呼ばれるメソッド doFromChild()を定義します。

    ボタンがタップされると、まずViewController2 を生成してその delegate プロパティに自分(self, ViewControllerインスタンス)を設定し、 それから画面遷移します。

    ViewController.swiftに追加するコード(赤字部分)
    import UIKit
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var myLabel: UILabel!
        @IBAction func tapButton(sender: AnyObject) {
            let storyboard = UIStoryboard(name:"Main",bundle:nil)
            let controller:ViewController2 = storyboard.instantiateViewControllerWithIdentifier("ViewController2") as! ViewController2
            controller.delegate = self
            self.presentViewController(controller, animated: true, completion: nil)
        }
        
        func doFromChild(sender: AnyObject) {
            myLabel.text = String(sender)
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
         }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
    
    }
    
  12. ViewController2.swift を変更します。
  13. ボタンがタップされると、delegate プロパティに値が設定されている場合は ViewController にキャストして、その中の doFromChildメソッドを呼び出します。引数にはここでは現在時刻を与えています。

    ViewController2.swiftに追加するコード(赤字部分)
    import UIKit
    
    class ViewController2: UIViewController {
        
        var delegate: UIViewController!
    
        @IBAction func tapBack(sender: AnyObject) {
            if delegate != nil {
                print("fire delegate")
                let controller: ViewController = delegate! as! ViewController
                controller.doFromChild(NSDate())
            }
            dismissViewControllerAnimated(true, completion: nil)
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
    
    }
    
  14. ViewController2.delegate.doFromChild()関数を実行しているときに画面に表示されているのは ViewController2 クラスのインスタンスです。 これを消して、元の ViewControllerクラスのインスタンスの画面に戻すには
        dismissViewControllerAnimated(true, completion: nil)
    
    を実行する必要があります。この例では ViewController2.tapBack() 関数の最後で実行していますが、 delegate として実行される ViewController.doFromChild() 関数の最後で行ってもよいかもしれません。
  15. たとえば、UIImagePickerクラスを利用するときなどは delegate 先のクラスで imagePickerContoller:didFinishPickingMediaWithInfo: が呼び出されますが、 その最後で dismissViewControllerAnimated()を呼び出して表示を元の画面に戻すのが普通です。

  16. プログラムを実行します。
    1. 最初の画面にはラベルとボタンが表示されます。
    2. この状態でボタンをタップすると新しい画面に遷移します(下から出てくる)。
    3. ここで "Back" ボタンをタップすると元の画面に戻るのですが、ラベルの表示が現在時刻に変更されています。
    4. この操作を繰り返すたびに、ラベルの表示は Back ボタンをタップしたときの現在時刻に更新されます。

    この処理は、BackボタンをタップしたときにViewController2 のdelegateプロパティの doFromChild() 関数が呼び出されることで実現されています。


    --> --> --> -->

  17. サンプルのプロジェクトはこちら。(Xcode 7.3.1版)


http://karel.tsuda.ac.jp