Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How can I create a View type that can accept a String or a View producing closure for it's init, similar to Button?

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

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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) }
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading