Более простой подход - просто использовать .map()
Он покажет UserProfile
, если user
имеет значение, и ничего, если user
равно nil:
var user: User?
var body: some View {
user.map { UserProfile(user: $0) }
}
Если вы хотите предоставить представление по умолчанию для значения nil, вы можете просто использовать оператор объединения nil:
var user: User?
var body: some View {
user.map { UserProfile(user: $0) } ?? UserProfile(user: .default)
}
Однако для этого требуется, чтобы два представления были одного типа, и на практике, если они того же типа, что вам лучше просто написать UserProfile(user: $0 ?? .default)
.
Интересен случай, когда два вида не относятся к одному и тому же типу. Вы можете просто стереть их тип, заключив их в AnyView
, но на этом этапе это становится немного громоздким и трудным для чтения:
var user: User?
var body: some View {
user.map { AnyView(UserProfile(user: $0)) } ?? AnyView(Text("Not logged in"))
}
Так что в этом случае мой предпочтительный подход заключается в использовании custom IfLet
struct (то же имя, что и у Procrastin8, но с другой реализацией и синтаксисом), которая позволяет мне писать что-то вроде этого, что я нахожу очень ясным в намерениях и легко набираемым.
var user: User?
var body: some View {
IfLet(user) { UserProfile(user: $0) }
.else { Text("Not logged in") }
}
Код для моего IfLet
компонента следующий.
struct IfLet<Wrapped, IfContent> : View where IfContent: View {
let optionalValue: Wrapped?
let contentBuilder: (Wrapped) -> IfContent
init(_ optionalValue: Wrapped?, @ViewBuilder contentBuilder: @escaping (Wrapped) -> IfContent) {
self.optionalValue = optionalValue
self.contentBuilder = contentBuilder
}
var body: some View {
optionalValue.map { contentBuilder($0) }
}
func `else`<ElseContent:View>(@ViewBuilder contentBuilder: @escaping () -> ElseContent) -> some View {
if optionalValue == nil {
return AnyView(contentBuilder())
} else {
return AnyView(self)
}
}
}
Наслаждайтесь!