当前位置: 首页 > 面试题库 >

Swift iOS: how to trigger next page using buttons

霍财
2023-03-14
问题内容

enter image description
here

enter image description
here

I have a QuizViewController which extends UIViewController ,
UIPageControllerDelegate, and a UIPageViewControllerDataSource.

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

My question is how do I get the next and back buttons to work properly?

Right now the pages can be changed via swipe. I don’t want to use swipe. I
want to control using the next and back buttons.

UPDATE

enter image descript<a href=ion here" src="https://imgs.xnip.cn/cj/l/93/1d71f5b4-a2f1-4026-9580-1dc3ff8f3d41.png" />

This is on the Main.storyboard

PageViewController is the middle one in the storyboard.

PageViewController has the storyboard id as QuizPageViewController.

QuizViewController is the left one in the storyboard.

QuizViewController instantiates a PageViewController using the storyboard id
QuizPageViewController

which is done by this block

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

When this instantiation happens, it also creates the front page for the
QuizPageItem.

QuizPageItem is the right most view in the Main.storyboard.

So if you see the 2 mockups, they are both QuizPageItems.

The first mockup should have a itemIndex of 0.

The second mockup should have itemIndex of 1.

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

Most of the answers I have received suggests solving it via the
QuizViewController aka the left most view in my Main.storyboard.

However, the buttons are in the QuizPageItem and not accessible via the
QuizViewController.

I want to know how I can connect the back/next buttons in the QuizPageItem to
execute the pagination which is controlled in the QuizViewController assuming
the way I am wiring up the various views in the Main.storyboard is correct

At the same time, I allow the possibility that the current way I am wiring up
the various views in the Main.storyboard is not ideal.

If so, please advise an alternative way.

This is the tutorial I follow to get to where I am currently at.

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

UPDATE 2 I apologise that I am seen as arguing. I genuinely want to learn
how to do this. I am pretty sure I am lacking some fundamental knowledge hence
I am unable to understand Michael Dautermann’s answer.

I assume there is a function in the QuizViewController that will trigger the
page turning.

I am not sure what that function is.

These are the functions that I know will get triggered when the buttons are
pressed.

I have the following inside the QuizPageItem class

 @IBAction func pageBackButton(sender: AnyObject) {

 }

 @IBAction func pageNextButton(sender: AnyObject) {
 }

However, they are not in the QuizViewController class but in the QuizPageItem
class.

Am I supposed to put the setViewController method in these two functions
inside QuizPageItem class?

And if so, how do I even access the QuizViewController instance from inside
the QuizPageItem class?

UPDATE 3:

My file structure is

  • QuizViewController.swift
  • QuizPageItem.swift

The QuizViewController controls which QuizPageItem you see. A QuizPageItem
represents a different view as designed by the mockup.

UPDATE4:

matt’s answer helped me a lot with understanding this FirstResponder which I
was totally unfamiliar with in the first place.

When I tried to implement it, I was faced with this error.

enter image description
here

I have googled around and I have tried to remedy it to no avail.

I kept triggering this error.

Attached is the code snippet for the 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)
    }
}

问题答案:

Michael Dautermann’s answer is perfectly correct, as this screencast shows:

enter image description
here

What you’re seeing is a page view controller with multiple pages (numbered so
you can see the order), each page containing a Next button, and I’m repeatedly
pressing the Next button to navigate to the next page.

Like yours, my project, illustrated in the screencast above, has a view
controller hierarchy:

  • UITabBarController

  • ViewController

  • UIPageViewController

  • Page (which has a main view, and the label and buttons are subviews of that)

It appears that the heart of your question is not so much what method causes a
page view controller to navigate to its next or previous page — that, as you
have already been told, is simply setViewControllers:... — but how the
button communicates up the view controller hierarchy. In my example, that
means sending a message from the button inside Page’s view, past the Page view
controller, past the UIPageViewController, and up to the ViewController, which
then tells th UIPageViewController what to do.

I can think of numerous ways to do that:

  • The button posts a notification for which the ViewController is registered

  • The button sends a nil-targeted action for which the ViewController has a handler

  • The button sends a message to the tab bar controller (its tabBarController property), which then sends a message down to its currently selected view controller, the ViewController

  • The button sends a message to its view controller (configured in the nib or storyboard as an action), which sends a message to its parentViewController!.parentViewController!, which is the ViewController.

Which do I prefer? Personally, I like the nil-targeted action best, because it
requires no extra code. The only func pageNextButton() implementation is in
ViewController. That is beauty of a nil-targeted action: it walks up the
responder chain, looking for a recipient, automatically. The Page view
controller and the UIPageViewController have no code at all in this regard.

I like this much better than parentViewController!.parentViewController!,
because the latter requires ultimately that the Page view controller knows the
name of a method in the ViewController that it can call, and it must cast down
to a ViewController — which is not very portable and gives the Page view
controller too much knowledge about its environment, in my opinion. With a
nil-targeted action, on the other hand, the sender is totally agnostic about
who the actual target will turn out to be! So I like nil-target action best,
and notification second best.



 类似资料:

相关阅读

相关文章

相关问答