4

I'm trying return View based selected menu item on function. But it throws error:

Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements.

There my code:

enum MenuItem {
  case Main
  case Report
}
struct Menu: View {
  @State var activeItem: MenuItem = .Main

  private func getActiveView() -> View {
    switch activeItem {
      case .Main:
        return DashboardView()
      case .Report:
        return ReportView()
    }
  }

  var body: some View {
    ...
    getActiveView()
    ...
  }
}

struct DashboardView: View {
    var body: some View {
        Text("Contact")
    }
}
struct ReportView: View {
    var body: some View {
        Text("Contact")
    }
}

Im new on SwiftUI. Any ideas how to return View?

4
  • You are not sufficiently explaining your situation. What is activeItem? Where is the body? What do you mean by returning DashboardView or ReportView? Commented Jun 28, 2020 at 12:47
  • @ElTomato I don't think those are relevant to the question. Why do you think they are relevant? OP just wants to return two different views. Commented Jun 28, 2020 at 12:50
  • @ElTomato Updated code. Based on menu I should show View Commented Jun 28, 2020 at 12:51
  • @Sweeper he didn't have enum, initially. Also, are you suggesting that you don't need the body clause in SwiftUI? Commented Jun 28, 2020 at 12:52

2 Answers 2

6

SwiftUI 2

Here is a solution tested with Xcode 12b / iOS 14

struct Menu: View {
    @State var activeItem: MenuItem = .Main

    // make function ViewBuilder
    @ViewBuilder
    private func getActiveView() -> some View {
        switch activeItem {
        case .Main:
            DashboardView()     // don't use 'return' as it disables ViewBuilder
        case .Report:
            ReportView()
        }
    }

    var body: some View {
        getActiveView()
    }
}
Sign up to request clarification or add additional context in comments.

Comments

4

SwiftUI gives us a type-erased wrapper called AnyView that we can return.

Tested Solution:

struct Menu: View {
    @State var activeItem: MenuItem = .Main
    
    func getActiveView() ->  some View {
        switch activeItem {
        case .Main:
            return AnyView(DashboardView())
        case .Report:
            return AnyView(ReportView())
        }
    }
    
    var body: some View {
        getActiveView()
    }
}

Note: type-erased wrapper effectively forces Swift to forget about what specific type is inside the AnyView, allowing them to look like they are the same thing. This has a performance cost, though, so don’t use it often.

For more information you can refer to the this cool article: https://www.hackingwithswift.com/quick-start/swiftui/how-to-return-different-view-types

3 Comments

I would try not to use AnyView. AnyView takes away essential static type information about the view tree that otherwise helps SwiftUI perform efficient updates. This means that SwiftUI now has more work to do to compare the differences between what has changed and what hasn't. The article that you like to even suggests that you should use Group over using AnyView, as Group is a ViewBuilder.
@Andrew I agree with you there is performance cost involved but it solve the issue without extra efforts.
But you can use a the ViewBuilder syntax which results in more readable code without the performance cost.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.