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 определяет, будет ли пользователь перемещаться по страницам горизонтально или вертикально.

Комментарии (1)


  1. Mulish
    17.11.2023 16:51
    +1

    Спасибо за статью, как раз то, что искал, чтобы без сторибодов!