Skip to main content

How to return different view types

About 3 minSwiftSwiftUIArticle(s)bloghackingwithswift.comcrashcourseswiftswiftuixcodeappstore

How to return different view types 관련

SwiftUI by Example

Back to Home

How to return different view types | SwiftUI by Example

How to return different view types

Updated for Xcode 15

The body property of any SwiftUI automatically gets the ability to return different views thanks to a special attributed called @ViewBuilder. This is implemented using Swift's result builder system, and it understands how to present two different views depending on our app's state.

However, this same functionality isn't automatically everywhere, which means any custom properties you make must return the same view type.

There are four ways you can fix this. The first option is to wrap your output in a group, so that no matter whether you send back an image or a text view they both go back in a group:

struct ContentView: View {
    var tossResult: some View {
        Group {
            if Bool.random() {
                Image("laser-show")
                    .resizable()
                    .scaledToFit()
            } else {
                Text("Better luck next time")
                    .font(.title)
            }
        }
        .frame(width: 400, height: 300)
    }

    var body: some View {
        VStack {
            Text("Coin Flip")
                .font(.largeTitle)

            tossResult
        }
    }
}

Download this as an Xcode projectopen in new window

The second is to use a type-erased wrapper called AnyView that we can return:

struct ContentView: View {
    var tossResult: some View {
        if Bool.random() {
            return AnyView(Image("laser-show").resizable().scaledToFit())
        } else {
            return AnyView(Text("Better luck next time").font(.title))
        }
    }

    var body: some View {
        VStack {
            Text("Coin Flip")
                .font(.largeTitle)

            tossResult
                .frame(width: 400, height: 300)                
        }
    }
}

Download this as an Xcode projectopen in new window

If you haven't heard of this concept, it 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.

Although both Group and AnyView achieve the same result for our layout, between the two it's generally preferable to use Group because it's more efficient for SwiftUI.

A third option is to apply the @ViewBuilder attribute yourself to any properties that need it, like this:

struct ContentView: View {
    @ViewBuilder var tossResult: some View {
        if Bool.random() {
            Image("laser-show")
                .resizable()
                .scaledToFit()
        } else {
            Text("Better luck next time")
                .font(.title)
        }
    }

    var body: some View {
        VStack {
            Text("Coin Flip")
                .font(.largeTitle)

            tossResult
                .frame(width: 400, height: 300)                
        }
    }
}

Download this as an Xcode projectopen in new window

That works, but honestly if you find yourself reaching for @ViewBuilder you should question whether you're trying to put too much into one view.

The fourth solution, and the one that works best the majority of the time, is to break up your views into smaller views, then combine them together as needed:

struct TossResult: View {
    var body: some View {
        if Bool.random() {
            Image("laser-show")
                .resizable()
                .scaledToFit()
        } else {
            Text("Better luck next time")
                .font(.title)
        }
    }
}

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Coin Flip")
                .font(.largeTitle)

            TossResult()
                .frame(width: 400, height: 300)
        }
    }
}

Download this as an Xcode projectopen in new window

This works particularly well to help break apart logic and layout, and also has the benefit of making your views more reusable elsewhere in your app. SwiftUI will automatically collapse your view hierarchy, so there is no meaningful performance difference when you break up a view.

The text “Coin flip” over a successful outcome with lots of lasers, as well as an unsuccessful outcome with the words “Better luck next time”.
The text “Coin flip” over a successful outcome with lots of lasers, as well as an unsuccessful outcome with the words “Better luck next time”.
Similar solutions…
How to fix “Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type” | SwiftUI by Example

How to fix “Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type”
How to show different images and other views in light or dark mode | SwiftUI by Example

How to show different images and other views in light or dark mode
How to preview your layout at different Dynamic Type sizes | SwiftUI by Example

How to preview your layout at different Dynamic Type sizes
How to create different layouts using size classes | SwiftUI by Example

How to create different layouts using size classes
How to preview your layout in different devices | SwiftUI by Example

How to preview your layout in different devices

이찬희 (MarkiiimarK)
Never Stop Learning.