ios SwiftUI(如果允许在View中)

yh2wf1be  于 2022-11-19  发布在  iOS
关注(0)|答案(3)|浏览(210)

我需要在View中使用if let语句。

@ObservedObject var person: Person?
 var body: some View {
      if person != nil {
        // this works
      }
      if let p = person {
        // Compiler error
      }
    }

包含控制流语句的闭包不能与函数生成器“ViewBuilder”一起使用

u1ehiz5o

u1ehiz5o1#

是的,代码快照中要求的符号是不允许的,至少目前是这样,但通过以下非常简单的方法-将控制流提取到函数中,可以实现预期的结果:

var person: Person? // actually @ObservedObject does not allowed optional
var body: some View {
    VStack {
         if person != nil {
           // same as before
         }
         personViewIfExists() // << just extract it in helper function
    }
}

private func personViewIfExists() -> some View { // generates view conditionally
    if let p = person {
      return ExistedPersonView(person: p) // << just for demo
    }
}

在某些情况下,可能还需要以下函数变量(尽管它产生相同的结果)

private func personViewIfExists() -> some View {
    if let p = person {
      return AnyView(ExistedPersonView(person: p))
    }
    return AnyView(EmptyView())
}
2q5ifsrm

2q5ifsrm2#

  • 2022 年 更新 *

您 可以 使用 普通 Swift if let

if let pieceOfData = pieceOfData {
  // now it's guaranteed
} else {
  // now it's not
}

中 的 每 一 个

  • 上 一 个 答案 *

或者 您 可以 创建 自己 的 IfLet 视图 构建 器 :

import SwiftUI

struct IfLet<Value, Content, NilContent>: View where Content: View, NilContent: View {

    let value: Value?
    let contentBuilder: (Value) -> Content
    let nilContentBuilder: () -> NilContent

    init(_ optionalValue: Value?, @ViewBuilder whenPresent contentBuilder: @escaping (Value) -> Content, @ViewBuilder whenNil nilContentBuilder: @escaping () -> NilContent) {
        self.value = optionalValue
        self.contentBuilder = contentBuilder
        self.nilContentBuilder = nilContentBuilder
    }

    var body: some View {
        Group {
            if value != nil {
                contentBuilder(value!)
            } else {
                nilContentBuilder()
            }
        }
    }
}

extension IfLet where NilContent == EmptyView {

    init(_ optionalValue: Value?, @ViewBuilder whenPresent contentBuilder: @escaping (Value) -> Content) {
        self.init(optionalValue, whenPresent: contentBuilder, whenNil: { EmptyView() })
    }
}

格式
现在 , 您 可以 使用 此 工具 执行 以下 操作 :

var body: some View {
    IfLet(pieceOfData) { realData in
        // realData is no longer optional
    }
}

格式
如果 您 的 可选 选项 是 nil , 是否 需要 回应 ?

var body: some View {
    IfLet(pieceOfData, whenPresent: { realData in
        // realData is no longer optional
        DataView(realData)
    }, whenNil: {
        EmptyDataView()
    })
}

格式

ryevplcw

ryevplcw3#

更简单的方法是使用.map()
如果user有值,它将显示UserProfile,如果user为nil,则不显示任何内容:

var user: User? 
var body: some View {
  user.map { UserProfile(user: $0) }
}

如果你想为nil值提供一个默认的视图,你可以只使用nil-coalescing操作符:

var user: User? 
var body: some View {
  user.map { UserProfile(user: $0) } ?? UserProfile(user: .default) 
}

然而,这要求两个视图是相同类型的,实际上,如果它们是相同类型的,最好只写UserProfile(user: $0 ?? .default)
有趣的是,两个视图的类型不同。您可以通过将它们 Package 在AnyView中来删除它们的类型,但在这一点上确实有点麻烦和难以阅读:

var user: User? 
var body: some View {
  user.map { AnyView(UserProfile(user: $0)) } ?? AnyView(Text("Not logged in"))
}

因此,在本例中,我首选的方法是使用一个自定义的IfLet结构体(与Procrastin 8的名称相同,但实现和语法不同),它允许我编写类似于下面这样的内容,我发现它的意图非常明确,而且易于键入。

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)
    }
  }
}

好好享受吧!

相关问题