Узнайте, как вычислять смещения в реальном времени в SwiftUI ScrollView.
SwiftUI, новый декларативный способ создания пользовательских интерфейсов, — действительно замечательная структура. Многое можно сделать за короткое время с полным предварительным просмотром в реальном времени, но иногда воспроизведение чего-то довольно распространенного в UIKit может стать головной болью. Смещения ScrollView — одна из таких вещей!
В UIKit каждый UIScrollView
имеет свойство, которое позволяет нам легко считывать смещение самого представления:
var contentOffset: CGPoint { get set }
Он возвращает структуру со значениями x
и y
. Супер просто, супер удобно!
SwiftUI, к сожалению, сегодня не хватает этого простого свойства. Поэтому нам нужно придумать способ как-то получить это значение.
В конце этого урока вы сможете создать что-то похожее на это:
Фреймворк SwiftUI часто позволяет (или заставляет) нам мыслить нестандартно для решения задач, и это отличный повод для этого.
Давайте сначала начнем с создания очень простого пользовательского интерфейса с длинным списком и меткой Text
, которая не сможет показать реальное значение смещения (конечно, мы добавим эту функцию позже), поскольку вы можете видеть, что переменная verticalOffset
никогда не изменяется:
Чтобы добиться результата, показанного выше, мы напишем View
, который будет вести себя точно так же, как SwiftUI ScrollView
, но каким-то образом транслирует значение своего смещения в реальном времени.
Во-первых, нам нужно создать новую структуру, которая будет соответствовать протоколу PreferenceKey
.
Официальная документация Apple дает нам следующее определение этого протокола:
Именованное значение, созданное представлением.
Представление с несколькими дочерними элементами автоматически объединяет свои значения для заданного предпочтения в одно значение, видимое для его предков.
Это сложный способ сказать, что мы позволяем представлению взаимодействовать с представлениями, которые его содержат, и передавать значения.
Чтобы соответствовать этому протоколу, должны быть реализованы свойство и функция, обе статические:
static var defaultValue: Self.Value { get } static func reduce(value: inout Self.Value, nextValue: () -> Self.Value)
Значением по умолчанию будет наше смещение, CGPoint
, с начальным значением 0,0
.
Давайте создадим структуру OffsetPreferenceKey
вот так:
Отлично, теперь пришло время создать нашу версию scrollView
.
Давайте создадим структуру с теми же свойствами, что и SwiftUI ScrollView
, но с дополнительным элементом, onOffsetChanged
замыканием, которое срабатывает, когда scrollview
изменяет свою позицию:
Как видите, я использовал общий var content для передачи всего содержимого файла ScrollView
. Как указано в определении структуры T
будет иметь тип View
.
Давайте теперь посмотрим на реализацию свойства body
:
- В строке 2 я создал
ScrollView
- В строке 3
GeometryReader
содержит пустое представление,Color.Clear
без измерений. Нам нужно отслеживать положение представления, и это умная идея использовать безразмерное представление. Внутри него я установил ключOffsetPreferenceKey
, передавая в качестве значения источник самого фрейма. Я использовалcoordinateSpace(name:)
, чтобы позволить другой функции находить и работать с нашим представлениемColor
, а также работать с измерениями относительно этого представления. - Я передаю исходную позицию внешнему миру, запуская замыкание при изменении этого значения в строке 15.
- В строке 12 я использую
content
, переданный инициализатору.
Это вся структура:
Готово! У нас есть новый OffsettableScrollView
, который сможет передавать значение своего смещения в любое время при его изменении!
Настало время изменить наше исходное представление содержимого с помощью, наконец, подключенного Text
:
Все кончено! Как видите, теперь это супер просто и супер чисто!
Надеюсь, вам понравилась эта статья. Если вас интересует видеоверсия, это руководство также есть на моем канале YouTube:
Удачного кодирования!