web-dev-qa-db-fra.com

Swift iOS: comment déclencher la page suivante à l'aide de boutons

 enter image description here

 enter image description here

J'ai un QuizViewController qui étend UIViewController , UIPageControllerDelegate et une UIPageViewControllerDataSource.

À l'intérieur 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
   }

Ma question est comment puis-je obtenir les boutons suivant et arrière pour fonctionner correctement?

À l'heure actuelle, les pages peuvent être modifiées par balayage. Je ne veux pas utiliser le glissement. Je veux contrôler en utilisant les boutons suivant et précédent.

UPDATE

 enter image description here

C'est sur le tableau principal

PageViewController est celui du milieu dans le storyboard.

PageViewController a l'ID de storyboard sous la forme QuizPageViewController .

QuizViewController est celui de gauche dans le storyboard.

QuizViewController instancie un PageViewController à l'aide de l'ID de storyboard QuizPageViewController

qui est fait par ce bloc

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

Lorsque cette instanciation se produit, elle crée également la page de couverture pour QuizPageItem.

QuizPageItem est la vue la plus à droite du tableau principal.

Donc, si vous voyez les 2 maquettes, il s’agit de QuizPageItems.

La première maquette doit avoir un itemIndex de 0.

La deuxième maquette devrait avoir itemIndex de 1.

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

La plupart des réponses que j'ai reçues suggèrent de résoudre le problème via la vue QuizViewController aka la vue la plus à gauche de mon tableau principal.

Cependant, les boutons se trouvent dans QuizPageItem et ne sont pas accessibles via QuizViewController.

Je souhaite savoir comment connecter les boutons Précédent/Suivant dans QuizPageItem pour exécuter la pagination contrôlée dans QuizViewController en supposant que les différentes vues de Main.storyboard soient câblées de manière appropriée

En même temps, j'autorise la possibilité que la manière actuelle de câbler les différentes vues dans le tableau principal ne soit pas idéale.

Si tel est le cas, veuillez indiquer un autre moyen.

C’est le tutoriel que je suis pour arriver là où je suis actuellement.

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

UPDATE 2 Je m'excuse de ce que je suis considéré comme argumentant. Je veux vraiment apprendre à faire cela. Je suis à peu près sûr que je manque de connaissances fondamentales, donc je suis incapable de comprendre la réponse de Michael Dautermann.

Je suppose qu'il existe une fonction dans QuizViewController qui déclenchera le changement de page.

Je ne suis pas sûr de ce que cette fonction est.

Celles-ci sont les fonctions que je sais que va se déclencher lorsque vous appuyez sur les boutons.

J'ai les éléments suivants dans la classe QuizPageItem

 @IBAction func pageBackButton(sender: AnyObject) {

 }

 @IBAction func pageNextButton(sender: AnyObject) {
 }

Cependant, ils ne font pas partie de la classe QuizViewController, mais de la classe QuizPageItem.

Suis-je censé mettre la méthode setViewController dans ces deux fonctions dans la classe QuizPageItem?

Et si oui, comment puis-je même accéder à l'instance QuizViewController à partir de la classe QuizPageItem?

UPDATE 3:

Ma structure de fichier est 

  • QuizViewController.Swift
  • QuizPageItem.Swift

Le QuizViewController contrôle le QuizPageItem que vous voyez. Un QuizPageItem représente une vue différente telle que conçue par la maquette.

UPDATE4:

la réponse de matt m'a beaucoup aidé à comprendre ce FirstResponder avec lequel je ne connaissais absolument rien du tout.

Lorsque j'ai essayé de l'implémenter, j'ai été confronté à cette erreur.

 enter image description here

J'ai cherché sur Google et j'ai essayé d'y remédier en vain.

J'ai continué à déclencher cette erreur.

Ci-joint l'extrait de code pour le 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)
    }
}
6
Kim Stacks

La réponse de Michael Dautermann est tout à fait correcte, comme le montre ce screencast:

 enter image description here

Ce que vous voyez est un contrôleur de visualisation de page avec plusieurs pages (numérotées pour que vous puissiez voir la commande), chaque page contenant un bouton Suivant. J'appuie plusieurs fois sur le bouton Suivant pour passer à la page suivante.

Comme le vôtre, mon projet, illustré dans la capture d'écran ci-dessus, a une hiérarchie de contrôleur de vue:

  • UITabBarController

  • ViewController

  • UIPageViewController

  • Page (qui a une vue principale, et l'étiquette et les boutons sont des sous-vues de celle-ci)

Il semble que le cœur de votre question ne soit pas tant sur la méthode qui oblige un contrôleur de vue de page à accéder à sa page suivante ou précédente - cela, comme on vous l'a déjà dit, est simplement setViewControllers:... - mais à la façon dont le bouton communique avec le contrôleur de vue hiérarchie. Dans mon exemple, cela signifie qu’il faut envoyer un message à partir du bouton situé dans la vue Page, au-delà du contrôleur de vue de Page, du contrôle UIPageViewController et jusqu’au ViewController, qui indique ensuite à UIPageViewController quoi faire.

Je peux penser à de nombreuses façons de le faire:

  • Le bouton publie une notification pour laquelle ViewController est enregistré.

  • Le bouton envoie une action non ciblée pour laquelle ViewController a un gestionnaire.

  • Le bouton envoie un message au contrôleur de la barre d’onglet (propriété tabBarController), qui envoie ensuite un message down à son contrôleur de vue actuellement sélectionné, le ViewController.

  • Le bouton envoie un message à son contrôleur de vue (configuré dans le nib ou le storyboard en tant qu'action), qui envoie un message à son parentViewController!.parentViewController!, qui est ViewController.

Lequel est-ce que je préfère? Personnellement, ce qui me plaît le plus, c’est l’action ciblée sur zéro, car elle ne nécessite aucun code supplémentaire. La seule implémentation de func pageNextButton() est dans ViewController. C'est la beauté d'une action non ciblée: elle parcourt la chaîne de répondeurs, recherchant un destinataire, automatiquement . Le contrôleur de vue de page et le UIPageViewController n'ont aucun code à cet égard.

J'aime beaucoup mieux cela que parentViewController!.parentViewController!, car ce dernier exige en fin de compte que le contrôleur de vue de page connaisse le nom d'une méthode dans ViewController qu'il peut appeler, et il doit afficher un ViewController - qui n'est pas très portable et donne la page. Voir contrôleur trop de connaissances sur son environnement, à mon avis. En revanche, avec une action sans cible, l'expéditeur est totalement agnostique quant à savoir qui sera la cible réelle! Je préfère donc les actions nulles et ciblées, ainsi que les notifications.

16
matt

Swift 3.0

La solution très simple est ici

Pour changer de page (UIViewController) à partir d'un UIViewController, commencez par obtenir l'instance de ViewController parent (qui sera UIPageViewController), puis définissez son ViewController actuel.

Dans mon cas, j'ai un UIPageViewController nommé "UserDetailsPVC" et il contient 4 pages (UIViewControllers) qui sont comme suit

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

Dans le UIPageViewController permet de définir un tableau de pages

 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")
]

Maintenant, pour changer la page de UIPageViewController à partir de l’un des UIViewController

    // 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)
11
Rajat Jain

Vous pouvez utiliser setViewControllers:direction:animated:completion: to tourner vos pages par programmation

Passez simplement le contrôleur de vue de la page précédente ou suivante et vous devriez être prêt.

7
Michael Dautermann

Vous pouvez utiliser:

Pour la page suivante 

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

Pour la page précédente

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

Vous pouvez utiliser cette fonction:

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];
}
1
vien vu

Swift 4.2 Un moyen très facile de réaliser vos souhaits View Controller

//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)
0
Shakeel Ahmed