UIHostingController 大小太大
UIHostingController size too big
我正在尝试再次在 View
中将 SwiftUI View
嵌入到 UIKit UIView
中。它看起来像这样:
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
只设置 Text
的 backgroundColor
。应该不会再大了。另外,这只是一个例子,所以这不仅仅是 Text
和设置背景颜色。
Now
Aim
如果文本真的很长,也会有一个问题 - 它不受屏幕大小的限制/parent(在这种情况下使用拥抱优先级):
如何确保 Representable
仅与内容本身一样大,在本例中为 Text
?如果文本被限制在一定宽度时换行,它也应该工作。
最简单的方法是使用 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
,您可以将它嵌入 ScrollView
,这样内容现在将是 UIView
:
ScrollView {
Text("Complex content here")
}
.introspectScrollView { scrollView in
scrollView.isScrollEnabled = false
scrollView.clipsToBounds = false
scrollView.subviews.first!.backgroundColor = .systemRed
}
如果您不想使用 Introspect(我会高度推荐),下面有第二种解决方案。第二种解决方案适用于大多数情况,但不适用于 all.
先看上面的解决方法
我创建了一个有效的答案。它看起来很复杂,但它确实有效。
它的工作原理基本上是使用内部 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)
}
}
}
我正在尝试再次在 View
中将 SwiftUI View
嵌入到 UIKit UIView
中。它看起来像这样:
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
只设置 Text
的 backgroundColor
。应该不会再大了。另外,这只是一个例子,所以这不仅仅是 Text
和设置背景颜色。
Now | Aim |
---|---|
如果文本真的很长,也会有一个问题 - 它不受屏幕大小的限制/parent(在这种情况下使用拥抱优先级):
如何确保 Representable
仅与内容本身一样大,在本例中为 Text
?如果文本被限制在一定宽度时换行,它也应该工作。
最简单的方法是使用 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
,您可以将它嵌入 ScrollView
,这样内容现在将是 UIView
:
ScrollView {
Text("Complex content here")
}
.introspectScrollView { scrollView in
scrollView.isScrollEnabled = false
scrollView.clipsToBounds = false
scrollView.subviews.first!.backgroundColor = .systemRed
}
如果您不想使用 Introspect(我会高度推荐),下面有第二种解决方案。第二种解决方案适用于大多数情况,但不适用于 all.
先看上面的解决方法
我创建了一个有效的答案。它看起来很复杂,但它确实有效。
它的工作原理基本上是使用内部 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)
}
}
}