Swift iOS: 如何使用按钮触发下一页

Swift iOS: how to trigger next page using buttons

我有一个 QuizViewController,它扩展了 UIViewControllerUIPageControllerDelegate 和一个 UIPageViewControllerDataSource.

里面QuizViewController.swift

private var pageViewController: UIPageViewController?

private func createPageViewController() {
       let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("QuizPageViewController") as! UIPageViewController
       pageController.dataSource = self

       if pageInfo.count > 0 {
           let firstController = getItemController(0)!
           let startingViewControllers: NSArray = [firstController]
           pageController.setViewControllers(startingViewControllers as? [UIViewController], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
       }

       pageViewController = pageController
       addChildViewController(pageViewController!)
       self.view.addSubview(pageViewController!.view)
       pageViewController!.didMoveToParentViewController(self)
   }

   private func getItemController(itemIndex: Int) -> QuizPageItem? {
       if itemIndex < pageInfo.count {
           let CurrentQuizPageItem = self.storyboard!.instantiateViewControllerWithIdentifier("QuizPageItem") as! QuizPageItem
           CurrentQuizPageItem.itemIndex = itemIndex
           CurrentQuizPageItem.thisPageInfo = pageInfo[itemIndex]
           return CurrentQuizPageItem
       }

       return nil
   }

   /*
       PageView Delegates
   */
   func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {

       let CurrentQuizPageItem = viewController as! QuizPageItem

       if CurrentQuizPageItem.itemIndex > 0 {
           return getItemController(CurrentQuizPageItem.itemIndex - 1)
       }

       return nil
   }

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

       let CurrentQuizPageItem = viewController as! QuizPageItem

       if CurrentQuizPageItem.itemIndex + 1 < pageInfo.count {
           return getItemController(CurrentQuizPageItem.itemIndex + 1)
       }

       return nil
   }

我的问题是如何让下一步和后退按钮正常工作?

现在可以通过滑动来更改页面。我不想使用滑动。我想使用下一步和后退按钮进行控制。

更新

这是在 Main.storyboard

PageViewController 是故事板中的中间那个。

PageViewController 的故事板 ID 为 QuizPageViewController

QuizViewController 是情节提要中左边的那个。

QuizViewController 使用情节提要 ID QuizPageViewController

实例化一个 PageViewController

这是由这个块完成的

       let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("QuizPageViewController") as! UIPageViewController
       pageController.dataSource = self

发生此实例化时,它还会为 QuizPageItem 创建首页。

QuizPageItem 是 Main.storyboard 中最右边的视图。

因此,如果您看到这 2 个模型,它们都是 QuizPageItems。

第一个模型的 itemIndex 应该为 0。

第二个模型的 itemIndex 应该为 1。

       let CurrentQuizPageItem = self.storyboard!.instantiateViewControllerWithIdentifier("QuizPageItem") as! QuizPageItem
       CurrentQuizPageItem.itemIndex = itemIndex

我收到的大多数答案都建议通过 QuizViewController 也就是我 Main.storyboard.

中最左边的视图来解决它

但是,按钮位于 QuizPageItem 中,无法通过 QuizViewController 访问。

我想知道如何连接 QuizPageItem 中的 back/next 按钮来执行在 QuizViewController 中控制的分页 假设我在Main.storyboard 正确

同时,我承认目前我在 Main.storyboard 中连接各种视图的方式并不理想。

如果是这样,请告知其他方法。

这是我到达当前位置所遵循的教程。

http://shrikar.com/ios-swift-tutorial-uipageviewcontroller-as-user-onboarding-tool/

更新 2 很抱歉我被认为是在争论。我真的很想学习如何做到这一点。我很确定我缺乏一些基础知识,因此我无法理解 Michael Dautermann 的回答。

我假设 QuizViewController 中有一个函数会触发翻页。

我不确定那个函数是什么。

这些是我知道的功能在按下按钮时触发。

我在 QuizPageItem 中有以下内容 class

 @IBAction func pageBackButton(sender: AnyObject) {

 }

 @IBAction func pageNextButton(sender: AnyObject) {
 }

但是,它们不在 QuizViewController class 中,而是在 QuizPageItem class.

我是否应该将 setViewController 方法放在 QuizPageItem 中的这两个函数中 class?

如果是这样,我如何从 QuizPageItem 内部访问 QuizViewController 实例 class?

更新 3:

我的文件结构是

QuizViewController 控制您看到的 QuizPageItem。 QuizPageItem 表示模型设计的不同视图。

更新4:

matt 的回答对我理解这个我一开始完全不熟悉的 FirstResponder 有很大帮助。

当我尝试实现它时,遇到了这个错误。

我用谷歌搜索了一下,但没有成功。

我一直在触发这个错误。

附件是 QuizViewPageItemController.swift

的代码片段
import UIKit
class QuizPageItemViewController: UIViewController, CheckboxDelegate {

    @IBOutlet weak var pageHeadingLabel: UILabel!
    @IBOutlet weak var pageInstructionLabel: UILabel!
    @IBOutlet weak var pageProgressView: UIProgressView!
    @IBOutlet weak var pageQuestionLabel: UILabel!
    @IBOutlet weak var pageAnswerView: UIView!
    @IBOutlet weak var pageBackButton: UIButton!
    @IBOutlet weak var pageNextButton: UIButton!
    let pageNo: Int 
    let maxPageNo: Int
    let thisPageInfo: [String]

    let interestsList = [
        "Blue Chips", "Small Caps", "Pharmaceuticals", "Agriculture",
        "Telecommunications", "Manufacturing", "Finance", "Banks",
        "Retail", "Travel", "Airlines", "Tourism"]

    init(pageNo: Int, maxPageNo: Int, thisPageInfo: [String]) {
        self.pageNo = pageNo
        self.maxPageNo = maxPageNo
        self.thisPageInfo = thisPageInfo

        super.init(nibName: nil, bundle: nil)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.pageBackButton.hidden = pageNo == 0
        self.pageNextButton.hidden = pageNo == maxPageNo
        pageHeadingLabel.text = thisPageInfo[0]
        pageInstructionLabel.text = thisPageInfo[1]
        pageQuestionLabel.text = thisPageInfo[2]

        if thisPageInfo[0] == "Welcome" {
            createCheckboxes()
            pageProgressView.setProgress(0.33, animated: true)
        } else if thisPageInfo[0] == "Awesome!" {
            createSlider()
            pageProgressView.setProgress(0.67, animated: true)
        } else if thisPageInfo[0] == "Almost there..." {
            createSlider()
            pageProgressView.setProgress(0.95, animated: true)
        }
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func createCheckboxes() {
        let fWidth = (self.view.frame.width - 40) / 2
        var frame = CGRectMake(0, 0, fWidth, 40)
        var contentSize = CGRectMake(0, 0, self.view.frame.width - 40, 0)

        for (var counter = 0; counter < interestsList.count; counter++) {
            let checkbox = Checkbox(frame: frame, title: interestsList[counter], selected: false)
            checkbox.mDelegate = self
            checkbox.tag = counter
            checkbox.backgroundColor = UIColor.redColor()

            if counter % 2 == 0 {
                frame.origin.x += fWidth
            } else{
                frame.origin.x -= fWidth
                frame.origin.y += frame.size.height
                contentSize.size.height += frame.size.height
            }

            checkbox.titleLabel?.adjustsFontSizeToFitWidth = true
            pageAnswerView.addSubview(checkbox)
        }
    }

    func didSelectCheckbox(state: Bool, identifier: Int, title: String) {
        print("checkbox '\(title)' has state \(state)")
    }

    func createSlider() {
        let slider = UISlider(frame:CGRectMake(0, 20, self.view.frame.width - 40, 20))
        slider.minimumValue = 0
        slider.maximumValue = 10
        slider.continuous = true
        slider.tintColor = ChatQColours().chatQBlue
        slider.value = 5
        //        slider.addTarget(self, action: "sliderValueDidChange:", forControlEvents: .ValueChanged)
        pageAnswerView.addSubview(slider)

        let leftlabel = UILabel(frame: CGRectMake(0, 40, 0, 0))
        leftlabel.text = "Strongly Avoid"
        leftlabel.sizeToFit()
        pageAnswerView.addSubview(leftlabel)

        let rightlabel = UILabel(frame: CGRectMake(0, 40, 0, 0))
        rightlabel.text = "Strongly Prefer"
        rightlabel.sizeToFit()
        rightlabel.frame.origin.x = slider.frame.width - rightlabel.frame.width
        pageAnswerView.addSubview(rightlabel)
    }
}

您可以使用 setViewControllers:direction:animated:completion: to turn your pages programatically

只需传递上一页或下一页的视图控制器,您就应该准备就绪。

您可以使用:

下一页

pageViewController.setViewControllers(startingViewControllers,
                                                direction: UIPageViewControllerNavigationDirection.Forward,
                                                animated: true,
                                                completion: nil)

上一页

pageViewController.setViewControllers(startingViewControllers,
                                                direction: UIPageViewControllerNavigationDirection.Reverse,
                                                animated: true,
                                                completion: nil)

你可以使用这个功能:

func movePageToIndex(pageIndex NSInteger)->() {
    var viewControllers = []
    var direction : UIPageViewControllerNavigationDirection! 
    if (pageIndex < currentIndex) {
        direction = UIPageViewControllerNavigationDirection.Reverse
    } else {
        direction = UIPageViewControllerNavigationDirection.Forward
    }
    viewControllers = @[[self.getItemController(pageIndex)]]

    WEAKSELF weakSelf = self;
    self.pageViewController.setViewControllers:viewControllers direction:direction animated:false completion:nil];
}

Michael Dautermann 的回答完全正确,正如这个截屏视频所示:

您看到的是一个包含多个页面的页面视图控制器(已编号以便您可以看到顺序),每个页面都包含一个“下一步”按钮,我反复按“下一步”按钮导航到下一页.

与您的项目一样,我的项目(如上面的截屏视频所示)具有视图控制器层次结构:

  • UITabBarController

  • ViewController

  • UIPageViewController

  • 页面(有一个主视图,标签和按钮是它的子视图)

您的问题的核心似乎不是什么方法导致页面视图控制器导航到其下一页或上一页 - 正如您已经被告知的那样,这只是 setViewControllers:... -但是按钮如何向上传达视图控制器层次结构。在我的示例中,这意味着从页面视图内的按钮发送消息,经过页面视图控制器,经过 UIPageViewController,直到 ViewController,然后告诉 UIPageViewController 怎么办。

我可以想出很多方法来做到这一点:

  • 按钮发布通知,ViewController 已注册

  • 按钮发送了一个 nil-targeted 动作,ViewController 有一个处理程序

  • 按钮向标签栏控制器(其tabBarController属性)发送消息,然后发送消息down到其当前选择的视图控制器, ViewController

  • 按钮向其视图控制器发送消息(在nib或故事板中配置为动作),后者向其parentViewController!.parentViewController!发送消息,即ViewController.

我更喜欢哪个?就个人而言,我最喜欢 nil-targeted 动作,因为它不需要额外的代码。唯一的 func pageNextButton() 实现在 ViewController 中。这就是无目标操作的美妙之处:它沿着响应链向上走,寻找接收者,自动。页面视图控制器和 UIPageViewController 在这方面根本没有代码。

我比 parentViewController!.parentViewController! 更喜欢这个,因为后者最终要求页面视图控制器知道它可以调用的 ViewController 中的方法的名称,并且它必须向下转换到 ViewController —— 在我看来,这不是很便携,并且给了页面视图控制器太多关于其环境的知识。另一方面,对于无目标操作,发送者完全不知道实际目标是谁!所以我最喜欢无目标动作,其次是通知。

Swift 3.0

这里有非常简单的解决方案

要从 UIViewController 更改页面 (UIViewController),首先获取 Parent ViewController 的实例(这将是 UIPageViewController)然后设置当前 ViewController

在我的例子中,我有一个名为 "UserDetailsPVC" 的 UIPageViewController,它包含 4 个页面 (UIViewControllers),如下所示

  PageOneVC:UIViewController
  PageTwoVC:UIViewController
  PageThreeVC:UIViewController
  PageFourVC:UIViewController

在 UIPageViewController 中定义一个页面数组

 var pages:[UIViewController] = [
        UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PageOneVC"),
        UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PageTwoVC"),
        UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PageThreeVC"),
        UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PageFourVC")
]

现在可以从任何 UIViewController

更改 UIPageViewController 的页面
    // get parent view controller
    let parentVC = self.parent as! UserDetailsPVC

    // change page of PageViewController
    parentVC.setViewControllers([parentVC.pages[1]], direction: .forward, animated: true, completion: nil)

Swift 4.2 实现您想要的视图控制器的非常简单的方法

//MARK:- Programatically move to Next Page or Previous Page
//Your Desire index in Your UIViewController Array
    let arr : [UIViewController] = [arrvc[1]] 
    pagecontroller.setViewControllers(arr,
                                    direction: UIPageViewController.NavigationDirection.reverse,
                                    animated: true,
                                    completion: nil)