SwiftUI View:两个不同的初始值设定项:无法转换“Text”类型的值;闭包结果类型“Content”

发布于 2025-01-12 06:22:34 字数 1311 浏览 0 评论 0原文

代码:

import SwiftUI

public struct Snackbar<Content>: View where Content: View {
    private var content: Content

// Works OK
    public init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    init(_ text: String) {
        self.init {
            Text(text) // cannot convert value of type 'Text' to closure result type 'Content'
                .font(.subheadline)
                .foregroundColor(.white)
                .multilineTextAlignment(.leading)
        }
    }

    public var body: some View {
        HStack {
            VStack(alignment: .leading, spacing: 4) {
                content
            }
            Spacer()
        }
        .frame(maxWidth: .infinity,
               minHeight: 26)
        .padding(.fullPadding)
        .background(Color.black)
        .clipShape(RoundedRectangle(cornerRadius: .defaultCornerRadius))
        .shadow(color: Color.black.opacity(0.125), radius: 4, y: 4)
        .padding()
    }
}

我收到此错误:

无法将“Text”类型的值转换为闭包结果类型“Content”

我想要实现的目标是拥有两个单独的初始值设定项,一个用于 View 类型的内容,另一个是快捷方式对于字符串,它将放置一个带有某种样式的预定义 Text 组件来代替 Content

如果 Textsome View 并且我认为它应该编译,为什么我会收到此错误。

The code:

import SwiftUI

public struct Snackbar<Content>: View where Content: View {
    private var content: Content

// Works OK
    public init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    init(_ text: String) {
        self.init {
            Text(text) // cannot convert value of type 'Text' to closure result type 'Content'
                .font(.subheadline)
                .foregroundColor(.white)
                .multilineTextAlignment(.leading)
        }
    }

    public var body: some View {
        HStack {
            VStack(alignment: .leading, spacing: 4) {
                content
            }
            Spacer()
        }
        .frame(maxWidth: .infinity,
               minHeight: 26)
        .padding(.fullPadding)
        .background(Color.black)
        .clipShape(RoundedRectangle(cornerRadius: .defaultCornerRadius))
        .shadow(color: Color.black.opacity(0.125), radius: 4, y: 4)
        .padding()
    }
}

I'm getting this error:

cannot convert value of type 'Text' to closure result type 'Content'

The goal I'm trying to achieve is to have 2 separate initializers, one for the content of type View and the other is a shortcut for a string, which will place a predefined Text component with some styling in place of Content.

Why am I getting this error if Text is some View and I think it should compile.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

夜未央樱花落 2025-01-19 06:22:34

对此的通用解决方案是提供一个在语义上等同于some View 的包装器。 AnyView 是内置的并用于此目的。

init(_ text: String) where Content == AnyView {
  self.init {
    AnyView(
      Text(text)
        .font(.subheadline)
        .foregroundColor(.white)
        .multilineTextAlignment(.leading)
    )
  }
}

另外,更改您的代码,

private let content: () -> Content

public init(@ViewBuilder content: @escaping () -> Content) {
  self.content = content
}

以便您不必将 content 的结果包装在另一个闭包中。

VStack(alignment: .leading, spacing: 4, content: content)

The general-purpose solution to this is to provide a wrapper that is semantically equivalent to some View. AnyView is built in and serves that purpose.

init(_ text: String) where Content == AnyView {
  self.init {
    AnyView(
      Text(text)
        .font(.subheadline)
        .foregroundColor(.white)
        .multilineTextAlignment(.leading)
    )
  }
}

Also, change your code to

private let content: () -> Content

public init(@ViewBuilder content: @escaping () -> Content) {
  self.content = content
}

so that you don't have to wrap the result of content in another closure.

VStack(alignment: .leading, spacing: 4, content: content)
从﹋此江山别 2025-01-19 06:22:34

您可以指定内容的类型。

代码:

public struct Snackbar<Content>: View where Content: View {
    private var content: Content

// Works OK
    public init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    init(_ text: String) where Content == ModifiedContent<Text, _EnvironmentKeyWritingModifier<TextAlignment>> {
        self.init {
            Text(text)
                .font(.subheadline)
                .foregroundColor(.white)
                .multilineTextAlignment(.leading) as! ModifiedContent<Text, _EnvironmentKeyWritingModifier<TextAlignment>>
        }
    }

    /* ... */
}

这里唯一的区别是 init 之后的 where 以及强制转换为 init 内的类型。

为了避免特定类型,您可以将其抽象为单独的视图:

init(_ text: String) where Content == ModifiedText {
    self.init {
        ModifiedText(text: text)
    }
}

/* ... */

struct ModifiedText: View {
    let text: String

    var body: some View {
        Text(text)
            .font(.subheadline)
            .foregroundColor(.white)
            .multilineTextAlignment(.leading)
    }
}

You can specify the type of Content.

Code:

public struct Snackbar<Content>: View where Content: View {
    private var content: Content

// Works OK
    public init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }

    init(_ text: String) where Content == ModifiedContent<Text, _EnvironmentKeyWritingModifier<TextAlignment>> {
        self.init {
            Text(text)
                .font(.subheadline)
                .foregroundColor(.white)
                .multilineTextAlignment(.leading) as! ModifiedContent<Text, _EnvironmentKeyWritingModifier<TextAlignment>>
        }
    }

    /* ... */
}

The only difference here is the where after the init and the force-cast to the type inside the init.

To avoid the specific type, you can abstract this into a separate view:

init(_ text: String) where Content == ModifiedText {
    self.init {
        ModifiedText(text: text)
    }
}

/* ... */

struct ModifiedText: View {
    let text: String

    var body: some View {
        Text(text)
            .font(.subheadline)
            .foregroundColor(.white)
            .multilineTextAlignment(.leading)
    }
}
独守阴晴ぅ圆缺 2025-01-19 06:22:34

一种方法是使内容可选并使用另一个文本变量并基于 nil 值显示视图。

public struct Snackbar<Content>: View where Content: View {
    private var content: Content? // <= Here
    private var text: String = "" // <= Here
    
    // Works OK
    public init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    
    init(_ text: String) {
        self.text = text // <= Here
    }
    
    public var body: some View {
        HStack {
            VStack(alignment: .leading, spacing: 4) {
                if let content = content { // <= Here
                    content
                } else {
                    Text(text)
                        .font(.subheadline)
                        .foregroundColor(.white)
                        .multilineTextAlignment(.leading)
                }
            }
            Spacer()
        }
        // Other code



您还可以使用AnyView

public struct Snackbar: View {
    private var content: AnyView // Here
    
    // Works OK
    public init<Content: View>(@ViewBuilder content: () -> Content) {
        self.content = AnyView(content()) // Here
    }
    
    init(_ text: String) {
        self.content = AnyView(Text(text)
                            .font(.subheadline)
                            .foregroundColor(.white)
                            .multilineTextAlignment(.leading)
        ) // Here
    }
    
    public var body: some View {
        HStack {
            VStack(alignment: .leading, spacing: 4) {
                self.content
            }
            Spacer()
        }

One way is to make content optional and use another text var and show view based on a nil value.

public struct Snackbar<Content>: View where Content: View {
    private var content: Content? // <= Here
    private var text: String = "" // <= Here
    
    // Works OK
    public init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    
    init(_ text: String) {
        self.text = text // <= Here
    }
    
    public var body: some View {
        HStack {
            VStack(alignment: .leading, spacing: 4) {
                if let content = content { // <= Here
                    content
                } else {
                    Text(text)
                        .font(.subheadline)
                        .foregroundColor(.white)
                        .multilineTextAlignment(.leading)
                }
            }
            Spacer()
        }
        // Other code



You can also use AnyView

public struct Snackbar: View {
    private var content: AnyView // Here
    
    // Works OK
    public init<Content: View>(@ViewBuilder content: () -> Content) {
        self.content = AnyView(content()) // Here
    }
    
    init(_ text: String) {
        self.content = AnyView(Text(text)
                            .font(.subheadline)
                            .foregroundColor(.white)
                            .multilineTextAlignment(.leading)
        ) // Here
    }
    
    public var body: some View {
        HStack {
            VStack(alignment: .leading, spacing: 4) {
                self.content
            }
            Spacer()
        }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文