70. Частые ошибки в SwiftUI
SwiftUI набирает обороты, статей и видео об этой технологии уже очень много, но ошибки и сложные ситуации возникают регулярно. В этой статье расскажу о парочке частых ошибок, и не самых очевидных проблемах, и сразу разберем эти ситуации.
@State и @Binding
Ссылки на официальную документацию: для @State, для @Binding.
Для чего нужен @State
Стейт позволяет хранить и изменять какое-то состояние внутри конкретной вьюхи: это может быть любая ваша модель с данными или простая строка/число.
Когда мы видим @State-свойство у вьюхи, то сразу понимаем, что это тут хранится источник истины для какой-то части этой вьюхи.
Вьюха может менять значение @State-свойства, но по умолчанию это происходит только в рамках этой же вьюхи.
Для чего нужен @Binding
Этот инструмент нужен, чтобы передать свойство вглубь по иерархии вьюх с возможностью изменения значения в обе стороны.
Например, вьюха содержит @State var isOn = false, и где-то в body у этой вьюхи есть Toggle (стандартный или кастомный), в который передается это значение $isOn. В данном примере значение можно поменять как по нажатию на Toggle, так и другим образом из основной вьюхи, где определено @State-свойство.
Таким образом, источник истины для тоггла - где-то снаружи, и тоггл не просто читает значение, но и изменяет его, передавая изменения наружу (к источнику в @State-поле).
Кстати, тут статья про работу с @Binding через get/set, может быть полезно иногда в работе.
Когда не нужен ни @State, ни @Binding
Если вьюха просто принимает на вход данные для отображения, и нам не нужна возможность изменять эти данные, передавать изменения вверх по иерархии, или сохранять эти данные во вьюхе (чтобы избежать лишних вычислений body), то можно использовать обычное let-свойство.
Как вариант, можно использовать var-свойство, чтобы сразу задать ему дефолтное значение и не делать дополнительный инициализатор с дефолтным значением (в случае с let-свойством), но это уже по ситуации.
Нужно помнить, что вьюхи в SwiftUI являются структурами и не могут изменять свои поля, в числе если это var-свойства.
Менять поля в структуре-View можно только для @State/@Binding.
Зона нажатия
Для примера возьмем такой код:
HStack(spacing: 12) {
Text("Нажми сюда")
Spacer()
Image(systemName: "person")
}
.padding(.horizontal)
.onTapGesture {
print("Нажали")
}
В данном случае принт будет выполняться при нажатии на текст или иконку.
Если нажать на пустое пространство между этими элементами, то onTapGesture не вызовется. Чтобы это исправить, можно применить модификатор contentShape:
HStack(spacing: 12) {
Text("Нажми сюда")
Spacer()
Image(systemName: "person")
}
.padding(.horizontal)
.contentShape(.rect)
.onTapGesture {
print("Нажали")
}
Теперь нажатие распознается в любом месте внутри нашего HStack.
Фреймы против паддингов
Можно верстать на фреймах, т.е. задавать явные ограничения по высоте и ширине, а можно использовать паддинги.
Фреймы
Преимущество: фреймы позволяют явно задавать размеры элементов, что обеспечивает более точный контроль над визуальным дизайном приложения.
Недостаток: фреймы ограничивают адаптивность дизайна. Например, при повороте экрана или изменении размера шрифта в настройках девайса фреймы не изменятся, и верстка "поедет". Придется либо писать много условий и проверок на разные ситуации, либо продавать бизнесу отсутствие адаптивности (плохая идея).
Паддинги
Преимущество: паддинги позволяют элементам автоматически изменять свои размеры и положение в зависимости от размера экрана, ориентации устройства или при изменении размера шрифта в настройках девайса, что обеспечивает адаптивность дизайна - это очень круто.
Недостаток: без опыта может быть сложно разобраться как сверстать на паддингах. Ведь есть же фрейм, почему бы явно не задать габариты?
Я рекомендую верстать на паддингах, если это возможно 🙂
Заключение
Чем больше практики - тем проще в работе. Набирайте опыт, делайте пет-проекты, читайте обучалки и применяйте на практике - и все у вас получится классно 👍
Другие мои статьи можно почитать тут.