In SwiftUI, the Button struct declaration is this:
struct Button<Label> where Label : View
Two of Button’s init’s read as follows:
init<S>(_ title: S, action: @escaping () -> Void) where S : StringProtocol
and
init(action: @escaping () -> Void, label: () -> Label)
I’m trying to create a similar struct that can take either creates a Label : View from a String passed in the init, or passes a closure that produces a Label in the init.
Here’s what I’ve tried:
import SwiftUI
struct ContentView: View {
var body: some View {
HelpView("Learn more about locked and unlocked tracks",
url: "https://example.com/help")
}
}
struct HelpView<Label>: View where Label : View {
let url: String
var label: () -> Label
init<S>(_ text: S, url: String) where S : StringProtocol {
self.url = url
self.label = { Text(text) }
}
init(url: String, label: @escaping () -> Label) {
self.url = url
self.label = label
}
@State private var showWebView = false
var body: some View {
Button {
showWebView = true
} label: {
label()
}
.sheet(isPresented: $showWebView) {
//WebView(url: URL(string: url)!)
}
}
}
This doesn’t compile as I get
Generic parameter ‘Label’ could not be inferred
at the call site and
Cannot assign value of type ‘Text’ to type ‘Label’
in the StringProtocol init.
How can I make this work?
>Solution :
Just put init with string into separated extension with specifier (same as Apple does for Button)
Here is fixed code. Tested with Xcode 13.2 / iOS 15.2
struct HelpView<Label>: View where Label : View {
let url: String
var label: () -> Label
init(url: String, label: @escaping () -> Label) {
self.url = url
self.label = label
}
@State private var showWebView = false
var body: some View {
Button {
showWebView = true
} label: {
label()
}
.sheet(isPresented: $showWebView) {
//WebView(url: URL(string: url)!)
}
}
}
extension HelpView where Label == Text {
init<S>(_ text: S, url: String) where S : StringProtocol {
self.url = url
self.label = { Text(text) }
}
}