web-dev-qa-db-fra.com

Comment placer l'élément UIPageControl au-dessus des pages glissantes d'un UIPageViewController?

Concernant ce tutorial par AppCoda sur la mise en oeuvre d’une application avec UIPageViewController , je voudrais utiliser un élément de contrôle de page personnalisé en haut des pages plutôt qu’en bas. 

Lorsque je viens de placer un élément de contrôle de page au-dessus des vues uniques qui seront affichées, le résultat logique est que les éléments de contrôle défilent avec la vue de page éloignée de l'écran. 

Comment est-il possible de placer l'élément de contrôle au-dessus des vues pour que les vues de page soient en plein écran (comme avec une image) afin que l'utilisateur puisse voir les vues sous l'élément de contrôle fixe?

J'ai joint un exemple de capture d'écran - crédits à AppCoda et Path :

enter image description here

58
sn3ek

Après des recherches plus approfondies et une recherche j'ai trouvé une solution , également sur stackoverflow.

La clé est le message suivant à envoyer à un élément UIPageControl personnalisé:

[self.view bringSubviewToFront:self.pageControl];

Le didacticiel AppCoda constitue le fondement de cette solution :

Ajoutez un élément UIPageControl au-dessus de RootViewController - le contrôleur de vue avec la flèche.

Créez un élément IBOutlet associé dans votre ViewController.m

Dans la méthode viewDidLoad, vous devez ensuite ajouter le code suivant en tant que dernière méthode appelée après l'ajout de toutes les sous-vues.

[self.view bringSubviewToFront:self.pageControl];

Pour affecter la page actuelle en fonction du pageIndex de la vue de contenu actuelle, vous pouvez ajouter ce qui suit aux méthodes UIPageViewControllerDataSource:

- (UIPageViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    // ...
    index--;
    [self.pageControl setCurrentPage:index];

    return [self viewControllerAtIndex:index];
}

- (UIPageViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    // ...
    index++;
    [self.pageControl setCurrentPage:index];

    // ...
    return [self viewControllerAtIndex:index];
}
50
sn3ek

Je n'avais pas le représentant pour commenter la réponse à l'origine de cela, mais je l'aime vraiment. J'ai amélioré le code et l'ai converti en Swift pour la sous-classe ci-dessous de UIPageViewController:

class UIPageViewControllerWithOverlayIndicator: UIPageViewController {
    override func viewDidLayoutSubviews() {
        for subView in self.view.subviews as! [UIView] {
            if subView is UIScrollView {
                subView.frame = self.view.bounds
            } else if subView is UIPageControl {
                self.view.bringSubviewToFront(subView)
            }
        }
        super.viewDidLayoutSubviews()
    }
}

Propre et ça marche bien. Nul besoin de maintenir quoi que ce soit, faites simplement de votre contrôleur de vue de page une instance de cette classe dans le storyboard ou faites que votre classe de contrôleur de vue de page personnalisée hérite de cette classe.

50
Eric F.

sn3ek Votre réponse m'a permis de faire presque tout le chemin. Je n'ai cependant pas défini la page actuelle à l'aide des méthodes viewControllerCreation.

J'ai fait mon ViewController also le délégué de la UIPageViewController. Ensuite, je règle PageControl 's CurrentPage dans cette méthode. En utilisant la pageIndex maintenue, je suis la ContentViewController mentionnée dans l'article d'origine.

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
  APPChildViewController *currentViewController = pageViewController.viewControllers[0];
  [self.pageControl setCurrentPage:currentViewController.pageIndex];
}

n'oubliez pas d'ajouter ceci à viewDidLoad

self.pageViewController.delegate = self;

Pour faire suite au commentaire de PropellerHead, l’interface de la ViewController aura la forme suivante:

@interface ViewController : UIViewController <UIPageViewControllerDataSource, UIPageViewControllerDelegate>
27
Coderdad

Le même effet peut être obtenu simplement en sous-classant UIPageViewController et en redéfinissant viewDidLayoutSubviews comme suit:

-(void)viewDidLayoutSubviews {
    UIView* v = self.view;
    NSArray* subviews = v.subviews;
    if( [subviews count] == 2 ) {
        UIScrollView* sv = nil;
        UIPageControl* pc = nil;
        for( UIView* t in subviews ) {
            if( [t isKindOfClass:[UIScrollView class]] ) {
                sv = (UIScrollView*)t;
            } else if( [t isKindOfClass:[UIPageControl class]] ) {
                pc = (UIPageControl*)t;
            }
        }
        if( sv != nil && pc != nil ) {
            // expand scroll view to fit entire view
            sv.frame = v.bounds;
            // put page control in front
            [v bringSubviewToFront:pc];
        }
    }
    [super viewDidLayoutSubviews];
}

Il n’est alors pas nécessaire de maintenir un UIPageControl séparé, etc.

3
zeroimpl

Voici une réponse RxSwift/RxCocoa que j’ai assemblée après avoir jeté un œil aux autres réponses.

let pages = Variable<[UIViewController]>([])
let currentPageIndex = Variable<Int>(0)
let pendingPageIndex = Variable<(Int, Bool)>(0, false)

let db = DisposeBag()

override func viewDidLoad() {
    super.viewDidLoad()
    pendingPageIndex
        .asDriver()
        .filter { $0.1 }
        .map { $0.0 }
        .drive(currentPageIndex)
        .addDisposableTo(db)

    currentPageIndex.asDriver()
        .drive(pageControl.rx.currentPage)
        .addDisposableTo(db)
}

func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
    if let index = pages.value.index(of: pendingViewControllers.first!) {
        pendingPageIndex.value = (index, false)
    }
}

func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
    pendingPageIndex.value = (pendingPageIndex.value.0, completed)
}
2
jacks205

Vous devez implémenter une UIPageControl personnalisée et l'ajouter à la vue. Comme d'autres l'ont mentionné, view.bringSubviewToFront(pageControl) doit être appelé.

J'ai un exemple d'un contrôleur de vue avec tout le code pour configurer une UIPageControl personnalisée (dans le storyboard) avec UIPageViewController

Vous devez mettre en œuvre deux méthodes pour définir le code de la page en cours.

func pageViewController(pageViewController: UIPageViewController, willTransitionToViewControllers pendingViewControllers: [UIViewController]) {
    pendingIndex = pages.indexOf(pendingViewControllers.first!)
}

func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
    if completed {
        currentIndex = pendingIndex
        if let index = currentIndex {
            pageControl.currentPage = index
        }
    }
}
1
samwize

Voici la réponse de Swifty 2 qui s'appuie beaucoup sur la réponse de @ zerotool ci-dessus. Il suffit de sous-classer UIPageViewController, puis d’ajouter ce remplacement pour rechercher le scrollview et le redimensionner. Ensuite, prenez le contrôle de page et déplacez-le en haut de tout le reste. Vous devez également définir la couleur d’arrière-plan des contrôles de page pour la supprimer. Ces deux dernières lignes pourraient aller dans votre délégué d'application.

override func viewDidLayoutSubviews() {

    super.viewDidLayoutSubviews()

    var sv:UIScrollView?
    var pc:UIPageControl?

    for v in self.view.subviews{

        if v.isKindOfClass(UIScrollView) {

            sv = v as? UIScrollView

        }else if v.isKindOfClass(UIPageControl) {

            pc = v as? UIPageControl
        }
    }

    if let newSv = sv {

        newSv.frame = self.view.bounds
    }

    if let newPc = pc {

        self.view.bringSubviewToFront(newPc)
    }
}

let pageControlAppearance = UIPageControl.appearance()
pageControlAppearance.backgroundColor = UIColor.clearColor()

btw - Je ne remarque pas de boucles infinies, comme mentionné ci-dessus. 

0
Lee Probert