81. Когда нужен и не нужен EmptyView()
Многие ребята, кто работает со SwiftUI, знают, что EmptyView() подходит для ситуаций, когда не нужно показывать ничего. Некоторые даже знают, что на эту вьюху не влияет frame. В этой статье покажу, когда можно/не нужно не использовать эту вьюшку.
Экран для примера
В двух случаях из трех для состояния "в ожидании" используется EmptyView().frame(width: 1000, height: 1000).
Попробуйте угадать, в каком случае из трех он не используется 👀
Код для экрана
struct EmptyViewExample: View {
@State private var someState: SomeState? = .idle
var body: some View {
VStack(spacing: 20) {
picker
HStack {
Text("1")
fullSwitchCaseView
}
HStack {
Text("2")
ifLetElseView
}
HStack {
Text("3")
ifLetView
}
resetButton
}
}
}
extension EmptyViewExample {
enum SomeState: CaseIterable, Identifiable {
var id: Self { self }
case idle, ready, loading, error
var description: String {
switch self {
case .idle: "в ожидании"
case .ready: "готов"
case .loading: "загрузка..."
case .error: "ошибка!"
}
}
@ViewBuilder
var dummyView: some View {
switch self {
case .idle:
EmptyView().frame(width: 1000, height: 1000)
case .ready:
Text("вьюха для состояния готовности")
case .loading:
Text("вьюха для загрузки")
case .error:
Text("вьюха для ошибки ‼️")
}
}
}
}
Пояснение
В нашем примере EmptyView() используется в первых двух вьюхах, и в enum для состояния экрана. При этом в enum SomeState наш EmptyView() нормально вписывается в общую картинку, а вот во вьюхах ситуация другая, посмотрим на них.
Первая вьюха с EmptyView()
@ViewBuilder
private var fullSwitchCaseView: some View {
switch someState {
case .ready: SomeState.ready.dummyView
case .loading: SomeState.loading.dummyView
case .error: SomeState.error.dummyView
case .idle, nil: EmptyView().frame(width: 1000, height: 1000)
}
}
Понятное дело, что это код для примера, и у вас в проекте скорее всего такого не будет, но все же: чтобы для опционального стейта отобразить нужную вьюху, при использовании switch нужно пройтись по каждому кейсу, в том числе nil, а если пропустить этот кейс, компилятор ожидаемо будет ругаться на необработанный сценарий.
Вторая вюха с EmptyView()
@ViewBuilder
private var ifLetElseView: some View {
if let someState {
someState.dummyView
} else {
EmptyView()
}
}
В этом примере мы воспользовались нашим удобным вычисляемым свойством dummyView, которое сразу возвращает нужную вьюху для каждого кейса нашего enum, а если стейт отсутствует, то "показываем" EmptyView().
Третья вьюха
@ViewBuilder
private var ifLetView: some View {
if let someState {
someState.dummyView
}
}
В этом примере мы не пишем else-случай, потому что использование @ViewBuilder делает это за нас "под капотом". То есть мы пишем меньше кода, чем во второй вьюхе, а результат тот же.
Четвертая вьюха (нет на демо)
private var optionalView: some View {
someState?.dummyView
}
Самый короткий вариант - просто вернуть вычисляемую вьюху сразу. Результат будет такой же, как и во всех предыдущих случаях, но кода меньше всего.
ViewBuilder
Как уже можно было заметить, во всех случаях кроме четвертой вьюхи мы явно применяем @ViewBuilder, чтобы создать some View. Ну а в четвертой он применяется неявно - внутри enum.
Если же убрать @ViewBuilder из любого нашего сценария, то получим ошибку: Branches have mismatching types 'Text' and 'EmptyView'.
На всякий случай - не используйте AnyView для избавления от этой ошибки, пользуйтесь именно @ViewBuilder.
Заключение
Если вы все еще верстаете вьюхи с использованием EmptyView(), т.е. одним из первых двух моих примеров, теперь можете так не делать 😉
Как и написано в документации, вам вряд ли придется использовать EmptyView() напрямую, если вообще придется хоть когда-нибудь использовать. Тем не менее я показал сценарий, когда это может пригодиться (вычисляемая вьюха для enum).
Благодаря @ViewBuilder в ваших вычисляемых вьюхах можно почти никогда не использовать EmptyView().
Код для этой статьи можно посмотреть тут, другие статьи по разработке - тут, а про инвестиции - тут.