Урок 1.4 Прокручиваемые представления

До сих пор все ваши представления в этом курсе аккуратно вписывались в экран. По мере роста ваших идей для приложений вы не всегда сможете утверждать это. Что если вы хотите, чтобы ваши пользователи заполняли длинную форму, например, страницу регистрации? Или, возможно, вы хотите позволить им изучить детали очень большого изображения. Какой подход вы бы выбрали?

В этом уроке вы узнаете, как использовать UIScrollView для отображения контента, который больше экрана устройства, и позволяющего пользователю взаимодействовать с ним.


Что вы узнаете:

  • Как и когда использовать прокручиваемые представления
  • Как использовать прокручиваемые представления с автораскладкой и стековыми представлениями
  • Как добавить буферное пространство вокруг вашего контента

Словарь

  • bounds — границы: Свойство, которое определяет размер и местоположение области отображения внутри представления (view) относительно его собственной системы координат.

  • content inset — внутренние отступы содержимого: Свойство, которое задает дополнительные отступы вокруг содержимого внутри прокручиваемого представления (scroll view).

  • content view — видимое содержимое: Представление, содержащее данные или элементы интерфейса, которые пользователь может просматривать и с которыми может взаимодействовать.

  • frame — рамка: Свойство, определяющее размер и местоположение представления (view) относительно системы координат его родительского представления.

  • scroll view — прокручиваемое представление: Представление, которое предоставляет возможность прокрутки содержимого, если его размер превышает размер видимой области.


Связанные ресурсы

  • Руководство по интерфейсам iOS: Прокручиваемые представления
  • Справка по API: UIScrollView
  • Справка по API: UIStackView
  • Техническое примечание TN2154: UIScrollView и Авторазметка

Всякий раз, когда вашему приложению нужно отобразить контент или позволить пользователю взаимодействовать с контентом, который не помещается полностью на экране, вам понадобится прокручиваемое представление (scroll view). Отличным примером является приложение "Фотографии". Откройте его на устройстве с iOS и выберите фотографию. Сначала изображение заполнит экран настолько, насколько это возможно, сохраняя при этом исходное соотношение сторон. Теперь представьте, что вы хотите рассмотреть определенную деталь изображения. Увеличьте изображение двойным нажатием. Даже если фото теперь больше экрана, особенно если вы используете устройство с небольшим экраном, вы сможете прокручивать изображение в любом направлении.

Для того чтобы UIScrollView работало, ему нужно знать два набора информации: положение и размер прокручиваемого представления, а также размер отображаемого содержимого. Программно эти значения хранятся в свойствах frame и contentSize прокручиваемого представления соответственно. Оба свойства можно управлять с помощью Авторазметки (Auto Layout) и Interface Builder.

Для того чтобы происходила прокрутка, ширина или высота (или и то, и другое) размера содержимого должны быть больше, чем ширина или высота рамки (frame). На приведенной ниже схеме размер рамки представлен красным прямоугольником. Обратите внимание, что высота содержимого больше высоты рамки, а ширина содержимого совпадает с шириной рамки, — следовательно, это представление будет прокручиваться только по вертикали.

Настройка размеров позволяет создать прокручиваемые представления, которые будут прокручиваться по горизонтали, вертикали или в обоих направлениях. Помимо требования к размеру содержимого, все представления, которые вы хотите прокручивать, должны быть подпредставлениями (subviews) прокручиваемого представления.

Прокручиваемые представления в Interface Builder

В этом уроке вы научитесь реализовывать прокручиваемые представления с помощью функции Auto Layout в Interface Builder. Представьте, что вы проектируете пользовательский интерфейс, который позволит пользователям вводить информацию для доставки, возможно, как часть процесса оформления заказа при покупке в интернете. Он будет выглядеть примерно так, как на схеме выше.

Начните с создания нового проекта в Xcode, используя шаблон iOS App. Назовите проект "ScrollingForm". При создании проекта убедитесь, что опция интерфейса установлена на "Storyboard", и сохраните его в папку вашего проекта.

Определение рамки прокручиваемого представления

В главном сториборде (Main storyboard) найдите прокручиваемое представление (scroll view) в библиотеке объектов (Objects library) и перетащите его на сцену контроллера представлений (view controller scene). Настройте размер прокручиваемого представления так, чтобы он соответствовал размеру безопасной зоны сцены (Safe Area)  1  .

Добавьте ограничения, чтобы закрепить все четыре края прокручиваемого представления по краям безопасной зоны контроллера представлений. Эти ограничения Auto Layout применяются к руководству по макету рамки прокручиваемого представления (Frame Layout Guide) и гарантируют, что размер прокручиваемого представления будет таким же, как у представления контроллера—будь то iPhone SE, iPad Pro или любое другое устройство. Эти ограничения также определяют размер и положение прокручиваемого представления.  2  

Программные ограничения для Content Layout Guide

При необходимости вы также можете применять ограничения программно к Content Layout Guide прокручиваемого представления. Другими словами, вы можете не только ограничивать размер и положение прокручиваемого представления, но и устанавливать ограничения для управления поведением его содержимого. Например, если у вас есть изображение внутри прокручиваемого представления, которое вы хотите удерживать в центре при увеличении и уменьшении масштаба, вы можете привязать центр этого изображения к центру Content Layout Guide прокручиваемого представления следующим образом:

imageView.centerXAnchor.constraints(equalTo: scrollView.contentLayoutGuide.centerXAnchor)
imageView.centerYAnchor.constraints(equalTo: scrollView.contentLayoutGuide.centerYAnchor)

Это лишь один из примеров, и хотя вы не будете использовать ограничения для Content Layout Guide прокручиваемого представления в этой практике по созданию формы с прокруткой, они могут быть полезны во многих сценариях.

Определение содержимого с использованием Stack View

Теперь вам нужно задать размер содержимого для прокручиваемого представления. Для большинства задач по размещению, логика реализации прокручиваемого представления с Auto Layout станет намного проще, если использовать фиктивное представление (dummy view) для содержимого прокручиваемого представления. Задача этого фиктивного представления — содержать контент, и, как правило, оно не видно пользователю. Иногда его называют content view.

Для продолжения работы с формой вы будете использовать stack view для размещения всего содержимого. Выберите вертикальный stack view из библиотеки объектов и добавьте его в качестве подпредставления (subview) прокручиваемого представления. Настройте размер так, чтобы он соответствовал размеру прокручиваемого представления, и закрепите края stack view к Content Layout Guide, перетягивая (Control-drag) Stack View из контурного представления (outline) в Content Layout Guide. Удерживая Command при нажатии на каждое ограничение, вы можете добавить их все сразу, не закрывая меню.  3  

В документе Outline щелкните стрелку рядом с Constraints под Stack View и разверните область представления Outline, чтобы увидеть полный набор только что добавленных ограничений. Убедитесь, что значение Constant для каждого из них установлено на 0.  4   Теперь Stack View определяет область содержимого для прокручиваемого представления.

Эта форма должна прокручиваться только по вертикали, а не по горизонтали. Создайте ограничение Equal Widthsмежду Stack View и Frame Layout Guide, перетащив (Control-drag) одно на другое в Outline. Найдите новое ограничение в Outline и убедитесь, что его значение Constant равно 0, а Multiplier — 1. Теперь ширина Stack View всегда будет равна ширине рамки прокручиваемого представления, что означает, что оно не сможет прокручиваться по горизонтали.

Начало создания формы

Вспомните урок о Auto Layout и Stack Views. Вы узнали, что когда для Stack View не определена ширина или высота, его размер зависит от его дочерних представлений. По мере добавления содержимого в Stack View, его размер увеличивается, чтобы вместить всё содержимое. Таким образом, содержимое внутри Stack View определяет его размер, и он будет ровно таким, каким необходимо.

Поскольку Stack Views увеличиваются, чтобы соответствовать своему содержимому, они являются идеальным вариантом для удержания содержимого в прокручиваемом представлении. По мере увеличения содержимого прокручиваемое представление будет прокручиваться, чтобы соответствовать размерам содержимого.

В проекте ScrollingForm вы добавите своё прокручиваемое содержимое в Stack View. Чтобы упростить процесс, вы создадите группу представлений, которая позволит вам добавлять столько полей, сколько вам нужно. Каждая группа будет состоять из трёх представлений: UIView (чтобы служить фоном и контейнером), Label и Text Field. Label будет подсказывать пользователю, какой тип данных вводить в текстовое поле. Оба представления будут дочерними для фонового представления.

 

 

Перетащите представление из библиотеки объектов в Stack View. (Подсказка: Ввод "UIView" в фильтр библиотеки — это быстрый способ найти объект представления.) Interface Builder показывает, что у вас есть проблемы с компоновкой, что обозначается белой стрелкой внутри красного круга в контуре документа, а также красными индикаторами компоновки в сцене.

Что происходит? Вы не задали достаточно информации о размере содержимого для прокручиваемого представления. Для начала, у добавленного вами UIView нет внутреннего, или естественного, размера. Следовательно, Stack View не может определить его размер, а это значит, что прокручиваемое представление не может определить размер своего содержимого. В следующем разделе, после того как вы добавите остальные представления, вы убедитесь, что у вас достаточно ограничений для определения требуемых размеров.

Добавьте метку (label) в представление, которое вы только что добавили. Используйте направляющие для выравнивания, чтобы разместить метку в верхнем левом углу представления.

Затем добавьте текстовое поле (text field) под меткой, снова аккуратно используя направляющие для выравнивания.

Отрегулируйте высоту представления так, чтобы его нижний край был выровнен с нижним краем текстового поля.

Затем отрегулируйте правые края метки и текстового поля так, чтобы они были выровнены с правым краем представления.

Добавление ограничений

Теперь, когда ваше представление аккуратно выровнено, добавление ограничений должно быть легким. Добавление этих ограничений решит проблемы с макетом, о которых говорилось выше, определив размер содержимого в прокручиваемом представлении. (Если вам нужен краткий обзор, ознакомьтесь с уроком о Auto Layout в Юните 2 курса Develop in Swift Fundamentals.)

Выберите метку и нажмите кнопку "Добавить новые ограничения". Добавьте ограничение для каждого края.  5   

Выберите текстовое поле и нажмите кнопку "Добавить новые ограничения". Поскольку в предыдущем шаге вы добавили ограничение между текстовым полем и меткой, вам нужно только добавить ограничения для оставшихся трех краев.  6  

Добавление дополнительных полей в вашу форму

Если бы вы сейчас запустили проект, смогли бы вы прокручивать содержимое? Почему или почему нет? Вы были бы правы, если бы сказали "нет". Контент не больше, чем прокручиваемое представление, поэтому прокрутка не нужна.

В "Outline" выберите представление в стеке и скопируйте его (Command-C). Вставьте его (Command-V) несколько раз в стековое представление, по одному разу для каждого поля, которое вы хотите включить в свою форму. Поскольку ваша форма будет собирать информацию для доставки, вам, вероятно, понадобятся следующие поля: имя, фамилия, строка адреса 1, строка адреса 2, город, штат, ZIP-код и номер телефона. Не забудьте изменить метки с описанием содержимого полей.  7  

Соберите и запустите ваше приложение на различных устройствах. На больших устройствах, возможно, будет достаточно места, чтобы отобразить все представления, и прокрутка не потребуется. На меньших устройствах, таких как iPhone XS, вы не сможете увидеть все представления, но вы должны иметь возможность прокрутить, чтобы увидеть остальные. Если у вас нет физического устройства достаточного размера, запустите приложение на маленьком устройстве в симуляторе.

Проблемы с клавиатурой

Когда вы запустили ваше приложение на меньших устройствах, вы могли заметить, что клавиатура закрывает некоторые текстовые поля во время их редактирования.

Очевидно, что если пользователь не может видеть текст, который он вводит, это не является хорошим опытом. В этом уроке не предоставляются подробности о том, как управлять клавиатурой с помощью scroll view, но вот краткое объяснение, которое поможет вам справиться с этой проблемой.

iOS отправляет уведомление каждый раз, когда отображается клавиатура. Вы можете добавить код, чтобы слушать это уведомление и выполнять функцию, когда уведомление публикуется. И, поскольку уведомление включает размер клавиатуры, ваша функция может настроить scroll view так, чтобы его содержимое не перекрывалось клавиатурой. Вы также можете добавить слушатель для другого уведомления, которое публикуется, когда клавиатура будет скрыта, что позволит вам отменить внесенные изменения.

Если вам интересно узнать больше об уведомлениях, ознакомьтесь с документацией API Reference для Notification Center.

Чтобы настроить размер представления содержимого, вам нужно создать outlet от scroll view к его view controller. Затем вы можете добавить следующий код в класс ViewController, чтобы исправить проблемы с клавиатурой:

func registerForKeyboardNotifications() {
        NotificationCenter.default.addObserver(self, selector:
        #selector(keyboardWasShown(_:)),
        name: .UIResponder.keyboardDidShowNotification,
        object: nil)
        NotificationCenter.default.addObserver(self, selector:
        #selector(keyboardWillBeHidden(_:)),
        name: UIResponder.keyboardWillHideNotification,
        object: nil)
 
    }
 
    @objc func keyboardWasShown(_ notificiation: NSNotification) {
        guard let info = notificiation.userInfo,
            let keyboardFrameValue =
            info[UIResponder.keyboardFrameBeginUserInfoKey]
            as? NSValue else { return }
 
        let keyboardFrame = keyboardFrameValue.cgRectValue
        let keyboardSize = keyboardFrame.size
 
        let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0,
        bottom: keyboardSize.height, right: 0.0)
        scrollView.contentInset = contentInsets
        scrollView.scrollIndicatorInsets = contentInsets
    }
 
    @objc func keyboardWillBeHidden(_ notification: 
       NSNotification) {
        let contentInsets = UIEdgeInsets.zero
        scrollView.contentInset = contentInsets
        scrollView.scrollIndicatorInsets = contentInsets
    }

Теперь добавьте следующую строку в viewDidLoad():

registerForKeyboardNotifications()

Соберите и запустите ваше приложение. Теперь вы должны иметь возможность видеть, что вводите в любом текстовом поле.

Вставки контента и вставки индикатора прокрутки

Чтобы убедиться, что клавиатура не перекрывает ваш контент, вы использовали вставку контента (content inset) для изменения размера области контента в scroll view, создавая пространство для клавиатуры. Вы часто будете использовать это решение, когда захотите добавить отступы к контенту в scroll view, чтобы контроллеры, панели инструментов и клавиатуры не мешали пользователю взаимодействовать с контентом.

Чтобы добавить отступы, используйте свойство contentInset для указания буферной зоны вокруг контента в scroll view. Добавляя отступы, вы делаете окно scroll view к его контенту меньше, не изменяя размер самого scroll view или его контента.

Свойство contentInset — это структура UIEdgeInsets с полями для верхней, нижней, левой и правой сторон.

Но есть проблема. Изменение значения contentInset имеет неожиданный побочный эффект, когда ваш scroll view отображает индикаторы прокрутки. Например, индикаторы прокрутки могут быть нарисованы позади клавиатуры, что является нежелательным результатом. Чтобы исправить это, вам нужно будет изменить значение scrollIndicatorInsets. Как и свойство contentInset, свойство scrollIndicatorInsets определяется как структура UIEdgeInsets. Вы можете исправить ситуацию, установив значения scrollIndicatorInsets, чтобы они совпадали со значением contentInset.

let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0,
bottom: keyboardSize.height, right: 0.0)
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets

Семейство Scroll View

UIScrollView является родительским классом для нескольких других классов в UIKit, включая UITableView и UICollectionView. Эти два известных класса наследуют всю функциональность классов UIScrollView. Когда вы перейдете к изучению этих новых тем, помните об этой связи, так как вы сможете использовать все инструменты, которые изучили в этом уроке, при работе с table views или collection views.

Задание

Создайте горизонтальный scroll view, который отображает три ваших любимых изображения.

Лабораторная работа — Я ШПИОН

Цель

Цель этой лабораторной работы — реализовать scroll view на image view, который позволит пользователям увеличивать и перемещать изображение. Ваше приложение будет содержать один экран с одной картинкой.

Создайте новый проект под названием "ISpy" с использованием шаблона iOS App.

Шаг 1

Настройка Storyboard

  • Добавьте scroll view в view controller и измените его размер так, чтобы он вписывался в Safe Area. Добавьте ограничения, чтобы закрепить края scroll view за границами Safe Area view controller.

  • Добавьте image view как подвид в scroll view и измените его размер так, чтобы он соответствовал границам scroll view. Добавьте ограничения, чтобы закрепить края image view за Content Layout Guide scroll view.

  • Добавьте изображение по вашему выбору в папку Assets. Установите его как изображение для image view.

  • Создайте аутлеты для scroll view и image view в файле ViewController.

Шаг 2

Реализация UIScrollViewDelegate

  • Чтобы использовать scroll view для увеличения, вашему view controller нужно соответствовать протоколу UIScrollViewDelegate. Вы можете узнать больше о UIScrollViewDelegate в документации. Но пока добавьте следующую строку кода в объявление класса:
class ViewController: UIViewController, UIScrollViewDelegate {...}
  • В методе viewDidLoad() установите делегата для scroll view как экземпляр ViewController.
  • Реализуйте метод делегата viewForZooming() и верните image view в теле метода.
  • Создайте новый метод updateZoomFor(size: CGSize), который будет обновлять масштаб изображения. Переопределите viewDidAppear(_:) и в теле вызовите updateZoomFor(size:), передав view.bounds.size.
  • Внутри метода updateZoomFor(size:) используйте imageView и параметр size для вычисления масштаба. Затем установите свойство minimumZoomScale для scrollView на этот масштаб, как показано ниже:
let widthScale = size.width / imageView.bounds.width 
let heightScale = size.height / imageView.bounds.height 
let scale = min(widthScale, heightScale)
scrollView.minimumZoomScale = scale
scrollView.zoomScale = scale
  • Код выше сначала вычисляет масштаб, необходимый для отображения всей ширины и высоты изображения. Затем он устанавливает минимальный масштаб как меньший из двух (ширины или высоты), чтобы вы не могли уменьшить масштаб меньше этого значения. Наконец, он задает начальный масштаб увеличения так, чтобы изображение вписывалось в экран.
  • Запустите приложение. Вы должны иметь возможность масштабировать и прокручивать изображение во всех направлениях. (Используйте Option-drag для увеличения в Симуляторе.)

Отличная работа! Вы создали приложение, которое может прокручивать и увеличивать изображение. Можете ли вы придумать, где может быть полезна эта функциональность? Обязательно сохраните ваше приложение в папке с проектами для дальнейшего использования.

 

 

 


Отрывок из книги
Develop in Swift Data Collections
Apple Education
https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewBook?id=1581183203
Этот материал может быть защищен авторским правом.

Information

Apple, the Apple logo, Apple Books, Apple TV, Apple Watch, Cocoa, Cocoa Touch, Finder, Handoff, HealthKit, iPad, iPad Pro, iPhone, iPod touch, Keynote, Mac, macOS, Numbers, Objective-C, Pages, Photo Booth, Safari, Siri, Spotlight, Swift, tvOS, watchOS, and Xcode are trademarks of Apple Inc., registered in the U.S. and other countries. App Store and iBooks Store are service marks of Apple Inc., registered in the U.S. and other countries. ​
The Bluetooth® word mark and logos are registered trademarks owned by Bluetooth SIG, Inc. and any use of such marks by Apple is under license. ​
IOS is a trademark or registered trademark of Cisco in the U.S. and other countries and is used under license. ​
Other product and company names mentioned herein may be trademarks of their respective companies.