MENU

【swift アプリ開発】画面間の値の受け渡し方法( Protocolを使ったdelegate編)

  • URLをコピーしました!
ios-appdevelopment-0004-03

こんにちは!Popoです。

こんな方へのお勧めの記事です。

  • モーダルビューやUIViewで画面間の値の受け渡しをしたい。

前回の記事では、navigationControllerを利用した画面間の値の受け渡しを解説しました。

今回はdelegateを利用した画面間の値の受け渡し方法を取り上げてみたいと思います。

delegateて理解しづらいと思いますが、使いこなせれば便利です。

是非、マスターしておきましょう。

目次

delegateとは何か

delegateとは何か

あるクラスから、他のクラスに処理を「移譲」(Delegate)するパターンのことです。

初心者の方はなかなか理解しづらいと思いますが、たとえば、UITableViewDelegate、UITableViewDataSourceといったようにAPIでもdelegateを利用しています。(他にUITextFieldDelegateもありますね)

実は、日ごろ使用しているんですが、それがdelegateを利用していると気づいていないのです。🤔

下記記事を参考にしました。

delegateを使う理由

delegateを使う理由

理論上は、「処理を他に委任することで、クラス間の依存関係を減らすことができる」です。

下記記事を参考にしました。

delegateを利用する際、下記2点で処理を記述する事になると思います。

  • イベント発生を検知する処理
  • イベント後にさせたい処理

イベント発生後①、イベント発生後の処理②に委譲させたいとなります。

今回の解説の場合、モーダルビューやUIViewをCLOSE後何か処理をさせたいという設計になっています。

  • ①モーダルビューやUIViewをCLOSE
  • ②CLOSE後何か処理をさせたい

私の使い方としては、「遷移先がモーダルビュー、UIView時に利用する」程度の考えですね。

モーダルビューやUIViewで入力された値を、メイン画面のUIViewControllerで使用するような場合に使用します。

アプリ開発の動作環境

アプリ開発の動作環境

今回、アプリ開発を行った環境になります。

項目バージョン
XcodeVersion 14.3 (14E222b)
SwiftSwift version 5.8
MacOSmacOS Ventura バージョン13.3.1(22E261)

サンプルアプリのロジックを実装してみよう!

サンプルアプリのロジックを実装してみよう!

それでは、「遷移先がモーダルビュー、UIView時に利用する」サンプルアプリを開発してみましょう!

概要

ロジックのパターンとしては下記になります。

遷移元遷移先
遷移先のdelegateを宣言
delegateメソッドの処理記述
protocolを定義

delegateを定義
delegateメソッド実行

下記の関係になります。

①イベント発生を検知する処理→遷移先

②イベント後にさせたい処理→遷移元

今回のデモアプリは、UIViewControllerの画面からUIViewの画面に遷移するアプリです。

UIView画面からUIViewController画面に戻る際に、UIViewController画面に引き継いだ値を赤字で表示します。

  • UIViewController画面:OneViewController
  • UIView画面:TwoView

手順1:遷移元画面

遷移元の実装したロジックの全体になります。「②イベント後にさせたい処理」側の処理になります。

全体

全体のソースコードです。

import UIKit

class OneViewController: UIViewController, ①TwoViewDelegate {

    fileprivate var twoView:TwoView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let oneWorkButton = UIButton(type: .custom)
        oneWorkButton.frame = CGRect(x: SCREEN_WIDTH/2 - (SCREEN_WIDTH/3.5)/2, y: SCREEN_HEIGHT/2, width: SCREEN_WIDTH/3.5, height: 40)
        oneWorkButton.backgroundColor = .green
        oneWorkButton.addTarget(self, action: #selector(OneViewController.oneWorkButtonClick(_:)), for: UIControl.Event.touchUpInside)
        oneWorkButton.setTitle("Two画面遷移", for: .normal)
        oneWorkButton.titleLabel?.font =  UIFont.systemFont(ofSize: 15)
        oneWorkButton.setTitleColor(.black, for: .normal)
        oneWorkButton.layer.cornerRadius = 10
        oneWorkButton.layer.borderColor = UIColor.gray.cgColor  // 枠線の色
        oneWorkButton.layer.borderWidth = 1.0 // 枠線の太さ
        self.view.addSubview(oneWorkButton)
        
        //事前に生成
        self.createTwoView()
    }

    //ボタンタップイベント
    @objc fileprivate func oneWorkButtonClick(_ sender: UIButton)
    {
        self.showTwoView()
    }

    //UIViewを生成しておく
    fileprivate func createTwoView()
    {
        self.twoView = TwoView()
        self.twoView.frame = CGRect(x: 0, y: SCREEN_HEIGHT, width: SCREEN_WIDTH, height: SCREEN_HEIGHT)

        self.twoView.backgroundColor = RGBAlpa(127,255,212,1)
        
        var baseView = UIApplication.shared.connectedScenes
            .map { $0 as? UIWindowScene }
            .compactMap { $0 }
            .first?
            .windows
            .filter { $0.isKeyWindow }
            .first?
            .rootViewController
        while ((baseView?.presentedViewController) != nil)  {
              baseView = baseView?.presentedViewController
        }
        baseView?.view.addSubview(self.twoView)

    }
    //Two画面close
    fileprivate func closeTwoView()
    {
        if self.twoView == nil
        {
            return
        }
        // アニメーションして移動
        UIView.animate(withDuration: 0.1,
            animations: { () -> Void in
                
                self.twoView.frame = CGRect(x: 0, y:SCREEN_HEIGHT, width: SCREEN_WIDTH, height: SCREEN_HEIGHT)
            },
            completion: { (finished: Bool) -> Void in
            }
        )
    }
     //Two画面表示
    fileprivate func showTwoView()
    {
        self.twoView.backgroundColor = RGBAlpa(127,255,212,1)
         self.twoView.delegate = self
        // アニメーションして移動
        UIView.animate(withDuration: 0.5,
            animations: { () -> Void in
                
                self.twoView.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT)
            },
            completion: { (finished: Bool) -> Void in
            }
        )
    }
  
    //delegate メソッド
    func TwoViewClick(buttonName:String)
    {
        //Two画面close
        self.closeTwoView()
        //タイトルUILabel
        let titleLabel:UILabel = UILabel()
        titleLabel.frame = CGRect(x: SCREEN_WIDTH/2 - (SCREEN_WIDTH/2)/2, y: SCREEN_HEIGHT/2 - 100, width: SCREEN_WIDTH/2, height: 40)
        titleLabel.font = UIFont(name: "Lato-Bold",size: CGFloat(20))
        titleLabel.text = buttonName
        titleLabel.textAlignment = .center
        titleLabel.textColor = .red
        titleLabel.adjustsFontSizeToFitWidth = true
        titleLabel.backgroundColor = .clear
        self.view.addSubview(titleLabel)
    }
}

遷移先のdelegateを宣言

delegateを宣言します。UITableViewDelegateやUITextFieldDelegateと同じ物になります。

class OneViewController: UIViewController, ①TwoViewDelegate {

delegateにselfを設定します。

 self.twoView.delegate = self

delegateメソッドの処理記述

遷移先画面から戻る場合の遷移後の処理を記述します。

ここでは、UIViewをCLOSEしてUILabelに引き継ぎ項目を設定して表示しています。

  ③
    //delegate メソッド
    func TwoViewClick(displayName:String)
    {
        //Two画面close
        self.closeTwoView()
        //タイトルUILabel
        let titleLabel:UILabel = UILabel()
        titleLabel.frame = CGRect(x: SCREEN_WIDTH/2 - (SCREEN_WIDTH/2)/2, y: SCREEN_HEIGHT/2 - 100, width: SCREEN_WIDTH/2, height: 40)
        titleLabel.font = UIFont(name: "Lato-Bold",size: CGFloat(20))
        titleLabel.text = buttonName
        titleLabel.textAlignment = .center
        titleLabel.textColor = .red
        titleLabel.adjustsFontSizeToFitWidth = true
        titleLabel.backgroundColor = .clear
        self.view.addSubview(titleLabel)
    }

この場合「displayName」という引き継ぎ項目を定義しています。
func TwoViewClick(displayName:String)

手順2:遷移先画面

次に遷移先画面の実装です。「①イベント発生を検知する処理」の処理になります。

全体

全体のソースコードです。

 import UIKit

①
// プロトコルを作る。
protocol TwoViewDelegate: AnyObject {
    func TwoViewClick(displayName:String)
}


class TwoView: UIView {

    
    ② weak var delegate: TwoViewDelegate?
    
    override func draw(_ rect: CGRect)
    {
        let twoBackButton = UIButton(type: .custom)
        twoBackButton.frame = CGRect(x: SCREEN_WIDTH/2 - (SCREEN_WIDTH/3.5)/2, y: SCREEN_HEIGHT/2, width: SCREEN_WIDTH/3.5, height: 40)
        twoBackButton.backgroundColor = .green
        twoBackButton.addTarget(self, action: #selector(TwoView.twoWorkButtonClick(_:)), for: UIControl.Event.touchUpInside)
        twoBackButton.setTitle("One画面遷移", for: .normal)
        twoBackButton.titleLabel?.font =  UIFont.systemFont(ofSize: 15)
        twoBackButton.setTitleColor(.black, for: .normal)
        twoBackButton.layer.cornerRadius = 10
        twoBackButton.layer.borderColor = UIColor.gray.cgColor  // 枠線の色
        twoBackButton.layer.borderWidth = 1.0 // 枠線の太さ
        self.addSubview(twoBackButton)
    }
    
    //ボタンタップイベント
    @objc fileprivate func twoWorkButtonClick(_ sender: UIButton)
    {
        self.delegate?.TwoViewClick(displayName: "Two画面に遷移したよ!")
    }
}

protocolを定義

protocolで値を引き継ぐためのメソッドとStringを定義します。

①
// プロトコルを作る。
protocol TwoViewDelegate: AnyObject {
    func TwoViewClick(displayName:String)
}

delegateを定義

delegateを定義します。

 ② weak var delegate: TwoViewDelegate?

delegateメソッド実行

今回のアプリの場合、TwoView画面からOneViewController画面に戻る際に値を引き継ぐため、戻るボタンタップイベントでdelegateメソッドを実行します。


//ボタンタップイベント
@objc fileprivate func twoWorkButtonClick(_ sender: UIButton)
{
     self.delegate?.TwoViewClick(displayName: "Two画面に遷移したよ!")
}

delegateで構造体を利用してみよう!!

delegateで構造体を利用してみよう!!

もちろん、構造体も引き継ぐことができます。

前回使用した構造体を引き継いでみましょう!

手順1:遷移先画面

遷移先画面のロジックを実装していきましょう!

構造体を定義

今回は遷移先画面:TwoView側で定義しました。

    struct oneStructList {
       var A:Bool
       var B:String
       var C:Int
       var D:twoStructList
    }
    struct twoStructList {
       var E:Bool
       var F:String
       var G:String
    }
    
    fileprivate var workStructList:oneStructList = oneStructList(A: true, B: "", C: 0, D: twoStructList(E: false, F: "", G: ""))

protocolの引き継ぎ項目を構造体に変更

引継ぎ項目に構造体を定義します。

// プロトコルを作る。
protocol TwoViewDelegate: AnyObject {
    func TwoViewClick(displayName:TwoView.oneStructList)
}

delegateメソッド実行

ボタンのタッチイベントにdelegateメソッドを定義します。

//ボタンタップイベント
@objc fileprivate func twoWorkButtonClick(_ sender: UIButton)
{
      self.delegate?.TwoViewClick(displayName: workStructList)
}

手順2:遷移元画面

次に遷移元画面のロジックを実装します。

delegateメソッドに処理追記

遷移先画面で定義しましたdelegateメソッドでの処理内容を記述します。

引き継いだ構造体の項目から値を取得して、UILabelに設定し表示します。

    //delegate メソッド
    func TwoViewClick(displayName:TwoView.oneStructList)
    {
        //Two画面close
        self.closeTwoView()
        //構造体解体
        let workA:Bool = displayName.A
        var workA01:String = ""
        if workA
        {
            workA01 = "true"
        } else {
            workA01 = "false"
        }
        let workB:String = displayName.B
        let workC:String = String(displayName.C)
        
        let workD:TwoView.twoStructList = displayName.D
        
        let workE:Bool = workD.E
        var workE01:String = ""
        if workE
        {
            workE01 = "true"
        } else {
            workE01 = "false"
        }
        let workF:String = workD.F
        let workG:String = workD.G
        
        let setLabeValue:String = workA01 + "\n" + workB + "\n" + workC + "\n" + workE01 + "\n" + workF + "\n" + workG
        //タイトルUILabel
        let titleLabel:UILabel = UILabel()
        //titleLabel.frame = CGRect(x: SCREEN_WIDTH/2 - (SCREEN_WIDTH/2)/2, y: SCREEN_HEIGHT/2 - 100, width: SCREEN_WIDTH/2, height: 40)
        titleLabel.frame = CGRect(x: 0 , y: 0 , width: SCREEN_WIDTH, height: 400)
        titleLabel.font = UIFont(name: "Lato-Bold",size: CGFloat(20))
        titleLabel.text = setLabeValue
        titleLabel.textAlignment = .center
        titleLabel.textColor = .red
        titleLabel.numberOfLines = 0
        titleLabel.adjustsFontSizeToFitWidth = true
        titleLabel.minimumScaleFactor = 0.3
        titleLabel.backgroundColor = .clear
        self.view.addSubview(titleLabel)
    }

手順3:シミュレータ確認

簡単に構造体に変更する事ができます。

まとめ

まとめ

delegateを利用して画面間の値受け渡しを行なってみました。

理解しづらい方

なんかよく理解できないな!

という方は、まずはロジックのパターンを覚えてみてください。

遷移元遷移先
遷移先のdelegateを宣言
delegateメソッドの処理記述
protocolを定義

delegateを定義
delegateメソッド実行

流れをつかむと理解しやすいと思います。

delegateを利用する理由

モーダルビューやUIViewで入力された値を、メイン画面のUIViewControllerで使用するような場合に使用します。

メインは上記と考えています。

アプリ開発で利用してみてください!

それではまたっ!

よかったらシェアしてね!
  • URLをコピーしました!
目次