Как изменить положение UIPageControl в iOS> = 8.0?

У меня есть простой UIPageViewController, который отображает UIPageControl по умолчанию в нижней части страниц. Интересно, возможно ли изменить положение UIPageControl, например, чтобы быть сверху экрана, а не снизу. Я искал вокруг и только нашел старые дискуссии, которые говорят, что мне нужно создать свой собственный UIPageControl. Является ли это проще с iOS8 и 9? Благодарю.

Ответ 1

Да, для этого вы можете добавить настраиваемый контроллер страниц.

self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 50, self.view.frame.size.width, 50)]; // your position

[self.view addSubview: self.pageControl];

затем удалите

- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController

а также

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController

Затем добавьте еще один метод делегата:

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray<UIViewController *> *)pendingViewControllers
 {
     PageContentViewController *pageContentView = (PageContentViewController*) pendingViewControllers[0];
     self.pageControl.currentPage = pageContentView.pageIndex;
 }

Ответ 2

Ответ Shameerjan очень хорош, но для его работы требуется еще одна вещь, и это реализация другого метода делегата:

func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {

    // If user bailed our early from the gesture, 
    // we have to revert page control to previous position
    if !completed {
         let pageContentView = previousViewControllers[0] as! PageContentViewController;
         self.pageControl.currentPage = pageContentView.pageIndex;
    }
}

Это связано с тем, что, если вы этого не сделаете, если вы слегка перемещаете элемент управления страницей, он вернется к предыдущей позиции, но элемент управления страницы отобразит другую страницу.

Надеюсь, поможет!

Ответ 3

Переопределите viewDidLayoutSubviews() на странице viewcontroller и используйте это

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // get pageControl and scroll view from view subviews
    let pageControl = view.subviews.filter{ $0 is UIPageControl }.first! as! UIPageControl
    let scrollView = view.subviews.filter{ $0 is UIScrollView }.first! as! UIScrollView
    // remove all constraint from view that are tied to pagecontrol
    let const = view.constraints.filter { $0.firstItem as? NSObject == pageControl || $0.secondItem as? NSObject == pageControl }
    view.removeConstraints(const)

    // customize pagecontroll
    pageControl.translatesAutoresizingMaskIntoConstraints = false
    pageControl.addConstraint(pageControl.heightAnchor.constraintEqualToConstant(35))
    pageControl.backgroundColor = view.backgroundColor

    // create constraints for pagecontrol
    let leading = pageControl.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor)
    let trailing = pageControl.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor)
    let bottom = pageControl.bottomAnchor.constraintEqualToAnchor(scrollView.topAnchor, constant:8) // add to scrollview not view

    // pagecontrol constraint to view
    view.addConstraints([leading, trailing, bottom])
    view.bounds.origin.y -= pageControl.bounds.maxY
}

Ответ 4

Просто найдите "PageControl" в подклассе PageViewController и установите фрейм, местоположение или что угодно

override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        for subView in view.subviews {
            if  subView is  UIPageControl {
                subView.frame.origin.y = self.view.frame.size.height - 164
            }
        }
    }

Ответ 5

Да, ответ Shameerjan очень хорош, но вместо добавления другого элемента управления страницей можно использовать индикатор страницы по умолчанию:

- (UIPageControl*)pageControl {
for (UIView* view in self.view.subviews) {
    if ([view isKindOfClass:[UIPageControl class]]) {
        //set new pageControl position
        view.frame = CGRectMake( 100, 200, width, height);
        return (id)view;
    }
}
return nil;
}

а затем увеличьте размер UIPageViewController, чтобы скрыть нижний зазор:

//somewhere in your code
self.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height+40);

Ответ 6

Для быстрого:

self.pageController = UIPageControl(
                                     frame: CGRect(
                                         x: 0,
                                         y: self.view.frame.size.height - 50,
                                         width: self.view.frame.size.width,
                                         height: 50
                                     )
                                   )

self.view.addSubview(pageController)

Помните, что используйте pageController.numberOfPages и делегируйте страницуView

затем удалите

func presentationCountForPageViewController(
    pageViewController: UIPageViewController
) -> Int

а также

func presentationIndexForPageViewController(
    pageViewController: UIPageViewController
) -> Int

Затем добавьте еще один метод делегата:

func pageViewController(
    pageViewController: UIPageViewController,
    willTransitionToViewControllers pendingViewControllers:[UIViewController]){
       if let itemController = pendingViewControllers[0] as? PageContentViewController {
           self.pageController.currentPage = itemController.pageIndex
       }
    }
}

Ответ 7

здесь очень эффективный способ изменить положение стандартного PageControl для PageViewController, не имея необходимости создавать новый...

extension UIPageViewController {
override open func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    for subV in self.view.subviews {
        if type(of: subV).description() == "UIPageControl" {
            let pos = CGPoint(x: subV.frame.origin.x, y: subV.frame.origin.y - 75 * 2)
            subV.frame = CGRect(origin: pos, size: subV.frame.size)
        }
    }
}
}

Ответ 8

Swift 4 версии @Jan ответ с исправленной ошибкой, когда пользователь отменяет переход:

Во-первых, вы создаете собственный pageControl:

let pageControl = UIPageControl()

Вы добавляете его в представление и позиционируете его так, как хотите:

self.view.addSubview(self.pageControl)
self.pageControl.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    self.pageControl.leftAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leftAnchor, constant: 43),
    self.pageControl.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -33)
])

Затем вам нужно инициализировать pageControl:

self.pageControl.numberOfPages = self.dataSource.controllers.count

Наконец, вам нужно реализовать UIPageViewControllerDelegate и его метод pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:):

func pageViewController(_ pageViewController: UIPageViewController,
                        didFinishAnimating finished: Bool,
                        previousViewControllers: [UIViewController],
                        transitionCompleted completed: Bool) {
    // this will get you currently presented view controller
    guard let selectedVC = pageViewController.viewControllers?.first else { return }

    // and its index in the dataSource controllers (I'm using force unwrap, since in my case pageViewController contains only view controllers from my dataSource)
    let selectedIndex = self.dataSource.controllers.index(of: selectedVC)!
    // and we update the current page in pageControl
    self.pageControl.currentPage = selectedIndex
}

Теперь, по сравнению с ответом @Jan, мы обновляем self.pageControl.currentPage используя pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:) (показано выше), вместо pageViewController(_:willTransitionTo:). Это преодолевает проблему отмененного перехода - pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:) вызывается всегда, когда переход завершен (он также лучше имитирует поведение стандартного управления страницей).

Наконец, чтобы удалить стандартный элемент управления страницами, обязательно удалите реализацию методов presentationCount(for:) и presentationIndex(for:) UIPageViewControllerDataSource - если методы будут реализованы, будет представлен стандартный элемент управления страницей.

Итак, вы НЕ хотите иметь это в своем коде:

func presentationCount(for pageViewController: UIPageViewController) -> Int {
    return self.dataSource.controllers.count
}


func presentationIndex(for pageViewController: UIPageViewController) -> Int {
    return 0
}

Ответ 9

Вот мой прием. Эта версия только изменяет индикатор, когда анимация завершена, т.е. когда вы добираетесь до целевой страницы.

/** Each page you add to the UIPageViewController holds its index */
class Page: UIViewController {
    var pageIndex = 0
}

class MyPageController : UIPageViewController {

    private let pc = UIPageControl()
    private var pendingPageIndex = 0

    // ... add pc to your view, and add Pages ... 

    /** Will transition, so keep the page where it goes */
    func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController])
    {
        let controller = pendingViewControllers[0] as! Page
        pendingPageIndex = controller.pageIndex
    }

    /** Did finish the transition, so set the page where was going */
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool)
    {
        if (completed) {
            pc.currentPage = pendingPageIndex
        }
    }
}

Ответ 10

Это хорошо, и с большим изменением

var pendingIndex = 0;
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
    if completed {
        pageControl.currentPage = pendingIndex
    }
}

func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
    let itemController = pendingViewControllers.first as! IntroPageItemViewController
    pendingIndex = itemController.itemIndex
}

Ответ 11

Убедитесь, что параметр pageControl добавлен как subview. Затем в

-(void)viewDidLayoutSubviews {
      [super viewDidLayoutSubviews];

      CGRect frame = self.pageControl.frame;
      frame.origin.x = self.view.frame.size.width/2 - 
                       frame.size.width/2;
      frame.origin.y = self.view.frame.size.height - 100 ;
      self.pageControl.numberOfPages = self.count;
      self.pageControl.currentPage = self.currentIndex;
      self.pageControl.frame = frame;
 }

Ответ 12

Просто получите первый контроллер представления и узнайте индекс в файле didFinishAnimating:

 func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {

    pageControl.currentPage = onboardingViewControllers.index(of: pageViewController.viewControllers!.first!)!
}