How to put the UIPageControl element on top of the sliding pages within a UIPageViewController?

后端 未结 7 1694
闹比i
闹比i 2020-12-02 10:20

Regarding to this tutorial by AppCoda about how to implement a app with UIPageViewController I\'d like to use a custom page control element on top of the pages instead of at

相关标签:
7条回答
  • 2020-12-02 10:46

    You have to implement a custom UIPageControl and add it to the view. As others have mentioned, view.bringSubviewToFront(pageControl) must be called.

    I have an example of a view controller with all the code on setting up a custom UIPageControl (in storyboard) with UIPageViewController

    There are 2 methods which you need to implement to set the current page indicator.

    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
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-02 10:53

    Here is a RxSwift/RxCocoa answer I put together after looking at the other replies.

    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)
    }
    
    0 讨论(0)
  • 2020-12-02 10:54

    I didn't have the rep to comment on the answer that originated this, but I really like it. I improved the code and converted it to swift for the below subclass of 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()
        }
    }
    

    Clean and it works well. No need to maintain anything, just make your page view controller an instance of this class in storyboard, or make your custom page view controller class inherit from this class instead.

    0 讨论(0)
  • 2020-12-02 11:02

    Here's the Swifty 2 answer very much based on @zerotool's answer above. Just subclass UIPageViewController and then add this override to find the scrollview and resize it. Then grab the page control and move it to the top of everything else. You also need to set the page controls background color to clear. Those last two lines could go in your app delegate.

    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 - I'm not noticing any infinite loops, as mentioned above.

    0 讨论(0)
  • 2020-12-02 11:04

    sn3ek Your answer got me most of the way there. I didn't set the current page using the viewControllerCreation methods though.

    I made my ViewController also the delegate of the UIPageViewController. Then I set the PageControl's CurrentPage in that method. Using the pageIndex maintained I'm the ContentViewController mention in the original article.

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

    don't forget to add this to viewDidLoad

    self.pageViewController.delegate = self;
    

    To follow up on PropellerHead's comment the interface for the ViewController will have the form

    @interface ViewController : UIViewController <UIPageViewControllerDataSource, UIPageViewControllerDelegate>
    
    0 讨论(0)
  • 2020-12-02 11:07

    The same effect can be achieved simply by subclassing UIPageViewController and overriding viewDidLayoutSubviews as follows:

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

    Then there is no need to maintain a seperate UIPageControl and such.

    0 讨论(0)
提交回复
热议问题