0

我正在尝试将 SwiftUI 再次嵌入到ViewUIKitUIViewView。它看起来像这样:

View
↓
UIView
↓
View

当前代码:

struct ContentView: View {
    var body: some View {
        Representable {
            Text("Hello world!")
        }
    }
}


struct Representable<Content: View>: UIViewRepresentable {
    private let content: () -> Content

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

    func makeUIView(context: Context) -> UIView {
        let host = UIHostingController(rootView: content())
        let hostView = host.view!

        return hostView
    }

    func updateUIView(_ uiView: UIView, context: Context) {
        uiView.backgroundColor = .systemRed
    }
}

我希望Representable只设置backgroundColor. Text它不应该更大。此外,这只是一个示例,因此这不仅仅是Text设置背景颜色。

现在 目标
现在 目标

如果文本真的很长,还有一个问题 - 它不受屏幕/父级大小的限制(在这种情况下使用拥抱优先级):

长文本

在这种情况下,我如何确保它Representable仅与内容本身一样大Text?如果文本在一行上换行,例如在限制到某个宽度时,它也应该起作用。

4

1 回答 1

0

最简单的方法是使用SwiftUI-IntrospectUIView从中获取。这是所需的所有代码:

Text("This is some really long text that will have to wrap to multiple lines")
    .introspect(selector: TargetViewSelector.siblingOfType) { target in
        target.backgroundColor = .systemRed
    }

如果视图有点复杂并且没有UIView专门针对它的 a ,您可以将它嵌入到 a 中ScrollView,因此内容现在将是 a UIView

ScrollView {
    Text("Complex content here")
}
.introspectScrollView { scrollView in
    scrollView.isScrollEnabled = false
    scrollView.clipsToBounds = false

    scrollView.subviews.first!.backgroundColor = .systemRed
}

如果您不想使用 Introspect(我强烈推荐),下面还有第二种解决方案。第二种解决方案适用于大多数情况,但不是全部


先看上面的解决方案。

我已经创建了一个有效的答案。它看起来很复杂,但它确实有效。

它基本上是通过使用内部GeometryReader测量要包装的内容的大小和外部GeometryReader测量整个容器的大小来工作的。这意味着Text现在将换行,因为它受到外部容器大小的限制。

代码:

struct ContentView: View {
    var body: some View {
        Wrapper {
            Text("This is some really long text that will have to wrap to multiple lines")
        }
    }
}
struct Wrapper<Content: View>: View {
    @State private var size: CGSize?
    @State private var outsideSize: CGSize?
    private let content: () -> Content

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

    var body: some View {
        GeometryReader { outside in
            Color.clear.preference(
                key: SizePreferenceKey.self,
                value: outside.size
            )
        }
        .onPreferenceChange(SizePreferenceKey.self) { newSize in
            outsideSize = newSize
        }
        .frame(width: size?.width, height: size?.height)
        .overlay(
            outsideSize != nil ?
                Representable {
                    content()
                        .background(
                            GeometryReader { inside in
                                Color.clear.preference(
                                    key: SizePreferenceKey.self,
                                    value: inside.size
                                )
                            }
                            .onPreferenceChange(SizePreferenceKey.self) { newSize in
                                size = newSize
                            }
                        )
                        .frame(width: outsideSize!.width, height: outsideSize!.height)
                        .fixedSize()
                        .frame(width: size?.width ?? 0, height: size?.height ?? 0)
                }
                .frame(width: size?.width ?? 0, height: size?.height ?? 0)
            : nil
        )
    }
}
struct SizePreferenceKey: PreferenceKey {
    static let defaultValue: CGSize = .zero

    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}
struct Representable<Content: View>: UIViewRepresentable {
    private let content: () -> Content

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

    func makeUIView(context: Context) -> UIView {
        let host = UIHostingController(rootView: content())
        let hostView = host.view!
        return hostView
    }

    func updateUIView(_ uiView: UIView, context: Context) {
        uiView.backgroundColor = .systemRed
    }
}

结果:

结果

另一个示例表明它确实使包装器的大小与 SwiftUI 视图完全相同:

struct ContentView: View {
    var body: some View {
        VStack {
            Wrapper {
                Text("This is some really long text that will have to wrap to multiple lines")
            }
            .border(Color.green, width: 3)

            Wrapper {
                Text("This is some really long text that will have to wrap to multiple lines. However, this bottom text is a bit longer and may wrap more lines - but this isn't a problem here")
            }
            .border(Color.blue, width: 3)
        }
    }
}

多个包装器

于 2022-02-16T02:05:27.017 回答