ios UIPageViewController和故事板

cidc1ykv  于 2023-04-08  发布在  iOS
关注(0)|答案(8)|浏览(177)

我正在尝试从故事板中配置UIPageViewController:

TutorialPageViewController.h

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

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

问题是……

  • 第一页用页面指示器显示效果很好,滑动时
  • 我可以看到第二页。
    *切换完成后,黑屏(页面指示器正确显示第2页),无法进行用户交互。
vngu2lb8

vngu2lb81#

2023. Swift更新。

2017年答案...

如今,这是死容易做到这一点简单地使用故事板
这种“全屏滑动教程”作为应用程序“介绍”流行了一段时间,所以我把下面的类称为IntroPages
第一步,做一个容器视图,容器视图为UIPageViewController。
如果是iOS新手,这里有一个container view tutorial
(注意:如果您不知道如何将容器视图“更改”为UIPageViewController,请向下滚动到该教程中的**“如何更改...”**部分!



你可以让容器任何你想要的形状。和任何容器视图一样,它可以是全屏的,也可以是屏幕的一小部分--任何你想要的。
第二步,
制作四个简单的、普通的视图控制器,它们可以是任何你想要的-图像、文本、表格,任何东西。

请注意,他们只是坐在那里你的故事板,不要链接到任何东西。
第三步,需要设置这四个页面的ID,id 1、id 2、id 3、id 4即可。

第四步,复制并粘贴!

import UIKit
class IntroPages: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

    // ** see note below about ".scroll" mode

    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在注解中-根据您的喜好,有用于循环线性分页的代码。)
在故事板上查看UIPageViewController。将类设置为IntroPages
就这么简单-你完了。
您只需在情节串连图板上设置过渡样式,

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

使用正常、常用、“.scroll”模式的重要注意事项...

令人难以置信的是,苹果已经从Xcode故事板的IB下拉列表中删除了正常的“.scroll”选项。
(On故事板,他们只允许你选择奇怪的选项,如“ curl ”,从来没有人使用。)
你现在必须在代码中完成这一点,这很简单。添加这一行:

class IntroPages: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {

   required init?(coder: NSCoder) {
       super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
   }

   etc, as above ... var pages = [UIViewController]()

这是愚蠢的,但这就是它的方式。
你完了!现在…

添加UIPageControl介绍...

您可以在最高级别的 Package 器类中添加UIPageControl,即上面图像示例中的“Intro”。
(So,令人惊讶的是 * 不是 * 在页面视图控制器中,而不是在“IntroPages”中。)
因此,在故事板上,非常简单地将UIPageControl拖到“Intro”上。
注意!奇怪的是,在故事板中,你不能移动UIPageControl
当你把一个页面控件拖到视图控制器上时,它只是坐在一个固定的地方。这很奇怪,但事实就是如此。不要浪费时间试图移动它:)
在Intro中,您需要以通常的方式访问页面视图控制器(IntroPages):

class Intro: UIViewController {

    @IBOutlet var pageControl: UIPageControl!
    var pageViewController: IntroPages!
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "segueIntroPages" {
            // if new to container views, identifier explained here:
            // https://stackoverflow.com/a/23403979/294884
            pageViewController = (segue.destination as! IntroPages)
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.bringSubviewToFront(pageControl)
        pageControl.currentPage = 0
    }

请注意这一关键行:

view.bringSubviewToFront(pageControl)

添加此功能

@IBAction func userDidChangePageControl(_ sender: UIPageControl) {}

然后在情节提要中单击页面控件并将valueChanged拖动到该函数。
该函数的最简单的大纲版本

@IBAction func userDidChangePageControl(_ sender: UIPageControl) {
    let newIndex = sender.currentPage
    pageViewController.setViewControllers(
        [pageViewController.pages[newIndex]],
        direction: (pageViewController.currentIndex < newIndex)
                     ? .forward : .reverse,
        animated: true, completion: nil)
}

但是请参见本QAhttps://stackoverflow.com/questions/66545624,以了解更全面的调查。

在IntroPages中添加属性...

var currentIndex: Int {
    if let visibleViewController = viewControllers?.first,
       let ci = pages.firstIndex(of: visibleViewController) {
        return ci
    }
    else {
        return 0
    }
}

在IntroPages中还添加以下内容,以便当用户滑动屏幕时,UIPageControl知道该做什么:

func pageViewController(_ pageViewController: UIPageViewController, 
       didFinishAnimating finished: Bool,
       previousViewControllers: [UIViewController], 
       transitionCompleted completed: Bool) {
    (parent as? Intro)?.pageControl.currentPage = currentIndex
}

在IntroPages中,由于Apple的错误/异常行为,还添加了以下内容:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    let subViews = view.subviews
    var scrollView: UIScrollView? = nil
    var pageControl: UIPageControl? = nil
    
    // maintain this code order...
    for view in subViews {
        if view.isKind(of: UIScrollView.self) {
            scrollView = view as? UIScrollView
        }
        else if view.isKind(of: UIPageControl.self) {
            pageControl = view as? UIPageControl
        }
    }
    
    // maintain this code order...
    if (scrollView != nil && pageControl != nil) {
        scrollView?.frame = view.bounds
        if let pageControl = pageControl {
                    view.bringSubviewToFront(pageControl) }
    }
}

你已经在路上了

u1ehiz5o

u1ehiz5o2#

对于希望看到工作页面滚动(向前/向后)的用户

-(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
     }
  }

只是为了补充Editoror的这个伟大的答案,如果你喜欢“循环”分页,下面是你要做的:仍然使用相同的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];
    }
tyky79it

tyky79it3#

使用Swift代码为UIPageViewController类扩展Joe Blow's answer

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
    }
}

阅读更多关于using UIPageViewController with container view的故事板设置。

9q78igpj

9q78igpj4#

在Storyboard中似乎有很多关于UIPageViewController的问题。
这里是some demo code,向您展示如何在故事板中使用UIPageViewController作为独立的全屏视图或UIContainerView,如果您只想在屏幕的一小部分区域分页。

zlhcx6iw

zlhcx6iw5#

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
    }
}
icnyk63a

icnyk63a6#

要使用@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]
    }
ffvjumwh

ffvjumwh7#

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

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

我建议您拥有一个NSMutableSet,它将作为可重用UIViewController示例的池。
viewControllerBeforeViewController:viewControllerBeforeViewController:中,使用NSPredicate搜索NSMutableSet,找到一个UIViewController,其中parentViewController等于nil。如果找到一个,返回它。如果没有,示例化一个新的,将其添加到NSMutableSet,然后返回它。
当您完成并且测试通过时,您可以将池提取到它自己的类中。

a64a0gku

a64a0gku8#

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
    }
}

相关问题