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

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

詳細はこちら
Swift

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

Q&A

1回答

1306閲覧

SegmentedControlとスワイプの両方でページを切り替えたい

samson66

総合スコア35

Swift

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

0グッド

0クリップ

投稿2019/09/21 16:50

編集2019/09/22 01:56

困っていること
UIPageViewControllerにEmbedしたViewControllerのSegmentedControlでページを切り替えることは出来るのですが、スワイプでページを切り替えようとすると以下のエラーが発生します。

エラー文
Fatal error: Unexpectedly found nil while unwrapping an Optional value
エラー発生場所
イメージ説明
コード全文

MainViewController

1import UIKit 2 3class MainViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate { 4 5 let idList: [String] = ["first", "second", "third"] 6 7 var pageViewController: UIPageViewController! 8 var viewControllers: [UIViewController] = [] 9 10 @IBOutlet weak var selectTab: UISegmentedControl! 11 12 override func viewDidLoad() { 13 super.viewDidLoad() 14 15 selectTab.setTitle("1枚目", forSegmentAt: 0) 16 selectTab.setTitle("2枚目", forSegmentAt: 1) 17 selectTab.setTitle("3枚目", forSegmentAt: 2) 18 19 for id in idList { 20 viewControllers.append((storyboard?.instantiateViewController(withIdentifier: id))!) 21 } 22 23 pageViewController = children[0] as? UIPageViewController 24 pageViewController.setViewControllers([viewControllers[0]], direction: .forward, animated: true, completion: nil) 25 26 pageViewController.dataSource = self 27 pageViewController.delegate = self 28 } 29 30 override func didReceiveMemoryWarning() { 31 super.didReceiveMemoryWarning() 32 } 33 34 func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { 35 let index = idList.firstIndex(of: viewController.restorationIdentifier!)! 36 if (index > 0) { 37 print("before") 38 return storyboard!.instantiateViewController(withIdentifier: idList[index - 1]) 39 } 40 return nil 41 } 42 43 func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { 44 let index = idList.firstIndex(of: viewController.restorationIdentifier!)! 45 if (index < idList.count - 1) { 46 print("after") 47 return storyboard!.instantiateViewController(withIdentifier: idList[index + 1]) 48 } 49 return nil 50 } 51 52 func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { 53 let index = idList.firstIndex(of: (pageViewController.viewControllers?.first!.restorationIdentifier)!) 54 self.selectTab.selectedSegmentIndex = index! 55 } 56 57 @IBAction func selectedTab(_ sender: UISegmentedControl) { 58 switch sender.selectedSegmentIndex { 59 case 0: 60 pageViewController.setViewControllers([viewControllers[0]], direction: .reverse, animated: false, completion: nil) 61 break 62 case 1: 63 pageViewController.setViewControllers([viewControllers[1]], direction: .reverse, animated: false, completion: nil) 64 break 65 case 2: 66 pageViewController.setViewControllers([viewControllers[2]], direction: .forward, animated: false, completion: nil) 67 break 68 default: 69 return 70 } 71 } 72}

StoryBoard
イメージ説明

エラーの原因がわかりません。
どのようにすればスワイプでも動くようになるのでしょうか?m(_ _)m

追記

PageViewController

1import UIKit 2 3class PageViewController: UIPageViewController { 4 5 override func viewDidLoad() { 6 super.viewDidLoad() 7 // Do any additional setup after loading the view. 8 self.setViewControllers([getFirst()], direction: .forward, animated: true, completion: nil) 9 self.dataSource = self 10 } 11 12 func getFirst() -> FirstViewController { 13 return storyboard!.instantiateViewController(withIdentifier: "first") as! FirstViewController 14 } 15 16 func getSecond() -> SecondViewController { 17 return storyboard!.instantiateViewController(withIdentifier: "second") as! SecondViewController 18 } 19 20 func getThird() -> ThirdViewController { 21 return storyboard!.instantiateViewController(withIdentifier: "third") as! ThirdViewController 22 } 23 24} 25 26extension PageViewController : UIPageViewControllerDataSource { 27 28 func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { 29 30 if viewController.isKind(of: ThirdViewController.self) { 31 return getSecond() 32 } else if viewController.isKind(of: SecondViewController.self) { 33 return getFirst() 34 } else { 35 return nil 36 } 37 } 38 39 func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { 40 41 if viewController.isKind(of: FirstViewController.self) { 42 return getSecond() 43 } else if viewController.isKind(of: SecondViewController.self) { 44 return getThird() 45 } else { 46 return nil 47 } 48 } 49 50}

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

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

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

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

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

hayabusabusash

2019/09/22 00:31

単純にidListにrestorationIdentifierがないのではないでしょうか? PageViewControllerに表示しているViewControllerのソースを見せていただくことはできますか?
samson66

2019/09/22 01:58

ありがとうございます。 追記にPageViewControllerのソースを追記に載せましたm(_ _)m
hayabusabusash

2019/09/22 02:51

restorationIdentifierを初めて知ったので調べてみましたが、 これってデフォルトはnilみたいなんですが、どこかで設定できるんでしょうか? あと気になったのでrestorationIdentifierを使った理由を教えていただきたいです。 逆にこちらが教えていただくような形になってしまい申し訳ないです。 ( https://developer.apple.com/documentation/uikit/uiviewcontroller/1621499-restorationidentifier )
samson66

2019/09/22 03:26 編集

コード自体は自分が一から書いた訳ではなく検索して載っているものを参考にしてきたので薄い知識の状態で動かしていますm(_ _)m segmentcontrolとスワイプの両輪で以前教えていただいたTabmanのタブ切り替えのようなものを自前で実装しようとしたのですが、日本語の最新の情報が限られていたのでそこから引用しています。 restorationIdentifierはこちらのコードを参考にしています。 https://teratail.com/questions/184661
hayabusabusash

2019/09/22 13:17

すみません遅くなりました。 なるほど、とりあえずrestorationIdentifierがnilになっていてfirstIndex()を強制アンラップして落ちている可能性が高いような気がします。 なのでPageViewControllerに表示させているViewControllerにrestorationIdentifierではなく、 identifierみたいなStringの変数を作ってそれで判定するのはどうでしょうか?
samson66

2019/09/23 02:23

何度もごめんなさい、 pageViewController(_: viewControllerBefore:) pageViewController(_: viewControllerAfter:) の仕組みを https://qiita.com/yajamon/items/e1754e7fc847b595c26a の図解の部分で何となく機能は理解はできたのですが、 中身に書かれているコードは人が書いたのを見て何となく動きを把握している状態です。 中身のコードのどこの部分をString型の変数で置き換えればよいのかがわからないです。 .restorationIdentifierの部分に変数を置いたところviewControllerが持っていない値だという 警告文が表示されました。 //pageViewController(_: viewControllerBefore:) func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { let index = idList.firstIndex(of: viewController.restorationIdentifier!)! if (index > 0) { return storyboard!.instantiateViewController(withIdentifier: idList[index - 1]) } return nil }
guest

回答1

0

PageViewControllerに表示している
firstsecondthirdのStoryboard identifierを使って生成しているViewControllerがあると思います。
そのViewControllerに適当な変数を作ってしまえばいいと思います。

Swift

1class FirstViewController: UIViewController { 2 3 let pageIdentifier: String = "first" 4 5 // 省略します 6}

この変数をrestorationIdentifierの部分に置き換えてしまえばいいと思います。
ただし、PageViewControllerのデリゲートメソッドの引数のViewControllerをキャストしないとダメです。

Swift

1func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { 2 // UIViewControllerのままだと変数にアクセスできないのでキャストします 3 if let firstViewController = viewController as? FirstViewController { 4 // 作った変数を使ってindexを取得 5 let index = idList.firstIndex(of: firstViewController.pageIdentifier)! 6 if (index > 0) { 7 print("before") 8 return storyboard!.instantiateViewController(withIdentifier: idList[index - 1]) 9 } 10 return nil 11 } 12 // 以降必要な分だけ分岐 13 }

このままだとキャストの分岐が長くなってしまうので、
PageViewControllerに表示するViewControllerに共通のViewControllerを作って、
それを継承させる形にするともう少し短く書けるかもしれません。

Swift

1// 共通のViewController 2class PageChildViewController: UIViewController { 3 4 var pageIdentifier: String = "" 5}

投稿2019/09/24 00:40

編集2019/09/24 00:44
hayabusabusash

総合スコア767

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問