我一直在尝试使用SwiftUI复制我的应用程序。它有一个RootViewController,根据枚举值,它显示了一个不同的子视图控制器。与在SwiftUI中一样,我们使用视图而不是视图控制器,我的代码如下所示:
struct RootView : View {
@State var containedView: ContainedView = .home
var body: some View {
// custom header goes here
switch containedView {
case .home: HomeView()
case .categories: CategoriesView()
...
}
}
}
不幸的是,我收到警告:
包含封闭流的控制流语句不能与函数生成器
ViewBuilder
一起使用。
那么,是否有其他选择可以切换,以便我可以复制此行为?
答案 0 :(得分:16)
更新:SwiftUI 2现在在函数构建器https://github.com/apple/swift/pull/30174
中包括对switch语句的支持。在尼古拉的答案中,它编译了开关,但无法处理转换,这是他的示例的一个版本,它确实支持转换。
struct RootView: View {
@State var containedViewType: ContainedViewType = .home
var body: some View {
VStack {
// custom header goes here
containedView()
}
}
func containedView() -> some View {
switch containedViewType {
case .home: return AnyView(HomeView()).id("HomeView")
case .categories: return AnyView(CategoriesView()).id("CategoriesView")
...
}
}
请注意已添加到每个AnyView的id(...)
。这样,SwiftUI可以在其视图层次结构中识别该视图,从而可以正确地应用过渡动画。
答案 1 :(得分:6)
谢谢大家的回答。我在 Apple开发论坛中找到了一种解决方案。
基尔·吉拉德回答。解决方案是按照Lu_,Linus和Mo的建议在函数中提取开关,但是我们必须将视图包装在AnyView
中才能起作用–像这样:
struct RootView: View {
@State var containedViewType: ContainedViewType = .home
var body: some View {
VStack {
// custom header goes here
containedView()
}
}
func containedView() -> AnyView {
switch containedViewType {
case .home: return AnyView(HomeView())
case .categories: return AnyView(CategoriesView())
...
}
}
答案 2 :(得分:3)
您必须将代码包装在View中,例如VStack
或Group
:
var body: some View {
Group {
switch containedView {
case .home: HomeView()
case .categories: CategoriesView()
...
}
}
}
或者,添加返回值应该可以:
var body: some View {
switch containedView {
case .home: return HomeView()
case .categories: return CategoriesView()
...
}
}
但是,解决此问题的最佳实践方法是创建一个返回视图的方法:
func nextView(for containedView: YourViewEnum) -> some View {
switch containedView {
case .home: return HomeView()
case .categories: return CategoriesView()
...
}
}
var body: some View {
nextView(for: containedView)
}
答案 3 :(得分:2)
如果您指定ViewBuilder
的返回类型,则似乎不需要将switch语句提取到单独的函数中。例如:
Group { () -> Text in
switch status {
case .on:
return Text("On")
case .off:
return Text("Off")
}
}
注意:如果将任意视图类型包装在
AnyView
中,并将其指定为返回类型,则还可以返回任意视图类型。
答案 4 :(得分:1)
您可以使用包装视图
struct MakeView: View {
let make: () -> AnyView
var body: some View {
make()
}
}
struct UseMakeView: View {
let animal: Animal = .cat
var body: some View {
MakeView {
switch self.animal {
case .cat:
return Text("cat").erase()
case .dog:
return Text("dog").erase()
case .mouse:
return Text("mouse").erase()
}
}
}
}
答案 5 :(得分:0)
不使用AnyView()。我将使用一堆if语句,并在我的Enum中实现Equatable和CustomStringConvertible协议以检索关联的值:
var body: some View {
ZStack {
Color("background1")
.edgesIgnoringSafeArea(.all)
.onAppear { self.viewModel.send(event: .onAppear) }
// You can use viewModel.state == .loading as well if your don't have
// associated values
if viewModel.state.description == "loading" {
LoadingContentView()
} else if viewModel.state.description == "idle" {
IdleContentView()
} else if viewModel.state.description == "loaded" {
LoadedContentView(list: viewModel.state.value as! [AnimeItem])
} else if viewModel.state.description == "error" {
ErrorContentView(error: viewModel.state.value as! Error)
}
}
}
然后我将使用一个结构分开我的观点:
struct ErrorContentView: View {
var error: Error
var body: some View {
VStack {
Image("error")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100)
Text(error.localizedDescription)
}
}
}
答案 6 :(得分:0)
您可以将 enum 与 sed
一起使用,如下...
清除枚举
@ViewBuilder
现在在查看文件中
enum Destination: CaseIterable, Identifiable {
case restaurants
case profile
var id: String { return title }
var title: String {
switch self {
case .restaurants: return "Restaurants"
case .profile: return "Profile"
}
}
}
如果你想在 NavigationLink 中使用相同的 case ... 你可以使用它如下
struct ContentView: View {
@State private var selectedDestination: Destination? = .restaurants
var body: some View {
NavigationView {
view(for: selectedDestination)
}
}
@ViewBuilder
func view(for destination: Destination?) -> some View {
switch destination {
case .some(.restaurants):
CategoriesView()
case .some(.profile):
ProfileView()
default:
EmptyView()
}
}
}
答案 7 :(得分:0)
在 default
中提供 switch
语句为我解决了这个问题:
struct RootView : View {
@State var containedView: ContainedView = .home
var body: some View {
// custom header goes here
switch containedView {
case .home: HomeView()
case .categories: CategoriesView()
...
default: EmptyView()
}
}
}