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

Ric*_*hiy 6 initializer ios swift swiftui viewbuilder

代码:

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

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

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

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

小智 6

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

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

另外,将您的代码更改为

private let content: () -> Content

public init(@ViewBuilder content: @escaping () -> Content) {
  self.content = content
}
Run Code Online (Sandbox Code Playgroud)

这样您就不必将结果包装content在另一个闭包中。

VStack(alignment: .leading, spacing: 4, content: content)
Run Code Online (Sandbox Code Playgroud)


Geo*_*e_E 0

您可以指定 的类型Content

代码:

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

    /* ... */
}
Run Code Online (Sandbox Code Playgroud)

这里唯一的区别是和强制where转换init为 内的类型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)
    }
}
Run Code Online (Sandbox Code Playgroud)