Формируем View В Swiftui Исходя Из Условий

Иногда нам нужно сформировать SwiftUI View с учетом некоторых условий.

Например, в приведенном выше коде мы определяем ГлавнаяПросмотр , который может содержать Просмотр профиля , в случае Логинманагер Есть loggedInUser .

Мы пытаемся реализовать это с помощью стандартного оператора if:

  
  
  
  
   

struct HomeView: View { @ObservedObject var loginManager: LoginManager var body: some View { VStack { if let user = loginManager.loggedInUser { ProfileView(user: user) } .

} } }

К сожалению, этот код выдаст ошибку при компиляции:
Замыкание, содержащее оператор потока управления, нельзя использовать с построителем функций ViewBuilder.
Поскольку здесь используются не обычные замыкатели, а конструкторы функций , мы не можем поместить в них произвольный код для формирования HStack или VStack. Так как же нам выйти из этой ситуации? Один из способов — перенести обработку таких опционалов непосредственно в создаваемые нами представления.

Например, мы можем передать в наш ProfileView не конкретное значение User, а сделать его необязательным:

struct ProfileView: View { var user: User? var body: some View { guard let user = user else { // We have to use 'AnyView' to perform type erasure here, // in order to give our 'body' a single return type: return AnyView(EmptyView()) } return AnyView(VStack { Text(user.name) .

}) } }

Этот код работает, но он не особенно красив.

Нет смысла создавать ProfileView для пользователя nil. Давайте воспользуемся другим подходом: мы используем карта нашему дополнительному пользователю, чтобы преобразовать его в ProfileView:

struct HomeView: View { @ObservedObject var loginManager: LoginManager var body: some View { VStack { loginManager.loggedInUser.map { user in ProfileView(user: user) } .

} } }

Это намного удобнее: нам не нужно вручную указывать EmptyView, если у User нет значения.

Мы также можем снова передать в ProfileView конкретное значение, а не необязательное.

Можно ли сделать еще лучше? Хорошие новости о @ViewBuilder заключается в том, что это не какая-то проприетарная реализация в SwiftUI, а общедоступный атрибут, с помощью которого мы можем аннотировать наши собственные функции и замыкания.

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

@ViewBuilder Замыкание для преобразования ненулевого значения в представление:

struct Unwrap<Value, Content: View>: View { private let value: Value? private let contentProvider: (Value) -> Content init(_ value: Value?, @ViewBuilder content: @escaping (Value) -> Content) { self.value = value self.contentProvider = content } var body: some View { value.map(contentProvider) } }

Используя эту конструкцию, мы теперь можем полностью перепроектировать всю структуру HomeView:

struct HomeView: View { @ObservedObject var loginManager: LoginManager var body: some View { VStack { Unwrap(loginManager.loggedInUser) { user in HStack { Text("Logged in as:") ProfileView(user: user) } } .

} } }

Теги: #Разработка для iOS #Разработка для MacOS #Swift #swiftUI #swift. дополнительные виды

Вместе с данным постом часто просматривают: