How do I make the bottom bar with dots of a UIPageViewController translucent?

99封情书 提交于 2019-11-27 17:03:29
Alex R. R.

It is very easy to make it work. You just only have to make the pageviewcontroller taller, and place a PageControl into the XIB file. The trick is put the PageControl in the foreground (and all the other common controls) at the beginning, and update the content of the PageControl with the PageViewController. Here is the code:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.

    self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];

    self.pageController.dataSource = self;
    // We need to cover all the control by making the frame taller (+ 37)
    [[self.pageController view] setFrame:CGRectMake(0, 0, [[self view] bounds].size.width, [[self view] bounds].size.height + 37)];

    TutorialPageViewController *initialViewController = [self viewControllerAtIndex:0];

    NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];

    [self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];

    [self addChildViewController:self.pageController];
    [[self view] addSubview:[self.pageController view]];
    [self.pageController didMoveToParentViewController:self];

    // Bring the common controls to the foreground (they were hidden since the frame is taller)
    [self.view bringSubviewToFront:self.pcDots];
    [self.view bringSubviewToFront:self.btnSkip];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {

    NSUInteger index = [(TutorialPageViewController *)viewController index];

    [self.pcDots setCurrentPage:index];

    if (index == 0) {
        return nil;
    }

    index--;

    return [self viewControllerAtIndex:index];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {

    NSUInteger index = [(TutorialPageViewController *)viewController index];

    [self.pcDots setCurrentPage:index];

    index++;

    if (index == 3) {
        return nil;
    }

    return [self viewControllerAtIndex:index];
}

- (TutorialPageViewController *)viewControllerAtIndex:(NSUInteger)index {

    TutorialPageViewController *childViewController = [[TutorialPageViewController alloc] initWithNibName:@"TutorialPageViewController" bundle:nil];
    childViewController.index = index;

    return childViewController;
}

- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
    // The number of items reflected in the page indicator.
    NSInteger tutorialSteps = 3;
    [self.pcDots setNumberOfPages:tutorialSteps];

    return tutorialSteps;
}

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
    // The selected item reflected in the page indicator.
    return 0;
}

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;
    // Confirm that the view has the exact expected structure.
    // If you add any custom subviews, you will want to remove this check.
    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 and such.

Swift 3 snippet

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if let scrollView = view.subviews.filter({ $0 is UIScrollView }).first,
        let pageControl = view.subviews.filter({ $0 is UIPageControl }).first {
        scrollView.frame = view.bounds
        view.bringSubview(toFront:pageControl)
    }
}

Here's a conversion of Zerotool's solution into Swift 2.1, though there's probably a more elegant way to write it:

override func viewDidLayoutSubviews() {
    var scrollView: UIScrollView?
    var pageControl: UIPageControl?

    // If you add any custom subviews, you will want to remove this check.
    if (self.view.subviews.count == 2) {
        for view in self.view.subviews {
            if (view.isKindOfClass(UIScrollView)) {
                scrollView = view as? UIScrollView
            } else if (view.isKindOfClass(UIPageControl)) {
                pageControl = view as? UIPageControl
            }
        }
    }

    if let scrollView = scrollView {
        if let pageControl = pageControl {
            scrollView.frame = self.view.bounds
            self.view.bringSubviewToFront(pageControl)
        }
    }

    super.viewDidLayoutSubviews()
}

I don't think you can change the behavior of UIPageViewController, so it seems likely that the Path app uses its own view controller. You can do the same: create your own container view controller that uses a UIPageControl to indicate the current page.

You can simply adjust the alpha of the UIPageViewController's UIPageControl.

First, you should retrieve it from the UIPageViewController like so:

- (UIPageControl *)getPageControlForPageViewController:(UIPageViewController *)pageViewController {
      for (UIView *subview in self.pageViewController.view.subviews) {
        if ([subview isKindOfClass:[UIPageControl class]]) {
          return (UIPageControl *) subview;
        }
      }

      return nil;
    }

Next, make use of the function. I've made a property on my ViewController called childPageControl. Give it the UIPageViewController's UIPageControl:

self.childPageControl = [self getPageControlForPageViewController:self.pageViewController];

Next, you can adjust the alpha to give a translucent effect:

self.childPageControl.alpha = .5;

You're very limited in what you can do to affect the UIPageViewController's UIPageControl, but you can at least achieve this with little effort.

Small hack I found today..

Please see the code below.

self.pageController.dataSource = self;
    CGRect rect = [self.view bounds];
    rect.size.height+=37;
    [[self.pageController view] setFrame:rect];
        NSArray *subviews = self.pageController.view.subviews;
        UIPageControl *thisControl = nil;
        for (int i=0; i<[subviews count]; i++) {
            if ([[subviews objectAtIndex:i] isKindOfClass:[UIPageControl class]]) {
                thisControl = (UIPageControl *)[subviews objectAtIndex:i];
            }
        }

        UIView *tempview = [[UIView alloc] initWithFrame:CGRectMake(0, -30, 320, 40)];
        [tempview addSubview:thisControl];
        thisControl.pageIndicatorTintColor = [UIColor lightGrayColor];
        thisControl.currentPageIndicatorTintColor = [UIColor greenColor];
        [self.view addSubview:tempview];

I wanted to do a similar effect in the app I was working on - I used a UIPageViewController with a separate UIPageControl.

This lets you place the UIPageControl anywhere you'd like in the view, including over the top of the UIPageViewController, and you keep its active page dot up to date via the UIPageViewController delegate method:

- (void)pageViewController:(UIPageViewController *)pageViewController
        didFinishAnimating:(BOOL)finished
   previousViewControllers:(NSArray<UIViewController *> *)previousViewControllers
       transitionCompleted:(BOOL)completed {
    if (completed) {
        self.pageControl.currentPage = [self.pageViewControllers indexOfObject:pageViewController.viewControllers.firstObject];
    }
}

No need to traverse the subview hierarchy trying to find the internal UIPageViewController page control, nor having to resize the contents of the internal scrollview.

Hope this helps.

Mohsin Qureshi

this code is in Swift Add following in your UIPageViewController

override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

    for view in self.view.subviews {
        if view.isKindOfClass(UIScrollView) {
            view.frame = UIScreen.mainScreen().bounds
        } else if view.isKindOfClass(UIPageControl) {
            view.backgroundColor = UIColor.clearColor()
        }
    }
}

I solve using this code:

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.


self.namesImage = @[@"page1.png", @"page2.png", @"page3.png", @"page4.png"];

self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"PageViewController"];
self.pageViewController.dataSource = self;


TutorialContentViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = @[startingViewController];

[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];


[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];

[[UIPageControl appearance] setPageIndicatorTintColor:[UIColor grayColor]];
[[UIPageControl appearance] setCurrentPageIndicatorTintColor:[UIColor whiteColor]];
[[UIPageControl appearance] setBackgroundColor: [[UIColor blackColor] colorWithAlphaComponent:0.1f]];
[[UIPageControl appearance] setOpaque:YES];

}
MessuKilkain

I found an other workarround that fits me better.

I reuse the code given by zerotool to get the UIPageControl (var called pageControl) and the UIScrollView (var called pageView) used by the UIPageViewController.

Once that done in the viewDidLoad, I just prevent clip subview of pageView and let the content spread more to be beneath the UIPageControl.

The pageControl is beneath the pageView so we have to manually make it come in front.

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    if(
       [[[self view] subviews] count] == 2
       )
    {
        UIScrollView* pageView = nil;
        UIPageControl* pageControl = nil;

        UIView* selfView = self.view;
        NSArray* subviews = selfView.subviews;

        for( NSInteger i = 0 ; i < subviews.count && ( pageView == nil || pageControl == nil ) ; i++ )
        {
            UIView* t = subviews[i];
            if( [t isKindOfClass:[UIScrollView class]] )
            {
                pageView = (UIScrollView*)t;
            }
            else if( [t isKindOfClass:[UIPageControl class]] )
            {
                pageControl = (UIPageControl*)t;
            }
        }

        if( pageView != nil && pageControl != nil )
        {
            [pageView setClipsToBounds:NO];
            [selfView bringSubviewToFront:pageControl];
        }
    }
}

Once I get my pageView covering the space occupied by the pageControl but under the pageControl, I just have to adjust the nib file use for each viewController displayed as page :

  • base view should not clip
  • the first and only subview :
    • should have constraint to set bottom to -37 (or more if you need but 37 is the size of the pageControl) from bottom of superview
    • should clip content
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!