困っていること
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}
エラーの原因がわかりません。
どのようにすればスワイプでも動くようになるのでしょうか?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}
単純にidListにrestorationIdentifierがないのではないでしょうか?
PageViewControllerに表示しているViewControllerのソースを見せていただくことはできますか?
ありがとうございます。
追記にPageViewControllerのソースを追記に載せましたm(_ _)m
restorationIdentifierを初めて知ったので調べてみましたが、
これってデフォルトはnilみたいなんですが、どこかで設定できるんでしょうか?
あと気になったのでrestorationIdentifierを使った理由を教えていただきたいです。
逆にこちらが教えていただくような形になってしまい申し訳ないです。
( https://developer.apple.com/documentation/uikit/uiviewcontroller/1621499-restorationidentifier )
コード自体は自分が一から書いた訳ではなく検索して載っているものを参考にしてきたので薄い知識の状態で動かしていますm(_ _)m
segmentcontrolとスワイプの両輪で以前教えていただいたTabmanのタブ切り替えのようなものを自前で実装しようとしたのですが、日本語の最新の情報が限られていたのでそこから引用しています。
restorationIdentifierはこちらのコードを参考にしています。
https://teratail.com/questions/184661
すみません遅くなりました。
なるほど、とりあえずrestorationIdentifierがnilになっていてfirstIndex()を強制アンラップして落ちている可能性が高いような気がします。
なのでPageViewControllerに表示させているViewControllerにrestorationIdentifierではなく、
identifierみたいなStringの変数を作ってそれで判定するのはどうでしょうか?
何度もごめんなさい、
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
}