从控制器内的页面更改 PageViewController 中的页面?

Change page in PageViewController from a page inside the controller?

我有一个 UIPageViewController,有 3 页。如何通过点击按钮切换到 UIPageViewController 内的页面? (每一页都是我在故事板中制作的单独 UIViewController)。

我已经尝试了很多代码,但是当我在我的故事板中制作 UIPageViewController 并且我这样设置它时,我总是遇到错误:

PageViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.dataSource = self;
    self.navigationController.navigationBarHidden = NO;

    [self setViewControllers:@[[self.storyboard instantiateViewControllerWithIdentifier:@"Main"]] 
                   direction:UIPageViewControllerNavigationDirectionForward 
                    animated:YES
                  completion:nil];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    if ([viewController isKindOfClass:[NavView2ViewController class]])
       return nil;

    return [self.storyboard instantiateViewControllerWithIdentifier:@"two"];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    if ([viewController isKindOfClass:[NavViewController class]])
        return nil;

    return [self.storyboard instantiateViewControllerWithIdentifier:@"one"];
}

您可以随时拨打 setViewController。用你想要的 ViewController 再次调用它。您也可以尝试通过访问 viewControllers 属性 找到您 ViewController。但我认为,此属性只会向您显示正在显示的当前 ViewController

您可以通过几种不同的方式执行此操作,具体取决于您希望如何构建应用程序的其余部分。

NSNotificationCenter

使用通知将允许您将子视图控制器与父页面视图控制器分离。它将足够灵活,允许在整个应用程序中添加其他代码来控制哪个页面可见。然而,一个缺点是存在一定程度的间接性,并且无法立即看出是什么触发了页面更改。

在您的 PageViewController 上设置观察者,例如:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.dataSource = self;
    self.navigationController.navigationBarHidden = NO;

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleButtonTap:)
                                                 name:MyPageViewControllerButtonNotification
                                               object:nil];

    [self setViewControllers:@[[self.storyboard instantiateViewControllerWithIdentifier:@"Main"]]
                   direction:UIPageViewControllerNavigationDirectionForward
                    animated:YES
                  completion:nil];
}

- (void)handleButtonTap:(NSNotification *)notification
{
    // Determine which page to move to based
    // on the notification object or userInfo
}

并且post来自任何子视图控制器的通知:

- (void)buttonTapped:(id)sender
{
    // Customise the sender or userInfo in order to
    // pass further information with the notification.
    [[NSNotificationCenter defaultCenter] postNotificationName:MyPageViewControllerButtonNotification
                                                        object:sender
                                                      userInfo:nil];
}

您将需要在您的 PageViewController 和您希望从中 post 此通知的所有视图控制器共有的文件中创建字符串 MyPageViewControllerButtonNotification

Delegates

使用委托模式将在视图控制器和页面视图控制器之间提供比通知更紧密的耦合,这可能会使以后更难以扩展。这样做的好处是很容易跟踪页面更改的来源。

在页面视图控制器和子视图控制器通用的文件中定义一个协议:

@protocol PageControlDelegate <NSObject>

- (void)moveToPage:(NSUInteger)pageNumber;

@end

确保您的页面视图控制器符合该协议:

@interface PageViewController : UIPageViewController <PageControlDelegate>

(并实现PageViewController.m中的方法)

- (void)moveToPage:(NSUInteger)pageNumber
{
    // switch to the appropriate page
}

当您为需要按钮交互的页面创建视图控制器时,您需要提供对您的 PageViewController 的(weak)引用:

@interface SomeChildViewController : UIViewController

@property (nonatomic, weak) id<PageControlDelegate> pageControlDelegate;

@end

以及设置下一页时的示例分配:

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    if ([viewController isKindOfClass:[NavViewController class]])
        return nil;

    SomeChildViewController *childController = [self.storyboard instantiateViewControllerWithIdentifier:@"one"];

    childController.pageControlDelegate = self;

    return childController;
}

然后您可以从任何具有 pageControlDelegate:

的子视图控制器直接调用此方法
- (void)buttonTapped:(id)sender
{
    NSUInteger pageNumber = // Decide which page to move to
    [self.pageControlDelegate moveToPage:pageNumber];
}

这是我想出的,它不需要黑魔法和代表。假设你有你的自定义 PageViewController class 调用 MyPageViewController 并且你实例化并将你的子视图控制器保存在 viewControllerList:

Swift 4

class MyPageViewController: UIPageViewController, UIPageViewControllerDataSource {

  lazy var viewControllersList: [UIViewController] = {
    let storyBoard = UIStoryboard(name: "Main", bundle: nil)
    let first = storyBoard.instantiateViewController(withIdentifier: "FirstViewController")
    let second = storyBoard.instantiateViewController(withIdentifier: "SecondViewController")

    return [first,
            second]
  }()
}

现在在你的子视图控制器中,你可以通过以下方式切换到下一个视图控制器。在此示例中,我在选择 table 行时执行切换:

Swift 4

  override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if let pageViewController = self.parent as? MyPageViewController {
      if let currentIndex = pageViewController.viewControllersList.index(of: self) {
        guard (pageViewController.viewControllersList.count - currentIndex) > 1 else {
          fatalError("Can't navigate to the next view controller!")
        }
        let nextViewController = pageViewController.viewControllersList[currentIndex + 1]
        pageViewController.setViewControllers([nextViewController], direction: .forward, animated: true, completion: nil)
      }
    }
  }