Home > database >  How to treat if-let-else as a single view in SwiftUI?
How to treat if-let-else as a single view in SwiftUI?

Time:06-08

I'm trying to write an extension which adds a caption (I called it statusBar) right below it, no matter what type the caption is (e.g. Text, Image, Link...). So I tried the code below. But Xcode said an error at the line before .statusBar that Type '()' cannot conform to 'View'. You can find I added a comment in the code below.

I know there must be something wrong within my .statusBar, because the error disappeared when I replaced my if-let-else block with a single view (e.g. Text("Hello, world!")). But I still want to display different contents based on that if-let statement. So how can I do with my code to solve this?


// .statusBar extension

struct StatusBarView: ViewModifier {
    
    let statusBar: AnyView
    
    init<V: View>(statusBar: () -> V) {
        self.statusBar = AnyView(statusBar())
    }
    
    func body(content: Content) -> some View {
        VStack(spacing: 0) {
            content
            statusBar
        }
    }
}

extension View {
    func statusBar<V: View>(statusBar: () -> V) -> some View {
        self.modifier(StatusBarView(statusBar: statusBar))
    }
}

// Inside main app view

Image(systemName: "link")
    .font(.system(size: 48))      // Xcode error: Type '()' cannot conform to 'View'
    .statusBar {
        //
        // If I change below if-let-else to a single view
        // (e.g. Text("Hello, world!"))
        // Then it works.
        //
        if let url = mediaManager.url {
            Text(url.path)
        } else {
            Text("No media loaded.")
        }
    }

CodePudding user response:

Make it closure argument a view builder, like

extension View {
    func statusBar<V: View>(@ViewBuilder statusBar: () -> V) -> some View {
        self.modifier(StatusBarView(statusBar: statusBar))
    }
}

the same can be done in init of modifier, but not required specifically for this case of usage.

Tested with Xcode 13.4 / iOS 15.5

CodePudding user response:

Wrap if-else into Group. Example:

Image(systemName: "link")
.font(.system(size: 48))
.statusBar {
     Group {
         if let url = mediaManager.url {
             Text(url.path)
         } else {
             Text("No media loaded.")
         }
     }
}

CodePudding user response:

You can also do like this

    Image(systemName: "link")
        .font(.system(size: 48))
        .statusBar {
            
            ZStack {
                if let url = mediaManager.url {
                    Text(url.path)
                } else {
                    Text("No media loaded.")
                }
                
            }
        }
  • Related