【问题标题】:How to put the UIPageControl element on top of the sliding pages within a UIPageViewController?如何将 UIPageControl 元素放在 UIPageViewController 内的滑动页面之上?
【发布时间】:2014-01-29 12:05:21
【问题描述】:

关于这个tutorial by AppCoda about how to implement a app with UIPageViewController,我想在页面顶部而不是底部使用自定义页面控制元素。

当我只是将页面控件元素放在将要显示的单个视图的顶部时,逻辑结果是控件元素随着页面视图远离屏幕滚动。

如何将控制元素放在视图顶部,使页面视图全屏显示(就像使用图像一样),以便用户可以看到固定控制元素下方的视图?

我附上了一个示例截图 - 归功于AppCoda and Path

【问题讨论】:

    标签: ios storyboard uipageviewcontroller uipagecontrol


    【解决方案1】:

    我没有代表评论由此产生的答案,但我真的很喜欢。我改进了代码并将其转换为 swift 用于 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()
        }
    }
    

    干净而且效果很好。无需维护任何东西,只需让您的页面视图控制器成为故事板中此类的实例,或者让您的自定义页面视图控制器类继承自此类。

    【讨论】:

    • 当我把这个 for 循环放在 ViewDidLoad 中时,我可以找到 scrollView 但找不到 UIPageControl 视图。我错过了什么吗?
    • @Hakim 也许控制器缺少 UIPageViewController 类型?没有确切情况的例子很难说。
    • 我要为它添加书签只是因为它的多功能性和实用性。非常感谢!
    【解决方案2】:

    经过进一步调查和搜索I found a solution,也在stackoverflow上。

    关键是将以下消息发送到自定义UIPageControl 元素:

    [self.view bringSubviewToFront:self.pageControl];
    

    The AppCoda tutorial is the foundation for this solution:

    RootViewController 顶部添加一个 UIPageControl 元素 - 带有箭头的视图控制器。

    在您的ViewController.m 中创建一个相关的IBOutlet 元素。

    viewDidLoad 方法中,您应该添加以下代码作为添加所有子视图后调用的最后一个方法。

    [self.view bringSubviewToFront:self.pageControl];
    

    要根据当前内容视图的 pageIndex 分配当前页面,您可以将以下内容添加到 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];
    }
    

    【讨论】:

    • 对我来说奇怪的是,在增加/减少索引之前设置 pageControl 页面有效。我从该方法中得到的 viewController 是 新的视图控制器,而不是转换之前的那个
    • 这一点也不奇怪;这是预期的。这些方法在 current 视图控制器上调用以确定接下来会发生什么。
    • @sn3ek 如何创建相关的 IBOutlet 元素。
    • @elavarasan 哪个元素? self.pageControl 例如是视图上页面控制元素的 IBOutlet...
    • 不建议在数据源方法中处理pageControl。作为数据源,您唯一应该做的就是返回请求的数据(并避免其他逻辑)-您无法控制何时调用这些方法-这取决于实现。例如,在滚动之后,您可能观察到 PageViewController 提前“预取”下一个相邻页面。这完全与您可能潜入的任何检查逻辑相混淆(并且可能解释了为什么@Gokul 得到与@sn3ek 不同的结果)。 (更好:改用委托)
    【解决方案3】:

    sn3ek 你的回答让我大部分时间都在那里。我没有使用viewControllerCreation 方法设置当前页面。

    我让我的ViewController also 成为UIPageViewController 的代表。然后我在那个方法中设置了PageControlCurrentPage。使用pageIndex 维护,我是原始文章中提到的ContentViewController

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

    别忘了将此添加到viewDidLoad

    self.pageViewController.delegate = self;
    

    要跟进 PropellerHead 的评论,ViewController 的界面将具有以下形式

    @interface ViewController : UIViewController <UIPageViewControllerDataSource, UIPageViewControllerDelegate>
    

    【讨论】:

    • 只是为了清楚。 UIPageViewController 的代理是 UIPageViewControllerDelegate,所以你的 ViewController 的界面看起来像这样:@interface ViewController : UIViewController &lt;UIPageViewControllerDataSource, UIPageViewControllerDelegate&gt;
    【解决方案4】:

    通过继承 UIPageViewController 并覆盖 viewDidLayoutSubviews 可以达到相同的效果,如下所示:

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

    那么就不需要单独维护一个UIPageControl之类的了。

    【讨论】:

    • 我其实很喜欢这个答案。它运作良好。直到我戴上运行 iOS 7.1 的 iPhone 4S。在这一点上,它变得无限。不知道为什么,但要小心。
    【解决方案5】:

    您必须实现自定义UIPageControl 并将其添加到视图中。正如其他人所提到的,必须调用view.bringSubviewToFront(pageControl)

    我有一个视图控制器的example,其中包含使用UIPageViewController 设置自定义UIPageControl(在情节提要中)的所有代码

    您需要实现两种方法来设置当前页面指示器。

    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
            }
        }
    }
    

    【讨论】:

      【解决方案6】:

      这是我在查看其他回复后汇总的 RxSwift/RxCocoa 答案。

      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)
      }
      

      【讨论】:

        【解决方案7】:

        这是 Swifty 2 的答案,非常基于上面@zerotool 的答案。只需将 UIPageViewController 子类化,然后添加此覆盖以查找滚动视图并调整其大小。然后抓住页面控件并将其移动到其他所有内容的顶部。您还需要将页面控件的背景颜色设置为清除。最后两行可以放在您的应用委托中。

        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()
        

        顺便说一句-如上所述,我没有注意到任何无限循环。

        【讨论】:

          猜你喜欢
          • 2014-08-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-17
          相关资源
          最近更新 更多