最简单的方法是使用SwiftUI-Introspect并UIView
从中获取。这是所需的所有代码:
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)
}
}
}