ios UIPageViewController 和故事板

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18398796/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 00:49:53  来源:igfitidea点击:

UIPageViewController and storyboard

iosuistoryboarduipageviewcontrollerxcode-storyboard

提问by Benjamin Toueg

I'm trying to configure a UIPageViewController SPECIFICALLY from storyboard:

我正在尝试从情节提要中特别配置 UIPageViewController:

enter image description here

在此处输入图片说明

TutorialPageViewController.h

TutorialPageViewController.h

#import <UIKit/UIKit.h>
@interface TutorialPageViewController : UIPageViewController <UIPageViewControllerDelegate, UIPageViewControllerDataSource>
@end

TutorialPageViewController.m

TutorialPageViewController.m

#import "TutorialPageViewController.h"

@interface TutorialPageViewController ()
@property (assign, nonatomic) NSInteger index;
@end

@implementation TutorialPageViewController
{
    NSArray *myViewControllers;
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.delegate = self;
    self.dataSource = self;
    [self didMoveToParentViewController:self];
    UIStoryboard *tutorialStoryboard = [UIStoryboard storyboardWithName:@"TutorialStoryboard" bundle:[NSBundle mainBundle]];
    UIViewController *tuto1 = [tutorialStoryboard instantiateViewControllerWithIdentifier:@"TutorialPageViewController_1"];
    UIViewController *tuto2 = [tutorialStoryboard instantiateViewControllerWithIdentifier:@"TutorialPageViewController_2"];

    myViewControllers = @[tuto1, tuto2, tuto1, tuto2];
    self.index = 0;

    [self setViewControllers:@[tuto1] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
}

- (UIViewController *)viewControllerAtIndex:(NSUInteger)index {
    return myViewControllers[index];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {

    NSUInteger index = self.index;

    if (index == 0) { return nil; }

    // Decrease the index by 1 to return
    index--;
    return [self viewControllerAtIndex:index];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {

    NSUInteger index = self.index;
    index++;
    if (index > [myViewControllers count]) { return nil; }

    return [self viewControllerAtIndex:index];
}

- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
    // The number of items reflected in the page indicator.
    return [myViewControllers count];
}

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
    // The selected item reflected in the page indicator.
    return 0;
}

@end

Problem is...

问题是...

  • The first page displays well with the page indicator. While swiping,
  • I can see properly the second page.
  • As soon as the transition finishes, I get a black screen (with the page indicator properly displaying page number 2). No user interaction is available anymore.
  • 第一页与页面指示器显示良好。滑动的同时,
  • 我可以正确地看到第二页。
  • 转换完成后,我会看到一个黑屏(页面指示器正确显示第 2 页)。用户交互不再可用。

回答by Fattie

2020. Swift updated.

2020. Swift 更新。

2017 answer...

2017答案...

Nowadays it is dead easy to do this simply using Storyboard.

如今,只需使用 Storyboard就可以轻松做到这一点。

These sort of "swiping full-screen tutorials" were popular as app "intros" for awhile, so I called the class below IntroPages.

这种“滑动全屏教程”作为应用程序的“介绍”流行了一段时间,所以我称之为下面的课程IntroPages

Step 1, make a container view that is a UIPageViewController.

第 1 步,创建一个 UIPageViewController 容器视图。

If new to iOS, here is a container view tutorial.

如果是 iOS 新手,这里有一个容器视图教程

( Note: if you don't know how to change the container view to a UIPageViewController, scroll down to the section "How to change..."on that tutorial.

(注意:如果您不知道如何将容器视图更改为 UIPageViewController,请向下滚动到该教程中的“如何更改...”部分。

enter image description here)

在此处输入图片说明)

You can make the container any shape you want. As with any container view, it can be full-screen or a small part of the screen - whatever you want.

你可以把容器做成任何你想要的形状。与任何容器视图一样,它可以是全屏或屏幕的一小部分 - 无论您想要什么。

Step 2,

第2步,

Make four straightforward view controllers which can be anything you want- images, text, tables, anything at all. (Purple in the example.)

制作四个简单的视图控制器,它们可以是你想要的任何东西——图像、文本、表格,任何东西。(示例中为紫色。)

four controllers

四个控制器

Note that they simply sit there on your storyboard, do not link them to anything.

请注意,它们只是坐在你的故事板上,不要将它们链接到任何东西。

Step 3, you must Set the IDsof those four pages. "id1", "id2", "id4", "id4" is fine.

第 3 步,您必须设置这四个页面的 ID。“id1”、“id2”、“id4”、“id4”都可以。

set the IDs

设置 ID

Step 4, copy and paste! Here's the class IntroPages,

第四步,复制粘贴!这是 IntroPages 类,

class IntroPages: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

    var pages = [UIViewController]()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
        self.dataSource = self

        let p1: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "id1")
        let p2: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "id2")
        let p3: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "id3")

        // etc ...

        pages.append(p1)
        pages.append(p2)
        pages.append(p3)

        // etc ...

        setViewControllers([p1], direction: UIPageViewController.NavigationDirection.forward, animated: false, completion: nil)
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController)-> UIViewController? {

        let cur = pages.firstIndex(of: viewController)!

        // if you prefer to NOT scroll circularly, simply add here:
        // if cur == 0 { return nil }

        var prev = (cur - 1) % pages.count
        if prev < 0 {
            prev = pages.count - 1
        }
        return pages[prev]
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController)-> UIViewController? {

        let cur = pages.firstIndex(of: viewController)!

        // if you prefer to NOT scroll circularly, simply add here:
        // if cur == (pages.count - 1) { return nil }

        let nxt = abs((cur + 1) % pages.count)
        return pages[nxt]
    }

    func presentationIndex(for pageViewController: UIPageViewController)-> Int {
        return pages.count
    }
}

(Look at the comments - there is code for either looping or linear paging as you prefer.)

(查看注释 - 根据您的喜好,有用于循环或线性分页的代码。)

That's all there is to it - you're done.

这就是它的全部 - 你已经完成了。

You simply set the transition style on the storyboard,

您只需在情节提要上设置过渡样式,

enter image description here

在此处输入图片说明

it is very likely that you want "Scroll", not the other one.

您很可能想要“滚动”,而不是另一个。

回答by EditOR

For someone, who wants to see working page scroll (forward / backward)

对于想要查看工作页面滚动(向前/向后)的人

-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController
     viewControllerBeforeViewController:(UIViewController *)viewController
  {
     NSUInteger currentIndex = [myViewControllers indexOfObject:viewController];
     // get the index of the current view controller on display

     if (currentIndex > 0)
     {
        return [myViewControllers objectAtIndex:currentIndex-1];
        // return the previous viewcontroller
     } else
     {
         return nil;
         // do nothing
     }
  }
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController
 viewControllerAfterViewController:(UIViewController *)viewController
  {
     NSUInteger currentIndex = [myViewControllers indexOfObject:viewController];
     // get the index of the current view controller on display
     // check if we are at the end and decide if we need to present
     // the next viewcontroller
     if (currentIndex < [myViewControllers count]-1)
     {
        return [myViewControllers objectAtIndex:currentIndex+1];
        // return the next view controller
     } else
     {
        return nil;
        // do nothing
     }
  }


Just to add to this great answer by EditOR, here's what you do if you prefer "round and around" paging: still using the same technique of EditOR

只是为了添加编辑器的这个很好的答案,如果您更喜欢“循环”分页,请执行以下操作:仍然使用相同的编辑器技术

-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController
        viewControllerBeforeViewController:(UIViewController *)viewController
    {
    NSUInteger currentIndex = [myViewControllers indexOfObject:viewController];

    --currentIndex;
    currentIndex = currentIndex % (myViewControllers.count);
    return [myViewControllers objectAtIndex:currentIndex];
    }

-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController
        viewControllerAfterViewController:(UIViewController *)viewController
    {
    NSUInteger currentIndex = [myViewControllers indexOfObject:viewController];

    ++currentIndex;
    currentIndex = currentIndex % (myViewControllers.count);
    return [myViewControllers objectAtIndex:currentIndex];
    } 

回答by samwize

Extending Joe Blow's answerwith Swift code for the UIPageViewControllerclass:

使用类的Swift 代码扩展Joe Blow 的回答UIPageViewController

import UIKit

class MyPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

    var pages = [UIViewController]()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.delegate = self
        self.dataSource = self

        let page1: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("page1")
        let page2: UIViewController! = storyboard?.instantiateViewControllerWithIdentifier("page2")

        pages.append(page1)
        pages.append(page2)

        setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
        let currentIndex = pages.indexOf(viewController)!
        let previousIndex = abs((currentIndex - 1) % pages.count)
        return pages[previousIndex]
    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
        let currentIndex = pages.indexOf(viewController)!
        let nextIndex = abs((currentIndex + 1) % pages.count)
        return pages[nextIndex]
    }

    func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
        return pages.count
    }

    func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
        return 0
    }
}

Read more on using UIPageViewController with container viewwith storyboard setup.

阅读更多关于使用 UIPageViewController 和带有故事板设置的容器视图

回答by William T.

There seems to be a lot of questions regarding UIPageViewController in Storyboard.

似乎有很多关于 Storyboard 中 UIPageViewController 的问题。

Here is some demo codeto show you how you can use the UIPageViewController in storyboard as a standalone full screen view or as a UIContainerView, if you want to page only a small area of your screen.

下面是一些演示代码,向您展示如何将故事板中的 UIPageViewController 作为独立的全屏视图或作为 UIContainerView 使用,如果您只想对屏幕的一小部分区域进行分页。

enter image description hereenter image description hereenter image description here

在此处输入图片说明在此处输入图片说明在此处输入图片说明

回答by PLJNS

Updated for Swift 3:

为 Swift 3 更新:

class YourPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

    var pages = [UIViewController]()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.delegate = self
        self.dataSource = self

        let page1: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "page1")
        let page2: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "page2")

        pages.append(page1)
        pages.append(page2)

        setViewControllers([page1], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        let currentIndex = pages.index(of: viewController)!
        let previousIndex = abs((currentIndex - 1) % pages.count)
        return pages[previousIndex]
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        let currentIndex = pages.index(of: viewController)!
        let nextIndex = abs((currentIndex + 1) % pages.count)
        return pages[nextIndex]
    }

    func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        return pages.count
    }
}

回答by Aquila Sagitta

To have infinite scroll back using @samwize's answer you need to add conditional to check for negative values. Otherwise you just switch between the first and second page. This is only necessary if you plan on having more than two pages.

要使用@samwize 的答案无限回滚,您需要添加条件以检查负值。否则,您只需在第一页和第二页之间切换。仅当您计划拥有两页以上的页面时才需要这样做。

func pageViewController(_ pageViewController: UIPageViewController,
                            viewControllerBefore viewController: UIViewController) -> UIViewController? {
        let currentIndex = pages.index(of: viewController)!
        var previousIndex = (currentIndex - 1) % pages.count
        if previousIndex < 0 {previousIndex = pages.count - 1}
        return pages[previousIndex]
    }

回答by jrc

Answer for Swift 5:

Swift 5 的答案:

import UIKit

class MyPageViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

    var allViewControllers = [UIViewController]()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.dataSource = self

        let vc1: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "viewController1")
        let vc2: UIViewController! = storyboard?.instantiateViewController(withIdentifier: "viewController2")
        allViewControllers = [vc1, vc2]

        self.setViewControllers([vc1], direction: UIPageViewController.NavigationDirection.forward, animated: false)
    }

    // UIPageViewControllerDataSource

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?
    {
        let currentIndex = allViewControllers.firstIndex(of: viewController)!
        return currentIndex == 0 ? nil : allViewControllers[currentIndex-1]
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?
    {
        let currentIndex = allViewControllers.firstIndex(of: viewController)!
        return currentIndex == allViewControllers.count-1 ? nil : allViewControllers[currentIndex+1]
    }

    func presentationCount(for pageViewController: UIPageViewController) -> Int
    {
      return allViewControllers.count
    }

    func presentationIndex(for pageViewController: UIPageViewController) -> Int
    {
        return 0
    }
}

回答by Rudolf Adamkovi?

The problem is that you're improperly reusing UIViewControllerinstances:

问题是您不正确地重用UIViewController实例:

myViewControllers = @[tuto1, tuto2, tuto1, tuto2];

I would suggest you to have an NSMutableSetthat would serve as a pool of reusable UIViewControllerinstances.

我建议您拥有一个NSMutableSet可用作可重用UIViewController实例池的对象。

In viewControllerBeforeViewController:and viewControllerBeforeViewController:search your NSMutableSetusing NSPredicateto find a UIViewControllerwith parentViewControllerequal to nil. If you find one, return it. If not, instantiate a new one, add it to the NSMutableSetand then return it.

viewControllerBeforeViewController:viewControllerBeforeViewController:搜索您NSMutableSet使用NSPredicate找到一个UIViewController具有parentViewController相等nil。如果你找到了,就退货。如果没有,则实例化一个新的,将其添加到 中NSMutableSet,然后返回它。

When you're done and your tests are passing, you can extract the pool into its own class.

完成并且测试通过后,您可以将池提取到它自己的类中。