🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Xcode

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

Swift

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

Q&A

解決済

2回答

1511閲覧

Modal画面(ViewController)にあるButtonタップで、後ろにあるTabBarのアクティブタブを切り替えたい(Storyboardで行いたい)

Teratopi

総合スコア8

Xcode

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

Swift

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

0グッド

0クリップ

投稿2021/03/01 13:21

編集2021/03/01 13:30

###前提
TabBarが2つある基本画面にModalで表示されるVewControllerが1つあります。
イメージ説明

実現したいこと

  1. アプリが起動した時は「Tab First」が選択されています。
  2. Tab Firstの「Modal画面遷移」ボタンをタップするとModalViewContに画面遷移します。
  3. Modal画面上の「Tab2に切り替え」ボタンをタップするとModal画面が閉じて「Tab Second」に切り替わったTabBarが表示されます。

この機能をデリゲートを使用して行おうと思っています。

###わからないこと
デリゲートの委任先(MainTabBarController)でデリゲートを設定する書き方がわかりません。

委任先でデリゲートを設定するには、委任するクラスのインスタンスを生成して、デリゲートプロパティに、そのクラスを代入する?と書く必要があると思います。

MainTabBarControllerクラス内に直接記述出来ないようなので
関数を作成してその中にデリゲートを設定する必要がると思うのですが
どう書いてよいか分からないのです。

###現状実現できていること
「Class ModalViewCont」にデリゲートインスタンス(var selectSecondTab : tabChangeDelegate?)を設定し、「TabBar2に切り替え」ボタン(IBAction)に「selectSecondTab?.tabChange()」を設定しています。
「MainTabBarController」には、デリゲートメソッド(func tabChange(){selectedIndex = 1})を書いています。
この関数で「TabSecond」に切り替わると思っています。

後は委任先(Class MainTabBarController)に、デリゲートの設定を書けば良いと思うのですが、ここがよくわかりません。
MainTabBarControllerに直接デリゲートの設定を書こうとすると、「関数内に書いてください」とエラーが出ます。

swift

1// MainTabBarController.swift 2 3import UIKit 4 5class MainTabBarController: UITabBarController, tabChangeDelegate { 6 7 /* ここにデリゲートの設定をする必要があると思いますが 8 どう書けばよいかわかりません。 9 */ 10 11 func tabChange() { 12 selectedIndex = 1 13 } 14 15 16 override func viewDidLoad() { 17 super.viewDidLoad() 18 19 20 } 21 22} 23

swift

1import UIKit 2 3class FirstViewCont: UIViewController { 4 5 @IBOutlet weak var modalSegue: UIButton! 6 7 override func viewDidLoad() { 8 super.viewDidLoad() 9 10 } 11 12 13 @IBAction func modalSegueAction(_ sender: Any) { 14 15 let modalVC = self.storyboard?.instantiateViewController(identifier: "modalVC") as! ModalViewCont 16 present(modalVC, animated: true, completion: nil) 17 18 } 19} 20

swift

1// SecondViewCont.swift 2import UIKit 3 4class SecondViewCont: UIViewController { 5 6 override func viewDidLoad() { 7 super.viewDidLoad() 8 9 } 10} 11

swift

1// ModalViewCont.swift 2 3protocol tabChangeDelegate { 4 func tabChange() 5 6} 7 8import UIKit 9 10class ModalViewCont: UIViewController, UITabBarControllerDelegate { 11 12 @IBOutlet weak var tabChangeBtn: UIButton! 13 14 var selectSecondTab : tabChangeDelegate? 15 16 override func viewDidLoad() { 17 super.viewDidLoad() 18 19 } 20 21 @IBAction func changeTab(_ sender: Any) { 22 23 selectSecondTab?.tabChange() 24 dismiss(animated: true, completion: nil) 25 26 } 27}

##開発環境
Mac-mini(M1)
macOS 11.2.1(BigSur)
XCode 12.4

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

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

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

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

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

guest

回答2

0

ベストアンサー

delegate の処理を FirstViewCont で行わせる点は Ranford さんのご回答と同じなのですが、UITabBarControler で管理されているのであれば NotficationCenter を使わない方法もあるので合わせてご紹介します。

まず、delegate の作り方で問題点があるのでそこから修正します。

Swift

1// AnyObject を準拠させる 2protocol tabChangeDelegate: AnyObject { 3 func tabChange(selectIndex: Int) 4}

後述する「循環参照」を避けるため、protocol で AnyObject を指定します。
AnyObject を指定することによって、tabChangeDelegate に準拠できるのは「クラス」に限定させることができます。

ちなみに、上で宣言した Delegate は、今後の拡張性を考えて、引数で変更したいインデックスを指定できるように書き換えました。

呼び出す方はこのような感じになります。

Swift

1import UIKit 2 3class ModalViewCont: UIViewController, UITabBarControllerDelegate { 4 @IBOutlet weak var tabChangeBtn: UIButton! 5 6 // 循環参照を避けるために weak 指定する 7 weak var selectSecondTab : tabChangeDelegate? 8 9 override func viewDidLoad() { 10 super.viewDidLoad() 11 12 } 13 14 @IBAction func changeTab(_ sender: Any) { 15 // 呼び出し 16 selectSecondTab?.tabChange(selectIndex: 1) 17 18 dismiss(animated: true, completion: nil) 19 } 20}

循環参照を避けるため、selectedSecondTab には weak 修飾子を指定します。weakを指定すると、delegate で指定したクラスに対し「強参照」するため強い循環参照が発生し、ModalViewCont のインスタンスを破棄させることができなくなってしまいます。

呼びされる方はこのような感じです。

Swfti

1import UIKit 2 3class FirstViewCont: UIViewController, tabChangeDelegate { 4 override func viewDidLoad() { 5 super.viewDidLoad() 6 } 7 8 @IBAction func modalSegueAction(_ sender: Any) { 9 10 let modalVC = self.storyboard?.instantiateViewController(identifier: "modalVC") as! ModalViewCont 11 // Delegate に自分自身(FirstViewContを指定) 12 modalVC.selectSecondTab = self 13 14 present(modalVC, animated: true, completion: nil) 15 } 16 17 // MARK: Dekegate 18 func tabChange(selectIndex: Int) { 19 // FirstViewCont が tabBarController に管理されている場合には、tabBarController がセットされている 20 if let tabBarController = self.tabBarController { 21 // 選択したい tabBar の index を直接指定できる 22 // 本来であれば、index が範囲内かチェックする必要がある。 23 tabBarController.selectedIndex = selectIndex 24 } 25 } 26}

FirstViewContUITabBarControllerに管理されているクラスなので、tabBarController には UITabBarController のインスタンスが入っています。

念の為、オプショナルバイディングで nil 判定を行っていますが、確実に呼び出しているのであれば判定はいらないと思います(失敗してもオプショナルチェインで処理が中断されるだけ)。

selectedIndexsetget もできるため、ここに表示させたいタブのインデックスを入れることで、UITabBarController で表示させている ViewController を切り替えることが可能です。

投稿2021/03/03 01:05

TsukubaDepot

総合スコア5086

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

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

Teratopi

2021/03/03 05:11

とても分かりやすい回答ありがとうございます。 循環参照をを避けるための書き方や、引数でインデックスを指定する書き方 とても参考になりました。 私がぼんやりイメージしていた、シンプルでスマートな回答頂きましたので ベストアンサーとさせていただきます。
TsukubaDepot

2021/03/03 05:14

KVOの方法も、全体をモデル化(MVCとかMVVCとか)するのであれば必要となってくる知識ですので、ぜひRanfordさんのご回答も参考に、さまざまな実装方法を獲得されておくのがいいかと思います。 もっとも、KVOよりも RxSwift や Combine を用いた方が見通しが効く書き方はできるかと思いますが。
Teratopi

2021/03/03 06:37

コメントありがとうございます。 『MVC』『MVVC』『RxSwift』は言葉としては知っていましたが『KVO』『Combine』初めて認知したキーワードでした。とても参考になりました、ありがとうございます。 恥ずかしながら、まだまだ学習不足で、かつ学習した内容がなかなか頭に残らないので時間ばかり費やしてしまいます。 もっと「teratail」を効果的に利用させて頂いて学習を効率化したいと改めて感じました。 いろいろ助言いただきありがとうございました。
guest

0

ModalViewContのインスタンスはFirstViewContで登場するので、
ModalViewContのdelegateはFirstViewContで受け取るべきです。

MainTabBarController > FirstViewCont > ModalViewContでインスタンスの受け渡しが不安なので、
直接ModalViewContのchangeTab()でNotificationCenter.default.post()を行なってはどうでしょう?

Swift

1class MainTabBarController: UITabBarController { 2 3 override func viewDidLoad() { 4 super.viewDidLoad() 5 addObserverNotification() 6 } 7 8 func removeObserverNotification() { 9 // どこかで通知の後始末 10 NotificationCenter.default.removeObserver(self) 11 } 12 13 func addObserverNotification() { 14 // ローカル通知の受付 15 NotificationCenter.default.addObserver(self, selector: #selector(tabChange(notification:)), name: .tabChange, object: nil) 16 } 17 18 @objc private func tabChange(notification: NSNotification) { 19 self.selectedIndex = 1 20 } 21} 22 23extension Notification.Name { 24 static let tabChange = Notification.Name("tabChange") 25}

Swift

1class FirstViewCont: UIViewController { 2 3 @IBOutlet weak var modalSegue: UIButton! 4 5 override func viewDidLoad() { 6 super.viewDidLoad() 7 } 8 9 @IBAction func modalSegueAction(_ sender: Any) { 10 11 let modalVC = self.storyboard?.instantiateViewController(identifier: "modalVC") as! ModalViewCont 12 present(modalVC, animated: true, completion: nil) 13 modalVC.selectSecondTab = self 14 } 15} 16 17extension FirstViewCont: tabChangeDelegate { 18 func tabChange(){ 19 self.tabBarController?.selectedIndex = 1 20 // selectedIndexが効けば不要、MainTabBarControllerにローカル通知を飛ばす 21 // NotificationCenter.default.post(name: .tabChange, object: nil, userInfo: nil) 22 } 23} 24

投稿2021/03/02 02:59

Ranford

総合スコア64

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

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

Teratopi

2021/03/02 07:47

丁寧な ご回答ありがとうございます。 今日明日は本業中で、教えていただいた内容を確認できる環境にないので、BAは2.3日お待ち頂けたらと思います。
Teratopi

2021/03/03 05:19

NotificationCenterの使い方は。とても参考になりました。 ありがとうございます。 今後の学習に役立てたいと思います。 重ねてありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問