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

Regarding this tutorial from AppCoda on how to implement the application with the UIPageViewController , I would like to use a custom page control on top of the pages, not the bottom.

When I simply place the page control on top of the individual views that will be displayed, the logical result is that the controls scroll to view the page away from the screen.

How can I place a control on top of the views so that the page views are in full screen mode (for example, with an image) so that the user can see the views under a fixed control?

I attached an example screenshot - AppCoda credits and the path :

enter image description here

+56
ios storyboard uipageviewcontroller uipagecontrol
Jan 10 '14 at 13:30
source share
7 answers

After further study and search, I found a solution , also on stackoverflow.

The key is the following message to send to the UIPageControl user element:

 [self.view bringSubviewToFront:self.pageControl]; 

The AppCoda tutorial is the basis for this solution :

Add a UIPageControl element on top of the RootViewController , the arrow view controller.

Create an associated IBOutlet in ViewController.m .

In the viewDidLoad method viewDidLoad you should add the following code as the last method that you call after adding all the subzones.

 [self.view bringSubviewToFront:self.pageControl]; 

To assign the current page based on pageIndex of the current content view, you can add the following UIPageViewControllerDataSource methods:

 - (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]; } 
+49
Jan 10 '14 at 15:50
source share

I had no comments to comment on the answer that arose because of this, but I really like it. I improved the code and converted it to fast 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. You don’t need to maintain anything, just make your pageview controller an instance of this class in the storyboard, or make your own view controller class inherit from this class.

+47
Jan 30 '15 at 18:19
source share

sn3ek Your answer gave me most of the way. I did not set the current page using the viewControllerCreation methods.

I made my ViewController also delegate of the UIPageViewController . Then I set the PageControl CurrentPage in this method. Using pageIndex supported. I mention ContentViewController 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 the PropellerHead comment, the interface for the ViewController will take the form

 @interface ViewController : UIViewController <UIPageViewControllerDataSource, UIPageViewControllerDelegate> 
+27
Apr 15 '14 at 0:26
source share

The same effect can be achieved simply by subclassing the 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 separate UIPageControl, etc.

+2
Jul 20 '14 at 15:23
source share

You need to implement a custom UIPageControl and add it to the view. As others have noted, you need to call view.bringSubviewToFront(pageControl) .

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

There are two methods that you must follow 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 } } } 
+1
Mar 08 '16 at 8:33
source share

Here is the RxSwift / RxCocoa answer that I compiled by looking at the other answers.

 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) } 
+1
Dec 31 '16 at 4:35
source share

Here the answers of Swifty 2 are very much based on @zerotool answer above. Just subclass UIPageViewController, and then add this override to find scrollview and resize it. Then grab the page control and move it to the top of everything else. You also need to set the background color for the page controls. These last two lines can be sent to your application 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 do not notice any infinite loops, as mentioned above.

0
Feb 25 '16 at 13:39
source share



All Articles