swift - 如何让 SwiftUI UIViewRepresentable 视图拥抱它的内容?
问题描述
我试图让 SwiftUI 识别 UIViewRepresentable 的内在大小,但它似乎将它视为框架设置为maxWidth: .infinity, maxHeight: .infinity
. 我创建了一个人为的示例,这是我的代码:
struct Example: View {
var body: some View {
VStack {
Text("Hello")
TestView()
}
.background(Color.red)
}
}
struct TestView: UIViewRepresentable {
func makeUIView(context: UIViewRepresentableContext<TestView>) -> TestUIView {
return TestUIView()
}
func updateUIView(_ uiView: TestUIView, context: UIViewRepresentableContext<TestView>) {
}
typealias UIViewType = TestUIView
}
class TestUIView: UIView {
required init?(coder: NSCoder) { fatalError("-") }
init() {
super.init(frame: .zero)
let label = UILabel()
label.text = "Sed mattis urna a ipsum fermentum, non rutrum lacus finibus. Mauris vel augue lorem. Donec malesuada non est nec fermentum. Integer at interdum nibh. Nunc nec arcu mauris. Suspendisse efficitur iaculis erat, ultrices auctor magna."
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .purple
addSubview(label)
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
widthAnchor.constraint(equalToConstant: 200),
label.leadingAnchor.constraint(equalTo: leadingAnchor),
label.trailingAnchor.constraint(equalTo: trailingAnchor),
label.topAnchor.constraint(equalTo: topAnchor),
label.bottomAnchor.constraint(equalTo: bottomAnchor),
])
}
}
struct Example_Previews: PreviewProvider {
static var previews: some View {
Example()
.previewDevice(PreviewDevice(rawValue: "iPhone 8"))
.previewLayout(PreviewLayout.fixed(width: 300, height: 300))
}
}
编辑:经过进一步检查,似乎我得到了Unable to simultaneously satisfy constraints.
,其中一个约束是'UIView-Encapsulated-Layout-Height'
对拉伸尺寸的约束。所以我想这可能有助于防止这些约束被强加于我的观点?没有把握...
解决方案
使用.fixedSize()
为我解决了问题。
我所做的使用UIViewController
,所以事情可能会有所不同UIView
。我试图实现的是做一个三明治结构,如:
SwiftUI => UIKit => SwiftUI
考虑一个简单的 SwiftUI 视图:
struct Block: View {
var text: String
init(_ text: String = "1, 2, 3") {
self.text = text
}
var body: some View {
Text(text)
.padding()
.background(Color(UIColor.systemBackground))
}
}
设置背景颜色是为了测试外部 SwiftUI 的环境值是否传递到内部 SwiftUI。一般的答案是肯定的,但如果您使用类似.popover
.
UIHostingController 的一个好处是您可以使用.intrinsicContentSize
. 这适用于诸如Text
and之类的Image
紧凑视图,或包含此类紧凑视图的容器。但是,我不确定GeometryReader
.
考虑以下视图控制器:
class VC: UIViewController {
let hostingController = UIHostingController(rootView: Block("Inside VC"))
var text: String = "" {
didSet {
hostingController.rootView = Block("Inside VC: \(text).")
}
}
override func viewDidLoad() {
addChild(hostingController)
view.addSubview(hostingController.view)
view.topAnchor.constraint(equalTo: hostingController.view.topAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: hostingController.view.bottomAnchor).isActive = true
view.leadingAnchor.constraint(equalTo: hostingController.view.leadingAnchor).isActive = true
view.trailingAnchor.constraint(equalTo: hostingController.view.trailingAnchor).isActive = true
view.translatesAutoresizingMaskIntoConstraints = false
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
}
override func viewDidLayoutSubviews() {
preferredContentSize = view.bounds.size
}
}
这里没有什么特别有趣的。我们关闭了我们和 SwiftUI 托管的自动调整掩码。我们强制我们的视图具有与 SwiftUI 相同的自然大小。UIView
UIView
稍后我会谈到更新preferredContentSize
。
一个无聊VCRep
:
struct VCRep: UIViewControllerRepresentable {
var text: String
func makeUIViewController(context: Context) -> VC {
VC()
}
func updateUIViewController(_ vc: VC, context: Context) {
vc.text = text
}
typealias UIViewControllerType = VC
}
现在来到 ContentView,即外部 SwiftUI:
struct ContentView: View {
@State var alignmentIndex = 0
@State var additionalCharacterCount = 0
var alignment: HorizontalAlignment {
[HorizontalAlignment.leading, HorizontalAlignment.center, HorizontalAlignment.trailing][alignmentIndex % 3]
}
var additionalCharacters: String {
Array(repeating: "x", count: additionalCharacterCount).joined(separator: "")
}
var body: some View {
VStack(alignment: alignment, spacing: 0) {
Button {
self.alignmentIndex += 1
} label: {
Text("Change Alignment")
}
Button {
self.additionalCharacterCount += 1
} label: {
Text("Add characters")
}
Block()
Block().environment(\.colorScheme, .dark)
VCRep(text: additionalCharacters)
.fixedSize()
.environment(\.colorScheme, .dark)
}
}
}
神奇的是,一切正常。您可以更改水平对齐方式或向视图控制器添加更多字符,并期望正确的布局。
需要注意的一点:
- 您必须指定
.fixedSize()
使用自然大小。否则视图控制器会占用尽可能多的空间,就像 GeometryReader() 一样。鉴于 's 的文档,这可能不足为奇.fixedSize()
,但命名很可怕。我从没想过会.fixedSize()
是这个意思。 - 您必须设置
preferredContentSize
并保持更新。最初我发现它对我的代码没有视觉影响,但水平对齐似乎是错误的,因为它VCRep
总是将其前导锚用作VStack
. 通过 检查视图尺寸后.alignmentGuide
,发现VCRep
尺寸为零。它仍然显示!由于默认情况下不启用任何内容剪辑!
顺便说一句,如果您遇到视图高度取决于其宽度的情况,并且您很幸运能够获得宽度(通过GeometryReader
),您也可以尝试直接在 SwiftUI 视图中自己进行布局body
,并且将您的 UIView 附加.background
为用作自定义画家。例如,如果您使用 TextKit 来计算文本布局,则此方法有效。
推荐阅读
- java - 如何将另一个 FragmentActivity 添加到 MainActivity 中?
- ios - 如何让我的图片填满整个 ScrollView?
- python - Tensorflow Speech_commands 错误:TensorSliceReader 构造函数不成功
- java - Java 生成工件失败
- typescript - 在 TypeScript 中使用错误类型时没有编译器错误
- c - 冗余或良好实践 - 结构
- r - R:如何只打印x轴上的时间而忽略日期
- vba - 使用 vba 用户表单根据 vlookup 结果更改数据
- oracle - 在使用约束创建表时需要帮助
- typescript - 无法识别量角器的配置界面