June 5, 2024

70. Частые ошибки в SwiftUI

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

iOS-разработчик борется с ошибками в 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.

Фреймы против паддингов

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

Фреймы

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

Недостаток: фреймы ограничивают адаптивность дизайна. Например, при повороте экрана или изменении размера шрифта в настройках девайса фреймы не изменятся, и верстка "поедет". Придется либо писать много условий и проверок на разные ситуации, либо продавать бизнесу отсутствие адаптивности (плохая идея).

Паддинги

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

Недостаток: без опыта может быть сложно разобраться как сверстать на паддингах. Ведь есть же фрейм, почему бы явно не задать габариты?

Я рекомендую верстать на паддингах, если это возможно 🙂

Заключение

Чем больше практики - тем проще в работе. Набирайте опыт, делайте пет-проекты, читайте обучалки и применяйте на практике - и все у вас получится классно 👍

Другие мои статьи можно почитать тут.