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

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

Asked 10 months ago
2
4 answers

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) }
    }
}

Source: link

0

Here is a way for you:

struct ContentView: View {
    var body: some View {
        
        VStack(spacing: 20.0) {
            
            HelpView("Learn more about", url: "https://example.com/help")
            
            HelpView("https://Apple.com/help", label: { Text("find my iPhone") })
            
            HelpView("https://google.com/help", label: { Image(systemName: "globe") })
            
        }

    }
}

struct HelpView<Content>: View where Content: View {
    
    let url: String
    var label: () -> Content
    @State private var showWebView = false

    init(_ url: String, label: @escaping () -> Content) {
        self.url = url
        self.label = label
    }
    
    init(_ text: String, url: String) where Content == Text {
        
        self.init(url, label: { Text(text) })
    }

    var body: some View {
        
        Button { showWebView.toggle() } label: { label() }
            .sheet(isPresented: $showWebView) {
                webView
            }
        
    }
    
    var webView: some View {
        return Text(url)
    }
}

Source: link

0

Consider the following example code:
function init() {
  var name = 'Mozilla'; // name is a local variable created by init
  function displayName() { // displayName() is the inner function, a closure
    alert(name); // use variable declared in the parent function
  }
  displayName();
}
init();
Consider the following code example:
function makeFunc() {
  var name = 'Mozilla';
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();
Here's a slightly more interesting example—a makeAdder function:
function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12
For instance, suppose we want to add buttons to a page to adjust the text size. One way of doing this is to specify the font-size of the body element (in pixels), and then set the size of the other elements on the page (such as headers) using the relative em unit:
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
Here's the JavaScript:
function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

Source: link

0

Unable to infer complex closure return type; add explicit type to disambiguate
struct GameView: View {
    @State private var cards = [
        Card(value: 100),
        Card(value: 20),
        Card(value: 80),
    ]
    
    var body: some View {
        MyListView(items: cards) { card in // Error is on this line, at the starting curly brace
            let label = label(for: card)
            return Text(label)
        }
    }
    
    func label(for card: Card) -> String {
        return "Card with value \(card.value)"
    }
}

struct MyListView<Item: Identifiable, ItemView: View>: View {
    let items: [Item]
    let content: (Item) -> ItemView
    
    var body: some View {
        List {
            ForEach(items) { item in
                content(item)
            }
        }
    }
}

struct Card: Identifiable {
    let value: Int
    let id = UUID()
}
First, you can manually declare what function it is:
MyListView(items: cards) { (card: Card) -> Text in 
            let label = label(for: card)
            return Text(label)
 }
Using Group:
var body: some View {
        MyListView(items: cards) { card in
            Group {
                let label = label(for: card)
                Text(label)
            }
        }
    }
Using func with @ViewBuilder tag
@ViewBuilder func cardView(card: Card) -> some View {
        let label = label(for: card)
        Text(label)
    }
    
    var body: some View {
        MyListView(items: cards, content: cardView)
    }
Additionally, you can simplify the second example and not use ViewBuilder, as you can manually say you will return Text, e.g.:
func cardView(card: Card) -> Text {
        let label = label(for: card)
        return Text(label)
    }

Source: link

Recent Questions on swift

    Programming Languages