Как получить размер представления в SwiftUI

Как получить размер представления в SwiftUI

В SwiftUI компоновка представлений зависит от их текущего состояния, которое формируется на основе внутренних свойств, внешних параметров и значений из среды.

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

Давайте разберёмся, как можно получить эту информацию.

📏 Чтение размера представления

В SwiftUI существует инструмент для работы с размерами представлений — GeometryReader.

Этот контейнер заполняет все доступное пространство и предоставляет GeometryProxy, который даёт доступ к параметрам окружающей среды, таким как размер и система координат.

Пример использования:

var body: some View { GeometryReader { geometry in Text("Размер: \(geometry.size.width) x \(geometry.size.height)") } }

Однако GeometryReader захватывает всё доступное пространство, а нам зачастую нужно просто узнать размеры конкретного элемента.

Здесь на помощь приходят модификаторы .background() и .overlay(), которые позволяют встроить GeometryReader без изменения размеров основного представления:

var body: some View { childView .background( GeometryReader { geometry in Color.clear // Доступ к geometry.size } ) }

Чтобы GeometryReader не мешал макету, можно использовать Color.clear — это создаст невидимый слой:

var body: some View { childView .background( GeometryReader { geometry in Color.clear .preference(key: SizePreferenceKey.self, value: geometry.size) } ) }

🔄 Передача данных от ребёнка к родителю

SwiftUI не позволяет напрямую передавать информацию "снизу вверх" по иерархии представлений, но для этого есть механизм PreferenceKey.

Создадим ключ для хранения размера представления:

struct SizePreferenceKey: PreferenceKey { static var defaultValue: CGSize = .zero static func reduce(value: inout CGSize, nextValue: () -> CGSize) {} }

Затем используем .preference() для передачи данных:

var body: some View { childView .background( GeometryReader { geometry in Color.clear .preference(key: SizePreferenceKey.self, value: geometry.size) } ) }

А теперь — способ получения этих данных в родительском представлении:

var body: some View { childView .background( GeometryReader { geometry in Color.clear .preference(key: SizePreferenceKey.self, value: geometry.size) } ) .onPreferenceChange(SizePreferenceKey.self) { newSize in print("Размер дочернего элемента: \(newSize)") } }

Любое родительское представление может подписаться на изменения этого ключа через .onPreferenceChange(), получая актуальные размеры.

📌 Улучшение кода с помощью расширения

Такой способ измерения размера элемента полезен настолько часто, что его удобно вынести в расширение:

extension View { func readSize(onChange: @escaping (CGSize) -> Void) -> some View { background( GeometryReader { geometry in Color.clear .preference(key: SizePreferenceKey.self, value: geometry.size) } ) .onPreferenceChange(SizePreferenceKey.self, perform: onChange) } }

Теперь использование сводится к одной строке:

childView.readSize { newSize in print("Размер: \(newSize)") }

🏁 Итоги

Мы разобрали, как в SwiftUI можно передавать информацию о размере представлений с помощью GeometryReader, PreferenceKey и .onPreferenceChange().

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

А как вы применяете PreferenceKey в своих проектах? Делитесь идеями и примерами! 🚀

Спасибо за чтение, и до встречи в следующих статьях! 😊

Начать дискуссию