UIPageViewController — это компонент пользовательского интерфейса iOS, который предлагает удобный способ навигации между последовательными страницами контента. Он использует постраничную модель представления, позволяющую пользователям пролистывать страницы влево или вправо с помощью жестов.

Особенно полезен в приложениях, где контент должен быть представлен в формате, напоминающем физические страницы. Например, в приложениях для чтения книг, образовательных курсах, галереях изображений или даже в некоторых типах интерактивных руководств. UIPageViewController обеспечивает дополнительный уровень привлекательности и удобства использования приложения.

Сегодня я хочу поделиться кратким и простым руководством о том, как просто и программно реализовать UIPageViewController. Итак, что же мы получим в итоге?


Для начала определим класс ContentViewController, который реализует собственный контроллер представления. Этот класс предназначен для использования в UIPageViewController, где каждая страница представлена экземпляром ContentViewController. Это позволяет легко создавать несколько страниц с различными цветами текста и фона:

class ContentViewController: UIViewController {
    
    private lazy var textLabel: UILabel = {
        let label = UILabel()
        label.textAlignment = .center
        label.textColor = .white
        label.font = UIFont.systemFont(ofSize: 25, weight: .bold)
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        setupConstraints()
    }

    private func setupViews() {
        view.addSubview(textLabel)
    }
    
    private func setupConstraints() {
        NSLayoutConstraint.activate([
            textLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            textLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            textLabel.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8)
        ])
    }

    func configure(with text: String, backgroundColor: UIColor) {
        textLabel.text = text
        view.backgroundColor = backgroundColor
    }
}

configure(with:backgroundColor:):

Этот метод позволяет настроить textLabel и цвет фона контроллера снаружи. Параметр text используется для установки текста в textLabel, а backgroundColor - для установки цвета фона view


Теперь создадим класс PageViewController, который наследуется от UIPageViewController. Этот класс эффективно управляет созданием и отображением различных страниц, каждая из которых представлена экземпляром ContentViewController с настраиваемым цветом текста и фона:

class PageViewController: UIPageViewController {

    var pages: [ContentViewController] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        self.dataSource = self

        let pageData: [(String, UIColor)] = [
            ("Let's", .paleLavender),
            ("Get", .paleTurquoise),
            ("Started", .paleGreen)
        ]

        for (text, color) in pageData {
            let contentVC = ContentViewController()
            contentVC.configure(with: text, backgroundColor: color)
            pages.append(contentVC)
        }

        if let firstPage = pages.first {
            setViewControllers([firstPage], direction: .forward, animated: true, completion: nil)
        }
    }
}

Что мы здесь делаем:

  • Создаем массив пустых страниц pages, который будет содержать экземпляры ContentViewController

  • Создаем массив кортежей pageData [(String, UIColor)], где каждый кортеж содержит строку и цвет. Эти данные используются для настройки каждой страницы в UIPageViewController

  • Проходим циклом по каждому элементу pageData (где каждый элемент - это кортеж) и для каждого элемента создаем новый экземпляр ContentViewController, далее вызывает метод configure для настройки contentVC и добавляем сконфигурированный contentVC в массив pages

  • Далее проверяем, находится ли первая страница в массиве страниц. Если страница существует, она устанавливается в качестве текущего активного контроллера в UIPageViewController

  • Устанавливает сам PageViewController в качестве источника данных (dataSource) для UIPageViewController. Это необходимо для управления содержимым страниц.


Все, что нам осталось сделать, это реализовать протокол UIPageViewControllerDataSource, который используется для предоставления контроллеров представления, которые будут отображаться в UIPageViewController:

extension PageViewController: UIPageViewControllerDataSource {
  
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let contentVC = viewController as? ContentViewController,
              let currentIndex = pages.firstIndex(of: contentVC),
              currentIndex > 0 else {
            return nil
        }
        return pages[currentIndex - 1]
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let contentVC = viewController as? ContentViewController,
              let currentIndex = pages.firstIndex(of: contentVC),
              currentIndex < pages.count - 1 else {
            return nil
        }
        return pages[currentIndex + 1]
    }
}

Эти два метода обеспечивают логику отображения контроллера и плавную и последовательную навигацию между страницами.

Рассмотрим каждый метод подробно:

  1. pageViewController(_:viewControllerBefore:):

    • Этот метод определяет, какой контроллер должен быть показан перед текущим контроллером в UIPageViewController

    • guard let проверяет, что текущий контроллер является экземпляром ContentViewController и определяет его текущий индекс в массиве pages

    • currentIndex > 0 гарантирует, что текущий контроллер не является первым в массиве, что позволяет избежать выхода за пределы массива

    • Если условие выполняется, метод возвращает контроллер представления, который находится перед текущим (pages[currentIndex - 1]). Если условие не выполняется, метод возвращает nil, указывая, что предыдущего контроллера нет

  2. pageViewController(_:viewControllerAfter:):

    • Этот метод определяет, какой контроллер должен отображаться после текущего контроллера в UIPageViewController

    • Аналогично первому методу, guard let проверяет, что текущий контроллер является экземпляром ContentViewController и определяет его индекс

    • currentIndex < pages.count - 1 проверяет, что текущий контроллер не является последним в массиве

    • Если условие выполняется, метод возвращает следующий контроллер представления (pages[currentIndex + 1]). Если нет, то возвращает nil, указывая на отсутствие следующего контроллера


Добавляем в App или Scene:

let pageViewController = PageViewController(transitionStyle: .pageCurl, navigationOrientation: .horizontal, options: nil)
window?.rootViewController = pageViewController

PageViewController предлагает различные стили перехода и ориентации навигации:

Параметры TransitionStyle: параметр .pageCurl имитирует эффект перелистывания настоящей книги или журнала, .scroll предоставляет опцию, при которой страницы скользят горизонтально или вертикально.

NavigationOrientation определяет, будет ли пользователь перемещаться по страницам горизонтально или вертикально.