programing

Swift에서 switch 문에 대한 대안UI View Builder 블록?

oldcodes 2023. 8. 22. 22:29
반응형

Swift에서 switch 문에 대한 대안UI View Builder 블록?

2024 2020년 6월 23일 편집: Xcode 12부터는 ViewBuilder에서 switch 및 iflet 문이 모두 지원됩니다.

스위프트를 사용하여 내 앱을 복제하려고 했습니다.UI. 열거형 값에 따라 다른 하위 보기 컨트롤러를 표시하는 RootViewController가 있습니다.스위프트에서처럼보기 컨트롤러 대신 보기를 사용하는 UI, 내 코드는 다음과 같습니다.

struct RootView : View {
   @State var containedView: ContainedView = .home

   var body: some View {
      // custom header goes here
      switch containedView {
         case .home: HomeView()
         case .categories: CategoriesView()
         ...
      }
   }
}

유감스럽게도 경고가 표시됩니다.

컨트롤 흐름 문을 포함하는 폐쇄는 함수 작성기에서 사용할 수 없습니다.

그러면 이 동작을 복제할 수 있도록 전환할 수 있는 대안이 있을까요?

⚠2024 2020년 6월 23일 편집:Xcode 12부터는 ViewBuilder에서 switch 및 iflet 문이 모두 지원됩니다.

답변 감사합니다, 여러분.저는 애플의 개발 포럼에서 해결책을 찾았습니다.킬 길라드가 답했습니다.해결책은 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())
     ... 
  }
}

업데이트: 스위프트UI 2는 이제 함수 빌더, https://github.com/apple/swift/pull/30174 에서 스위치 문을 지원합니다.


니콜라이의 대답에 덧붙여, 스위치를 컴파일하게 했지만 전환에는 작동하지 않았습니다. 여기 전환을 지원하는 그의 예 버전이 있습니다.

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

에 하십시오.id(...)각 AnyView에 추가되었습니다.이렇게 하면 Swift가 가능합니다.UI는 뷰 계층 내에서 뷰를 식별하여 전환 애니메이션을 올바르게 적용할 수 있도록 합니다.

의 반환 유형을 지정하면 별도의 함수에 스위치 문을 추출할 필요가 없는 것 같습니다.ViewBuilder예:

Group { () -> Text in
    switch status {
    case .on:
        return Text("On")
    case .off:
        return Text("Off")
    }
}

참고: 임의의 보기 유형을 다음으로 묶어서 반환할 수도 있습니다.AnyView반환 유형으로 지정합니다.

에 열거형을 사용할 수 있습니다.@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"
    }
  }
  
}

이제 보기 파일에서

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

NavigationLink에서 동일한 사례를 사용하려는 경우...다음과 같이 사용할 수 있습니다.

struct ContentView: View {
  
  @State private var selectedDestination: Destination? = .restaurants
  
  var body: some View {
    NavigationView {

      List(Destination.allCases,
           selection: $selectedDestination) { item in
        NavigationLink(destination: view(for: selectedDestination),
                       tag: item,
                       selection: $selectedDestination) {
          Text(item.title).tag(item)
        }
      }
        
    }
  }
  
  @ViewBuilder
  func view(for destination: Destination?) -> some View {
    switch destination {
    case .some(.restaurants):
      CategoriesView()
    case .some(.profile):
      ProfileView()
    default:
      EmptyView()
    }
  }
}

같이 보기로 묶어야 .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 AnyView {
    switch containedView {
        case .home: return HomeView()
        case .categories: return CategoriesView()
        ...
    }
}

var body: some View {
    nextView(for: containedView)
}

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

래퍼로 할 수 있습니다 보기

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

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

언급URL : https://stackoverflow.com/questions/56736466/alternative-to-switch-statement-in-swiftui-viewbuilder-block

반응형