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

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

ただいまの
回答率

87.94%

PageViewControllerとContenerViewを使い、スワイプ又はボタンで複数のViewControllerを遷移したいのですが、各ViewControllerの配置順が固定できません

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,478

score 13

お世話になっております。

今回、ご質問させて頂きたい内容は、PageViewControllerとContenerViewを使い、スワイプ又はボタンで配列に格納した複数のViewController(今回は3つ)を遷移にて行き来できる、という内容を実装したいと思っております。
しかし、エラー自体は出ていないのですが、下記のような意図しない動きをしてしまい困っております。

① 立ち上げ時、配列の2番目に入っているViewControllerの画面が2つ分表示される(3ページ分しか用意していないはずが、4ページ分のスワイプができてしまう)

② 上部に配置したボタンで各ViewControllerに遷移する方法で、ページ3(サンプルでは"red")からページ1(サンプルでは"green")又は、ページ2(サンプルでは"blue")に遷移した際も、①と同じ現象が起きてします。

以上の意図しない動きが出てしまい、原因がわからず困っております。
わかりづらい表現で申し訳ありませんが、お分かりになる方がいらっしゃいましたら、原因と解決法をお教え頂きたく思います。
お手数ですが、ご回答をお願い致します。

※下記に開発コードと開発中のストーリーボード画面を記載させて頂きます。
※ストーリーボードのIDは全てswiftファイル名と同じです。
※PageViewControllerの移動法はScrollです。

イメージ説明

コード

//親のコンテナビュー側のswiftファイル

import UIKit

struct Page_new_Settings {

    //UIPageViewControllerに配置するUIViewControllerクラスの名称
    static let pageControllerIdentifierList : [String] = ["green","blue","red"]

    //UIPageViewControllerに追加するViewControllerのリストを生成する
    static func generateViewControllerList2() -> [UIViewController] {

        var viewControllers2 : [UIViewController] = []
        self.pageControllerIdentifierList.forEach { viewControllerName in

            //ViewControllerのIdentifierからViewControllerを作る
            let viewController = UIStoryboard(name: "Main", bundle: nil) .instantiateViewControllerWithIdentifier("\(viewControllerName)")

            viewControllers2.append(viewController)
        }

        print(viewControllers2)
        return viewControllers2
    }

}




class gamen3_ViewController: UIViewController, UIPageViewControllerDataSource{


    // ページングして表示させる ViewControllerを保持する
    var pageViewController: UIPageViewController?
    var viewControllerIndex : Int = 0


    override func viewDidLoad() {
        super.viewDidLoad()

        // ContainerView に Embed した UIPageViewController を取得する
        pageViewController = childViewControllers[0] as? UIPageViewController

        // dataSource を設定する
        pageViewController!.dataSource = self

        // 最初に表示する画面として配列の先頭の ViewController を設定する
        pageViewController!.setViewControllers([Page_new_Settings.generateViewControllerList2().first!], direction: .Forward, animated: false, completion: nil)

    }



    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }


    // コンテナビュー側に配置した3つのボタンがタップされたらそれぞれのタグによってコントロラーを呼び出す
    @IBAction func handleButton(sender: UIButton) {
        switch sender.tag {
        case 0:

            let targetViewControllers : [UIViewController] = Page_new_Settings.generateViewControllerList2()
            pageViewController!.setViewControllers([targetViewControllers[0]], direction: .Forward, animated: false, completion: nil)

            if self.viewControllerIndex == 0 {
                return
            } else {
                self.viewControllerIndex = self.viewControllerIndex - 1
            }


        case 1:

            let targetViewControllers : [UIViewController] = Page_new_Settings.generateViewControllerList2()


            pageViewController!.setViewControllers([targetViewControllers[1]], direction: .Forward, animated: false, completion: nil)

            if self.viewControllerIndex == 1 {
                return
            } else {
                self.viewControllerIndex = self.viewControllerIndex + 1
            }

        case 2:
            let targetViewControllers : [UIViewController] = Page_new_Settings.generateViewControllerList2()


            pageViewController!.setViewControllers([targetViewControllers[2]], direction: .Forward, animated: false, completion: nil)

            if self.viewControllerIndex == 2 {
                return
            }

            else if(self.viewControllerIndex == 1){

            self.viewControllerIndex = self.viewControllerIndex + 1
            }
            else {
                self.viewControllerIndex = self.viewControllerIndex + 2
            }



        default:
            break
        }
    }

    // 戻る方向にページ送りした時に呼ばれるメソッド
    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {

        let targetViewControllers : [UIViewController] = Page_new_Settings.generateViewControllerList2()

        if self.viewControllerIndex == 0 {
            return nil
        } else {

            self.viewControllerIndex = self.viewControllerIndex - 1
        }



        return targetViewControllers[self.viewControllerIndex]

    }

    // 進む方向にページ送りした時に呼ばれるメソッド
    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {

        let targetViewControllers : [UIViewController] = Page_new_Settings.generateViewControllerList2()

        if self.viewControllerIndex == targetViewControllers.count - 1 {

            return nil
        } else {

            self.viewControllerIndex = self.viewControllerIndex + 1
        }



        return targetViewControllers[self.viewControllerIndex]
    }


}
//1ページ目
import UIKit

class green: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }

}
//2ページ目
import UIKit

class blue: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }


}
//3ページ目
import UIKit

class red: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }
   override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }


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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • fuzzball

    2016/08/31 15:48

    スワイプ時の「緑→青→青→赤」ですが、これは起動直後になるのではなく、ボタン押下後のスワイプで発生する症状でしょうか?

    キャンセル

  • Ando77

    2016/08/31 15:56

    ご指摘頂きました「緑→青→青→赤」は前画面からの移動後(単なるshowによる画面遷移)すでに発生しているので、ボタン押下後ではなく、起動直後だと思われます。

    キャンセル

  • fuzzball

    2016/08/31 16:06

    起動直後から発生していしているのであれば的外れかも知れませんが、回答を修正したので見てみて下さい。

    キャンセル

回答 1

checkベストアンサー

+1

ボタン押下時の後半の処理、例えば「緑画面」ボタンの場合に、

if self.viewControllerIndex == 0 {
    return
} else {
    self.viewControllerIndex = self.viewControllerIndex - 1
}

という処理が書かれています。
これは「戻る方向スワイプ」の処理のコピペのようですが、ボタン押下時のコードとしては正しくありません。

ボタン押下時は、押したボタンの画面にダイレクトに遷移するのですから、「緑画面」ボタンの場合は、

self.viewControllerIndex = 0

これで良いはずです。
他のボタンについても同様に修正すればいいのですが、3つとも同じような処理なので、下記のようにsender.tagを使って簡潔に書くことができます。

@IBAction func handleButton(sender: UIButton) {
    let targetViewControllers : [UIViewController] = Page_new_Settings.generateViewControllerList2()
    pageViewController!.setViewControllers([targetViewControllers[sender.tag]], direction: .Forward, animated: false, completion: nil)
    self.viewControllerIndex = sender.tag
}

 さらに修正

全体的に書き換えたので回答丸投げです。

大雑把に、

  • generateViewControllerList2()が呼ばれるたびに(緑青赤の)ViewControllerが生成されて気持ちが悪いので一度だけ生成するようにした。
  • viewControllerIndexが、実際の値と一致していないような気がしたので使うのをやめた。

修正点には「※」を付けておいたので確認して下さい。

class gamen3_ViewController: UIViewController, UIPageViewControllerDataSource {

    //※最初に一度だけViewControllerのテーブルを作る
    let targetViewControllers : [UIViewController] = Page_new_Settings.generateViewControllerList2()

    // ページングして表示させる ViewControllerを保持する
    var pageViewController: UIPageViewController?
    //var viewControllerIndex : Int = 0 //※この変数は使わない

    override func viewDidLoad() {
        super.viewDidLoad()

        // ContainerView に Embed した UIPageViewController を取得する
        pageViewController = childViewControllers[0] as? UIPageViewController

        // dataSource を設定する
        pageViewController!.dataSource = self

        // 最初に表示する画面として配列の先頭の ViewController を設定する
        pageViewController!.setViewControllers([targetViewControllers.first!], direction: .Forward, animated: false, completion: nil) //※targetViewControllersを使うように変更
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // コンテナビュー側に配置した3つのボタンがタップされたらそれぞれのタグによってコントロラーを呼び出す
    @IBAction func handleButton(sender: UIButton) {
        //let targetViewControllers : [UIViewController] = Page_new_Settings.generateViewControllerList2() //※削除
        pageViewController!.setViewControllers([targetViewControllers[sender.tag]], direction: .Forward, animated: false, completion: nil)
    }

    // 戻る方向にページ送りした時に呼ばれるメソッド
    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {

        //let targetViewControllers : [UIViewController] = Page_new_Settings.generateViewControllerList2() //※削除

        if let index = targetViewControllers.indexOf(viewController) { //※インデックスを調べる
            //print("[BEFORE] index=", index)
            if index > 0 {
                return targetViewControllers[index-1] //※1つ前のViewControllerを返す
            }
        }
        return nil
    }

    // 進む方向にページ送りした時に呼ばれるメソッド
    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {

        //let targetViewControllers : [UIViewController] = Page_new_Settings.generateViewControllerList2() //※削除

        if let index = targetViewControllers.indexOf(viewController) { //※インデックスを調べる
            //print("[AFTER] index=", index)
            if index < targetViewControllers.count-1 {
                return targetViewControllers[index+1] //※1つ先のViewControllerを返す
            }
        }
        return nil
    }

}

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/08/31 16:22

    ボタン押下時のコードの訂正につきまして、ご指摘ありがとうございます。
    修正後、赤画面から「緑画面ボタン」で戻った際には、やはり「緑→青→青→赤」になってしまい、赤画面から「青画面ボタン」で戻った際には、落ちることは無くなりましたが、
    ① 始めに緑側へスワイプした時
    「緑←緑←青(ここに戻る)→赤」
    ② 始めに赤側へスワイプした時
    「緑←青(ここに戻る)→赤→赤」
    上記の2パターンになってしまいます。
    fuzzball様側では、問題なく動作しているということは、コードではなく、ストーリーボード上の制約や設定に問題があるのでしょうか?
    お手数をかけてしまい、大変恐れ入ります。

    キャンセル

  • 2016/08/31 16:31

    「問題なく動作している」というのは私の勘違い‥というか調査不足です。その後、調べた後で回答を修正しました。もう少し調べてみます。

    キャンセル

  • 2016/08/31 16:48

    ご返信ありがとうございます。
    私一人では、知識が少なく、とても原因がわかりそうにありませんので、
    大変お手数ですが、引き続き宜しくお願い致します。

    キャンセル

  • 2016/08/31 17:38

    fuzzball様
    ご回答の編集ありがとうございます。
    ご指摘頂きました編集点は自分では気付けなかった点なので、大変参考になりました。
    頂きましたコードにて動かしたところ、何の誤作動もなく動作致しました。
    編集頂きました各点を一つずつ見直して、今後のために勉強させて頂きたいと思います。
    わかりにくい説明を理解して頂いた上に、複数回にわたりご回答頂き、大変お世話になりました。誠にありがとうございました。

    キャンセル

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

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

関連した質問

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

  • トップ
  • Swiftに関する質問
  • PageViewControllerとContenerViewを使い、スワイプ又はボタンで複数のViewControllerを遷移したいのですが、各ViewControllerの配置順が固定できません