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

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

ただいまの
回答率

88.06%

Swift デリゲートを使って値を渡して遷移元を再描画させる方法

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,591

score 24

遷移元の画面に値を渡してその値によって遷移元を再描画させたいのですが、セグエで遷移して戻ると(dismissやunwindなどで)遷移元の画面は遷移前の状態のままで変化がありません。
そこで、デリゲートを使ってみたのですが、それでも遷移元には反映されていません。
コード記述のどこかが間違っているのか、やり方が間違っているのか・・・
解る方がいらっしゃいましたら教えて頂けないでしょうか。
宜しくお願い致します。

 詳細説明

ナビゲーションコントローラーを1枚目のテーブルビューに埋め込んで、1枚目のテーブルビューから2枚目のテーブルビューに遷移して→PageViewControllerに遷移したら、コンテンツVCの各ページはスワイプで移動できるようになっています。

画面遷移はセグエを使用しています。

画像の左から4つ目のPageViewControllerのナビゲーションバー上にある「設定」ボタンから、画像下段のSettingViewControllerへ遷移します。

SettingViewControllerにて遷移元に反映させたい設定などをします。

実際に戻る画面は(戻った時に表示される画面)PageViewControllerの管理するコンテンツVCとなるPageContentViewControllerの遷移した時に表示されていたページが表示されます。

サンプルコードではviewの背景色を変えるメソッドにしておりますが、実際は設定項目が複数あるので、ひとつひとつデリゲートメソッドに抜き出して呼び出すよりは、再読込(リロードのような)させられるのならばその方法も知りたいです。

できれば、presentで遷移したような状態になりたいのですが・・・
(presentで遷移すれば再描画されるのですが、テーブルビューに戻るボタンが消えるなどのエラーがでてテーブルビューに戻れなくなってしまって詰まりました)

イメージ説明

 サンプルコードで試したこと

SettingViewController内の「OFF/ON]ボタンを選択して戻るボタンを押下すると、デリゲートメソッドのtest1(),test2()の中に記述したprint文はデバッグエリアに出力されるのですが、背景色を変更するようなことにはなりません。

<例> 
設定を「ON」にして「戻る」押下
(ONにするとsoundSettingが1になります)

<print出力>
test1メソッドに入ったsoundSetting: 1
test2メソッドに入ったsoundSetting: 1

<遷移元画面>
変化なし

 サンプルコード   version Swift3

import Foundation

protocol testDelegate: class {
    var soundSetting:Int { get set }
    func test1()
    func test2(soundSetting: Int)
}
import UIKit

class SettingViewController: UIViewController {

    weak var testdelegate: testDelegate?

    var soundSetting:Int = 0
    var pageIndex:Int = 0
    var selectedVC:Int = 0
    var arrayLength:Int = 0


    override func viewDidLoad() {
        super.viewDidLoad()

        // Viewを閉じる(VCへ戻る)dismisボタン作成
        let backDMButton = UIButton()
        backDMButton.frame = CGRect(x: 0, y: 0, width: 120, height: 30)
        backDMButton.center = CGPoint(x: self.view.frame.midX, y: 35)
        backDMButton.setTitle("dismiss戻り", for: .normal)
        backDMButton.setTitleColor(UIColor.magenta, for: .normal)
        backDMButton.addTarget(self, action: #selector(backDMVC(sender:)), for: .touchUpInside)
        self.view.addSubview(backDMButton)

        // セグメントボタンを作成
        let array : NSArray = ["OFF","ON"]
        let uiSegmentedControl: UISegmentedControl = UISegmentedControl(items: array as [AnyObject])
        uiSegmentedControl.frame = CGRect(x: 0, y: 0, width: 200, height: 30)
        uiSegmentedControl.center = CGPoint(x: self.view.frame.width/2, y: 100)
        uiSegmentedControl.selectedSegmentIndex = soundSetting
        uiSegmentedControl.backgroundColor = UIColor.white
        uiSegmentedControl.tintColor = UIColor.darkGray
        uiSegmentedControl.addTarget(self, action: #selector(self.segmentChanged(segcon:)), for: UIControlEvents.valueChanged)
        self.view.addSubview(uiSegmentedControl)      

    }  // viewDidLoadを閉じる


    // セグメントボタン押下時に実行するメソッド
    func segmentChanged(segcon: UISegmentedControl){
        switch segcon.selectedSegmentIndex {
        case 0:
            soundSetting = 0
        case 1:
            soundSetting = 1
        default:
            print("Error")
        }
    }

    // セグエで遷移した戻り
    func backDMVC(sender: AnyObject) {

        let nav = self.presentingViewController  as! UINavigationController
        let pageViewController = nav.topViewController as! PageViewController
        pageViewController.soundSetting = soundSetting

        self.dismiss(animated: true, completion: nil)

        let delegate = PageContentViewController()
        self.testdelegate = delegate

        self.testdelegate?.soundSetting = soundSetting
        self.testdelegate?.test1()
        self.testdelegate?.test2(soundSetting: soundSetting)
    }
}
import UIKit
class PageContentViewController: UIViewController, testDelegate {

//シーン移動の際に設定されるデータ
    var pageIndex:Int = 0   
    var selectedVC:Int = 0
    var selectedSVC:Int = 0  

    var soundSetting:Int = 0

 override func viewDidLoad() {
        super.viewDidLoad()

    (略)
}

   func test1() {
        self.view.backgroundColor = UIColor.yellow
        print("test1メソッドに入った: \(soundSetting)")
    }

    func test2(soundSetting: Int) {
        if soundSetting == 0 {
            self.view.backgroundColor = UIColor.red
            print("test2メソッドに入ったsoundSetting: \(soundSetting)")
        }
        if soundSetting == 1 {
            self.view.backgroundColor = UIColor.brown
            print("test2メソッドに入ったsoundSetting: \(soundSetting)")
        }
    }
}
import UIKit

class PageViewController: UIPageViewController, UIPageViewControllerDataSource {

    // シーン移動の際に渡される値
    var selectedVC:Int = 0            // viewControllerで押されたセルのindex
    var selectedSVC: Int = 0    // SecondViewControllerで押されたセルのindex
    var arrayLength: Int = 0     

    var soundSetting: Int = 0

    var pageIndex: Int = 0

    // ページングするviewControllerを格納する配列のプロパティ宣言
    var contentVCs = [UIViewController]()   

    override func viewDidLoad() {
        super.viewDidLoad()

        dataSource = self  

        for index in 0..<arrayLength { 
            let contentVC = storyboard?.instantiateViewController(withIdentifier: "PageContentViewController") as! PageContentViewController
            contentVC.pageIndex = index
            contentVC.selectedSVC = selectedSVC
            contentVC.selectedVC = selectedVC
            contentVCs.append(contentVC)
        }

        self.setViewControllers([contentVCs[selectedSVC]], direction: .forward, animated: true, completion: nil)

    }  // viewDidLoad()を閉じる


    // 設定ボタンからセグエで遷移
    @IBAction func goSecondSettingBySegue(_ sender: Any) {
        performSegue(withIdentifier: "toSecondSetting", sender: nil)

    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let settingVC = segue.destination as? SettingViewController {
            settingVC.pageIndex = pageIndex
            settingVC.selectedVC = selectedVC
            settingVC.arrayLength = arrayLength
            settingVC.soundSetting = soundSetting
        }
    }


    // MARK: - UIPageViewControllerDataSource
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let index = contentVCs.index(of: viewController as! PageContentViewController), index > 0 else {
            return nil
        }
        let previousVC = contentVCs[index - 1]
        return previousVC
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let index = contentVCs.index(of: viewController as! PageContentViewController), index < contentVCs.count - 1 else {
            return nil
        }
        let nextVC = contentVCs[index + 1]
        return nextVC
    }

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

delegateの設定の仕方が誤っています。

let delegate = PageContentViewController()
self.testdelegate = delegate


のようにしてしまっているので、SettingViewControllerで新たに生成したPageContentViewControllerで書き換えて行ってしまっています。
PageViewControllerで生成した、今表示されているPageContentViewControllerをdelegateとして設定してやる必要があります。

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let settingVC = segue.destination as? SettingViewController {
            settingVC.pageIndex = pageIndex
            settingVC.selectedVC = selectedVC
            settingVC.arrayLength = arrayLength
            settingVC.soundSetting = soundSetting
            settingVC.testdelegate = currentVC as? testDelegate // ここに現在表示されているPageContentViewControllerを渡してやる
        }
}


currentVCは私が勝手に作った値です。PageViewControllerで今表示しているものを保持しているのであればそれを入れてあげてください。わからなければコメントください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/09/18 17:58

    ちょっと遅くなってしまいましたがgithubの方にpullrequestを送らせていただきました。
    一応、想定している動きにはなるのではないか?と思います。
    わからない部分あればコメントください。

    キャンセル

  • 2018/09/21 15:39

    プルリクエストありがとうございます。
    想定している動きになりました。
    githubの扱いが不慣れでプッシュぐらいしかできなかったものですから、github関連の作業に時間がかかっしまいました。razumaさんのコメント読むだけで自分のミスしている部分が分かっていたのですが、なんとかマージやら修正などを加えてみたかったので返事が遅くなってしまいました。すみません。

    razumaさんのサンプルコードを見ながら修正やコピペをしているうちに、プロトコルのプロパティの{get set}部分を削除してしまっていて、他のところでもプロパティの変更処理のコードを忘れていたのが大きな原因でした。

    最初のコンテンツVCにプロトコルを継承させていたときよりも、今回教えて頂いたページ管理するVCにプロトコルを継承することで、すっきりしたコードが書けました。とても勉強になりました。

    >> 「SecondViewController」をどうにかして参照できるようにする
    の、他の質問にもヒントを頂き、ありがとうございました。
    ですが、PageViewControllerから SecondViewController → ViewControllerの遷移で、既存のインスタンスを呼出す方法が分からず、結局、BarButtonItemを自作して[>back]ボタンとし、そのボタンイベント内で呼び出して値を渡すことぐらいしか思いつきませんでした。もうちょっと考察してみます・・・。

    長々とお付き合いありがとうございました。
    github上での失礼がありましたら、ご容赦下さいませ。(まだ全然使い方が分かってないです。)

    キャンセル

  • 2018/09/21 18:33

    私もあまりgithubを使ったことがなかったので、今回初プルリクエストを出してみました。(してみたかっただけ)
    実際に色々とやってみないとなかなか使い方もわからないので何でも試してみることは良いことだと思います。
    delegateについても、全くわからない→もやもや→なんとなくわかったかも
    ぐらいまでには理解できたのかなと思っています。
    今後も勉強を進めていきプログラムについての理解が深まってくるとスッキリ!な状態になると思います。(私も初めの頃はdelegateってなんとなくこうやっておけば動くんでしょ?くらいな感じでした。)

    お疲れ様でした。今後もがんばってください。

    キャンセル

0

今回の質問で最初にアップした「該当のサンプルコード」に合わせて、解決した方法を記述しました。

今回のように、全体的に値を共有させたいのであれば、渡すのではなく、UserDefalts, AppDelegate, シングルトンなどの方法が適しているとアドバイスを頂いたので、実際はそうしょうと思っていますが、デリゲートも勉強したかったので、プロトコルを使ったdelegateで、設定画面VC、ページ管理VC、コンテンツVCの3カ所でデリゲートを使った値渡しをしてみました。

razumaさん 
分かりやすい説明のおかげで解決できました。ありがとうございました。

■ プロトコル
import UIKit
protocol testDelegate: class {
    var soundSetting:Int {get set} 
    func test(soundSetting: Int)
}


■ 設定画面
import UIKit
class SecondSettingViewController: UIViewController { // デリゲート処理させるところ

    weak var testdelegate: testDelegate?

    (略)

    // セグエで遷移した戻りのdismiss
    func backDMVC(sender: AnyObject) {
        self.testdelegate?.soundSetting = soundSetting       // プロパティの変更値をセット
        self.testdelegate?.test(soundSetting: soundSetting)  // PVCのtest()メソッドを処理
        self.dismiss(animated: true, completion: nil)
    }
    (略)
}


■ ページを管理するViewController
import UIKit
class PageViewController: UIPageViewController, UIPageViewControllerDataSource,UIPageViewControllerDelegate, testDelegate { // 継承

    (略)

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let secondSettingVC = segue.destination as? SecondSettingViewController {
            secondSettingVC.soundSetting = soundSetting
            secondSettingVC.testdelegate = self  // 設定画面のtestdelegateに自身をセット
        }
    }

    // testDelegate
    func test(soundSetting: Int) {
        for content in contentVCs {
            let contents = content as? PageContentViewController 
            contents?.soundSetting = soundSetting        // プロパティの変更値をセット
            contents?.test1()                            // PCVCのメソッドを処理
            contents?.test2(soundSetting: soundSetting)  // PCVCのメソッドを処理
    }
    (略)
}


■ ページングされるコンテツのViewController
import UIKit
import AVFoundation
class PageContentViewController: UIViewController, AVAudioPlayerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        (略)
        // アプリ起動の際も背景色をセットするのに使用
        test1()     
        test2(soundSetting: soundSetting)
    }


   // デリゲートしてもらう処理(設定画面で変更された値で遷移元を再描画させる)
    func test1() {
        self.view.backgroundColor = UIColor.yellow
    }

    func test2(soundSetting: Int) {
        if soundSetting == 0 {
            self.view.backgroundColor = UIColor.red
        }
        if soundSetting == 1 {
            self.view.backgroundColor = UIColor.brown
        }
    }
    (略)
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.06%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る