August 22, 2024

81. Когда нужен и не нужен EmptyView()

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

Экран для примера

Для состояния "в ожидании" справа от цифры используется EmptyView() с габаритами 1000 * 1000

В двух случаях из трех для состояния "в ожидании" используется 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().

Код для этой статьи можно посмотреть тут, другие статьи по разработке - тут, а про инвестиции - тут.