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

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

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

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

Q&A

解決済

2回答

656閲覧

PageViewController をスワイプしかけて辞めると、次にスワイプした時にインデックスが2つカウントアップしてしまう事への回避方法

taro_nii_chan

総合スコア207

Swift

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

0グッド

1クリップ

投稿2017/08/24 03:22

編集2017/08/27 05:03

やりたい事

UIPageViewController の子クラス PageViewController を作成し、extension の中で右にスワイプした時と左にスワイプさせたときで次のページもしくは前のページを表示させるようにしたいです。ページは要素数3つだけの配列にし、ループさせることでページがたくさんあるように見せようとしています。

困っていること

毎回綺麗にスワイプをすればほぼ思い通りに動くのですが、ページをめくりかけて「やっぱや〜めた」をすると、次にスワイプした時に2ページ分進んでしまいます。
理由が全く分からないわけではないのですが、「めくりかけてやっぱやめた」に対して page だけでなく index も変わらないようにするにはどうしたらいいのかが分からなくて困っています。回避する方法がありましたらご教授願います。

後、「ほぼ」と言いましたが、先へスワイプする時に一旦戻って進む事がある、もしくはその逆があります(print 文で確認)。

swift

1// 2// PageViewController.swift 3// ColorSample 4// 5 6import UIKit 7 8let count: Int = 1 << (6 * 3) 9 10class PageViewController: UIPageViewController { 11 12 var eachViewControllers: [EachViewController] = [] 13 var red: [Int] = [] 14 var green: [Int] = [] 15 var blue: [Int] = [] 16 var rgba: [UIColor] = [] 17 var index: Int = 1 18 var lastPage: Int = 1 19 20 override func viewDidLoad() { 21 super.viewDidLoad() 22 23 self.dataSource = self 24 25 // 1677万色の色の配列(要素数:count個)を作る 26 for i in 0..<count { 27 red.append(Int(arc4random_uniform(256))) 28 green.append(Int(arc4random_uniform(256))) 29 blue.append(Int(arc4random_uniform(256))) 30 rgba.append(UIColor(red: CGFloat(red[i]) / 255.0, green: CGFloat(green[i]) / 255.0, blue: CGFloat(blue[i]) / 255.0, alpha: CGFloat(1.0))) 31 } 32 33 // EachViewController を3つだけ作る 34 for i in 0..<3 { 35 let eachViewController = EachViewController() 36 eachViewController.setIndexTo(i) 37 eachViewController.setBackgroundRgba(rgba[i]) 38 eachViewControllers.append(eachViewController) 39 } 40 41 // 1番目の EachViewController をセットする 42 setViewControllers([eachViewControllers[1]], direction: .reverse, animated: false, completion: nil) 43 } 44 45 override func didReceiveMemoryWarning() { 46 super.didReceiveMemoryWarning() 47 // Dispose of any resources that can be recreated. 48 } 49 50} 51 52extension PageViewController : UIPageViewControllerDataSource { 53 54 // 次のページを表示する時に呼ばれる 55 func pageViewController(_ pageViewController: 56 UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { 57 58 // ページを取得する 59 let page = eachViewControllers.index(of: viewController as! EachViewController)! 60 61 // インデックスをセットする 62 index = (index + 1) % count 63 64 // 前後ののページの値をセットする 65 let nextPage = (page + 1) % 3 66 let prevPage = (page + 2) % 3 67 68 // デバッグ 69 print("index: \(index), lastPage: \(lastPage) , page: >> [\(prevPage) -> \(page) -> \(nextPage)]") 70 71 lastPage = nextPage 72 73 // 新しく表示するページに変数をセットする 74 eachViewControllers[lastPage].setBackgroundRgba(rgba[index]) 75 eachViewControllers[lastPage].setIndexTo(index) 76 77 // 要らなくなったページをリリースする処理をここに入れる 78 79 80 // 新しいページを返す 81 return eachViewControllers[lastPage] 82 } 83 84 // 前のページを表示する時に呼ばれる 85 func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController:UIViewController) -> UIViewController? { 86 87 // ページを取得する 88 let page = eachViewControllers.index(of: viewController as! EachViewController)! 89 90 // 前後ののページの値をセットする 91 let nextPage = (page + 1) % 3 92 let prevPage = (page + 2) % 3 93 94 // インデックスをセットする 95 index = (index - 1 + count) % count 96 97 // デバッグ 98 print("index: \(index), lastPage: \(lastPage) , page: << [\(prevPage) -> \(page) -> \(nextPage)]") 99 100 lastPage = prevPage 101 102 // 新しく表示するページに変数をセットする 103 eachViewControllers[lastPage].setBackgroundRgba(rgba[index]) 104 eachViewControllers[lastPage].setIndexTo(index) 105 106 // 要らなくなったページをリリースする処理をここに入れる 107 108 109 // 新しいページを返す 110 return eachViewControllers[lastPage] 111 } 112}

補足

言葉が足りませんでした。

swift

1 eachViewControllers[lastPage].setBackgroundRgba(rgba[lastPage]) 2 eachViewControllers[lastPage].setIndexTo(lastPage)

だと3ページ分しか表示できません。

やりたいのは

swift

1let count: Int = 1 << (6 * 3)

と定義したとても大きい数(count)だけのページを表示させたいのです。
(その、何番目なのかを index で持たせようとしています)

でも、
EachViewController クラスのインスタンスを count 個作るとメモリを喰って大変ですよね。
なので、添字が 0, 1, 2 の3つのインスタンスを

swift

1 for i in 0..<3 { 2 let eachViewController = EachViewController() 3 eachViewController.setIndexTo(i) 4 eachViewController.setBackgroundRgba(rgba[i]) 5 eachViewControllers.append(eachViewController) 6 }

の様に作り、
その背景色(rgba)及びページ番号(index)だけを書き換えて使いまわすことで大きい数のページがあるかのように見せたいという事です。

最初に eachViewControllers[0], eachViewControllers[1], eachViewControllers[2]を作って真ん中の eachViewControllers[1] を表示させておき、
1ページ進んだら[0], [1], [2]、から [1], [2], [0] へ(添字だけで書いてます)、さらに進んだら[2], [0], [1] と進めていくことで使いまわそうという考えです(戻る時も同様)。

どうしても言葉でうまく説明できないのがもどかしいのですが、少しでも伝わるでしょうか?

8/27 (14:03) 追記

コメントにも書きましたが、
pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:)
は使わずに、意図している動きが出来ているように見えています。

swift

1// PageViewController.swift 2extension PageViewController : UIPageViewControllerDataSource { 3 4 // 次のページを表示する時に呼ばれる 5 func pageViewController(_ pageViewController: 6 UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { 7 8 // ここにスワイプする前のページが入ってくるみたい(前回、スワイプ完了でも途中でやめても) 9 let eachViewController = pageViewController.viewControllers?.first as! EachViewController 10 11 // インデックスをインクリメントする 12 index = (eachViewController.index + 1) % count 13 14 // ページを取得する 15 let page = eachViewControllers.index(of: viewController as! EachViewController)! 16 17 // 次のページの値をセットする 18 let nextPage = (page + 1) % 3 19 20 // 新しく表示するページに変数をセットする 21 eachViewControllers[nextPage].setBackgroundRgba(rgba[index]) 22 eachViewControllers[nextPage].setRgb(red: red[index], green: green[index], blue: blue[index]) 23 eachViewControllers[nextPage].setIndexTo(index) 24 25 26 // 新しいページを返す 27 return eachViewControllers[nextPage] 28 } 29}

で上手く行ってそうです。

swift

1let eachViewController = pageViewController.viewControllers?.first as! EachViewController 2index = (eachViewController.index + 1) % count

がスワイプを途中でやめたときにインクリメントされない(経験則ですが)ようなので、
これを使ったのが良かったのかなと思っています。

勘違い等あるかも知れないので、閉めるのはまだにしておこうと思います。

気付いていない点、勘違いしてる点、
そもそもコードの書き方が汚い等ありましたら教えて下さい。

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

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

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

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

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

guest

回答2

0

まず、EachViewControllerを使いまわしたいなら、最低2個あれば足りると思います。

次に、EachViewControllerindexプロパティがあるのだから、PageViewControllerindex変数を持つ必要はありません。
EachViewControllerindexと、PageViewControllerindexで整合が取れなくなってしまっているのが、今回の問題点です。
indexは以下のように更新すれば良いです。

swift

1eachViewController.index = (eachViewController.index + 1) % count

投稿2017/08/27 06:45

Stripe

総合スコア2183

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

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

taro_nii_chan

2017/08/27 07:16

なるほどです。 EachViewController は表に出てるのと、隠れてる裏のとの2つで良かったんですね。 PageViewController が index を持っていなければいけないというのも先入観でした。 動作確認済みです。 ありがとうございました。
guest

0

ベストアンサー

どうしてこんなことをしようとしているのかわかりませんが、
ページ切り替え時に、新しく表示するEachViewControllerの背景色を
そのEachViewControllerが入っている配列番号に合わせてrgba配列から取り出したいなら、
そもそもindexという変数は不要で、

swift

1 eachViewControllers[lastPage].setBackgroundRgba(rgba[lastPage]) 2 eachViewControllers[lastPage].setIndexTo(lastPage)

のように、lastPageの番号の色を取り出したらいいと思うし、そもそも背景色の設定は
EachViewControllerの作成時に

swift

1 for i in 0..<3 { 2 let eachViewController = EachViewController() 3 eachViewController.setIndexTo(i) 4 eachViewController.setBackgroundRgba(rgba[i]) 5 eachViewControllers.append(eachViewController) 6 }

という形で配列番号に対応するする背景色を設定済みなので、
ページ切り替え時に背景色を設定し直す必要すらないように思います。


(8/24 21:40追記)

補足ありがとうございます。
やりたいことがわかりました。

そういうことであれば、
https://developer.apple.com/documentation/uikit/uipageviewcontrollerdelegate/1614090-pageviewcontroller
で説明されている、
UIPageViewControllerDelegatepageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:)メソッドを使うとうまくいくと思います。

現在実装しているUIPageViewControllerDataSourceのメソッドは、
次に表示するビューコントローラーを教えてくれと言っているだけであり、
それが呼ばれたからと言って必ずページ遷移するわけではありません。

上記で紹介したメソッドを使えば、画面遷移のアニメーションが完了したタイミングで呼ばれ、
次の画面に遷移した時は、transitionCompletedパラメータがtrue、
次に遷移するのをやめて戻した時は、transitionCompletedパラメータがfalse
で呼ばれます。
これを使うとうまくいくと思います。

また、
http://qiita.com/eKushida/items/d4fe95576a86f9e1d9fb
の「ページ番号の取得」の例にあるように、このタイミングでviewControllers.firstを取得すれば、
アニメーションが完了して現在表示されているビューコントローラーを得ることができますので、
そのビューコントローラーのプロパティにページ番号を入れておけば、
現在表示されているビューコントローラーのページ番号を知ることもできます。
このページ番号に相当しているのが、今回の質問ではindexなのだと思います。


(8/28 2:10追記)

確かに、EachViewControllerにindexを入れておけば、それで十分だと思います。

またStripeさんがおっしゃっている通り、それを使えばPageViewControllerの
indexプロパティは不要になるので、削除してしまえばよいと思います。

さらに、EachViewControllerを使い回す場合は2個で十分という話もありますが、
ページ切り替えに速度を求めないなら、使い回しでさえ不要で、ページ切り替え毎に
EachViewControllerを新しく作ればいいという話もあります。

(サンプル)

extension PageViewController : UIPageViewControllerDataSource { func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { return nextPage(pageViewController, moveTo:1) } func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController:UIViewController) -> UIViewController? { return nextPage(pageViewController, moveTo:-1) } func nextPage(_ pageViewController: UIPageViewController, moveTo:Int) -> EachViewController { let currentPage = pageViewController.viewControllers?.first as! EachViewController let newIndex = (currentPage.index + moveTo) % count let newPage = EachViewController() newPage.setBackgroundRgba(rgba[newIndex]) newPage.setIndexTo(newIndex) return newPage } }

投稿2017/08/24 04:31

編集2017/08/27 17:13
TakeOne

総合スコア6299

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

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

taro_nii_chan

2017/08/24 08:19

質問に補足を追加させていただきました。
TakeOne

2017/08/24 12:44

補足ありがとうございます。 やりたいことがわかりましたので、回答を追記しました。
taro_nii_chan

2017/08/24 13:19

おー、ありがとうございます。 そもそも使おうとしてるツール(メソッド)が違ってたんですね。 貴重な情報、ありがたいです。 続きは明日以降になると思いますが、 頑張ってみます。
TakeOne

2017/08/24 14:00

UIPageViewControllerDataSourceのメソッドはもちろん必要なんですが、現在のページ番号(indexプロパティ)を更新するのはそのタイミングじゃなくて、didFinishAnimatingのメソッドが呼ばれたタイミングだということです。頑張ってみて下さい。
taro_nii_chan

2017/08/26 14:51

意図した通りに動くようになりました。 pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:)は動きを追う上でとても役に立ちました。スワイプを初めた時、スワイプしきった時/しきらずに戻した時の動きの違いなど。 そして、結果的にそれは使わなくてもやりたい事が実現できていそうだと言うことが分かってきました。今日は遅いのでまた日を改めて詳しく途中経過報告致します。
taro_nii_chan

2017/08/27 05:04

質問に追記いたしました。 よろしければご覧くださいませ。
TakeOne

2017/08/27 17:15

回答追記しました。
taro_nii_chan

2017/08/27 23:06

なるほどです。 実は EachViewController を2つないし3つで実行すると、スワイプを連打すると真っ白なページになることがありました。その場面でちょこっとスワイプしかけて戻ると色のついたページが表示されていました。それが使いまわしなしだと綺麗に表示されます。「ページ切り替えに速度を求めないなら」とのことでしたけど、むしろこっちの方が高速に対応してるように見えました。 なのでこれで行こうと思います。 この質問に回答していただいて私としてはとても勉強になりました。 お二人にベストアンサーをさしあげたい気持ちでいっぱいです。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問