質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

259閲覧

Protocolを用いてController層とView層でやり取りをしたい

Naoki_Pro

総合スコア23

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2020/02/25 15:02

前提・実現したいこと

こんばんは。いつもお世話になってます。
Stoty board無しで開発をしています。ナビゲーションバーのOkボタンをタップした時にテキストフィールドの文字が空だったら、アラートを表示する。空じゃなかったら、特に何も起きない。という処理の実装をしたいのですが上手くいきません。

下記ソースコードの説明

ViewController.swfit と View.swift がやり取りをするために ViewController.swift でプロトコルを作っています。
Okボタンがタップされた時に、プロトコルのメソッドを実行して返ってきた値が true か false かでアラートを出すか出さないか決めています。

現状と推測

プロトコルのメソッドの返り値が true か false 以前に、 nil になってしまいます。 View.swift に移譲してあるプロトコルのメソッドが、ログを見る限り起動( print(return fasle), print(return true)の箇所 )していないので、 ViewContoller.swift の delegate? の部分でオプショナルチェイニング?がかかり、それ以降の .confirmTextFeild() が実行されず nil になってしまっているのだと僕は考えているのですが、解決できません。

プロトコルのメソッドに返り値を設定したのは今回が初めてですが、今までの返り値なしで自分で作成してきたプロトコルは何も問題無く動いていたので原因が分からず悩んでいます。

シュミレータとログ画面

シュミレータとログ画面

該当のソースコード

ViewController

1import UIKit 2 3protocol ViewControllerDelegate { 4 func confirmTextFeild() -> Bool 5} 6 7class ViewController: UIViewController { 8 9 var subView: UIView! 10 var delegate: ViewControllerDelegate? 11 12 override func viewDidLoad() { 13 super.viewDidLoad() 14 15 view.backgroundColor = .white 16 configureNavItem() 17 configureSubView() 18 } 19 20 override func viewDidLayoutSubviews() { 21 subView.frame = CGRect(x: view.bounds.origin.x, y: view.bounds.origin.y, width: view.bounds.width, height: view.bounds.height) 22 } 23 24 func configureSubView() { 25 subView = View() 26 view.addSubview(subView) 27 } 28 29 func configureNavItem() { 30 31 navigationItem.rightBarButtonItem = UIBarButtonItem(title: "OK", style: .plain, target: self, action: #selector(tappedOk)) 32 33 } 34 35 @objc func tappedOk() { 36 37 let flag = delegate?.confirmTextFeild() 38 39 if flag == nil { 40 print("nil") 41 return 42 } 43 44 if !flag! { 45 print("No") 46 let alertController = UIAlertController(title: "メッセージ", message: "文字が記入されていません", preferredStyle: .alert) 47 alertController.addAction(UIAlertAction(title: "ok", style: .default, handler: nil)) 48 present(alertController, animated: true, completion: nil) 49 } else { 50 print("ok") 51 } 52 } 53 54}

View

1import UIKit 2 3class View: UIView { 4 5 var textField: UITextField! 6 var viewController: UIViewController! 7 8 override init(frame: CGRect) { 9 super.init(frame: frame) 10 11 configureTextField() 12 configureViewControllerDelegate() 13 } 14 15 func configureTextField() { 16 textField = UITextField() 17 textField.backgroundColor = .darkGray 18 textField.textColor = .white 19 addSubview(textField) 20 } 21 22 func configureViewControllerDelegate() { 23 let localViewController = ViewController() 24 localViewController.delegate = self 25 viewController = localViewController 26 } 27 28 required init?(coder: NSCoder) { 29 fatalError("init(coder:) has not been implemented") 30 } 31 32 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 33 self.endEditing(true) 34 } 35 36 override func layoutSubviews() { 37 textField.frame = CGRect(x: Double(self.center.x / 2), y: Double(self.center.y), width: 200, height: 40) 38 } 39 40} 41 42extension View: ViewControllerDelegate{ 43 44 func confirmTextFeild() -> Bool { 45 if textField.text == "" { 46 print("return false") 47 return false 48 } else { 49 print("return true") 50 return true 51 } 52 } 53 54} 55 56extension View: UITextFieldDelegate { 57 58 func textFieldShouldReturn(_ textField: UITextField) -> Bool { 59 textField.resignFirstResponder() 60 } 61 62}

試したこと

View.swiftでViewController.swiftをインスタンス化して、delegate = self にする場所が悪いのかと思い、他の箇所に変えて実行したのですが、上手くいきませんでした。

引き続き自分でも原因を探りますが、どなたか原因が分かる方がいましたらご回答お願いします。

補足情報

xcodeバージョン11.3.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2020/02/25 15:42

「View.swiftでViewController.swiftをインスタンス化」って普通は逆ですね。delegate の向きも逆で、ViewControllerDelegate ではなく ViewDelegate を定義して view.delegate = self (self は ViewController) とするのが普通です。
Naoki_Pro

2020/02/26 06:21

お返事ありがとうございます。 View層側でデリゲートを記述し、処理はController層に移譲するというコードはよく見かけるのですが、今回僕が書いたような、ViewController層側でデリゲートを記述し、処理はView層に移譲するというパターンはないのでしょうか?
hoshi-takanori

2020/02/26 06:41

はい。というか、もともとViewControllerはViewの持ち主なので、デリゲート機構を使わなくても目的のViewにアクセスできるはずです。また、Viewは表示に徹するべきで、入力内容が適切がどうかを判断するのはViewControllerの責務です。そして何より、Viewの中でViewControllerを作るのはありえないです。
Naoki_Pro

2020/02/26 13:15

お返事ありがとうございます。 確かにViewController層はView層を管理していてView層にアクセスできるので、デリゲートの必要はなかったです。解説ありがとうございますm(__)m
guest

回答1

0

ベストアンサー

直接 Viewtextfield を見れば良さそうに思えます。

(delegateの向きについては、hoshi-takanoriさんも指摘されていますが、コードをみてて同じような印象を受けました)。

ViewUIView のサブクラスですから、subViewUIView ではなく View のクラスにできそうな気がしますが、いかがでしょうか。

こんな感じに変更すれば、おそらく期待通りに動くと思います(こちらでは動きました)。

  • ViewController.swift

Swift

1class ViewController: UIViewController { 2 3 // UIView -> 自作の View に変更 4 var subView: View! 5 // 削除 6// var delegate: ViewControllerDelegate? 7 8// 中略 9 @objc func tappedOk() { 10 // *** subView のテキストフィールドをチェック 11 if subView.textField.text == "" { 12 print("No") 13 let alertController = UIAlertController(title: "メッセージ", message: "文字が記入されていません", preferredStyle: .alert) 14 alertController.addAction(UIAlertAction(title: "ok", style: .default, handler: nil)) 15 present(alertController, animated: true, completion: nil) 16 } else { 17 print("ok") 18 } 19 } 20}

投稿2020/02/25 15:59

編集2020/02/25 16:02
TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Naoki_Pro

2020/02/26 06:18

お返事ありがとうございます。 確かに仰る通りで、subView.textFeild.text == " " で空か否か確認すれば良いだけでした。 delegate の向きに関してなのですが、View層側で delegate を記述して、Controller層に処理を移譲するソースコードはよく見かけるのですが、僕が今回書いたようにController層で delegate を記述して、View層に処理を移譲するというパターンはないのでしょうか? 今回 subView.textFeild.text でテキストフィールドにアクセスできることを完全に忘れていたので、Controller層で delegate を記述し処理はView層に移譲して、テキストフィールドが空か否か判断できるのではないか考え、上記のようなデリゲートになってしまいました。
TsukubaDepot

2020/02/26 08:57

一般的に言う「Controller層で delegate を記述して、View層に処理を移譲するというパターン」があるのか無いのかまではわかりませんが、現在のViewControllerクラスとViewクラスそれぞれの包含関係を整理(できれば図示して整理)してみると、なんだか変な関係になっているとわかるかと思います。
Naoki_Pro

2020/02/26 13:13

お返事ありがとうございます。 ViewController層はView層を管理しているため、delegate を使わなくても今回のように subView.textField.text みたいにアクセスすれば良いだけなので、確かに変な関係になってしまいますね。。。 ベストアンサーにさせて頂きます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問