在 UIViewRepresentable SwiftUI 中设置自定义 UIView 的框架

9

我想使用UIViewRepresentable在SwiftUI中使用自定义的UIView。我希望我的UIView与.frame()中设置的大小相同,这样我就可以像这样使用它:

MyViewRepresentable()
.frame(width: 400, height: 250, alignment: .center)

例如,我可以将帧设置为属性:
struct MyViewRepresentable: UIViewRepresentable {
    var frame: CGRect
    func makeUIView(context: Context) -> UIView {
        let myView = MyView(frame: frame)

        return view
    }
    func updateUIView(_ uiView: UIView, context: Context) {}
}

使用方法:

struct ContentView: View {
    var body: some View {
        MyViewRepresentable(frame: CGRect(x: 0, y: 0, width: 400, height: 250))
            .frame(width: 400, height: 250, alignment: .center)
    }
}

这不是一个解决方案,我想知道如何使它正确。

2个回答

13
如果 MyView 有正确的内部布局(只依赖于自身边界),则不需要外部额外的限制。
struct MyViewRepresentable: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        return MyView(frame: .zero)
    }
    func updateUIView(_ uiView: UIView, context: Context) {}
}

将恰好在 400x250 框架内呈现

struct ContentView: View {
    var body: some View {
        MyViewRepresentable()
            .frame(width: 400, height: 250, alignment: .center)
    }
}

如果不是这样,则内部MyView布局存在缺陷。


2
谢谢您指引我正确的方向!我查看了我的代码并发现我是在viewDidLoad中添加了MyView子视图,而不是在layoutSubviews中。 - IrelDev
1
谢谢@IreiDev,这对我也有帮助。 也许你可以把这个作为你自己问题的答案,这会很有帮助。 - heiko
谢谢@IrelDev,我实际上也遇到了UIViewControllerRepresentable框架大小的类似问题,并在viewDidLayoutSubviews中分配框架并使用updateUIViewController解决了我的问题! - om-ha
1
实际上,我发布了一个答案来涵盖更多细节。 - om-ha

1
如果Asperi的回答对您没有用,那么很可能是他们所说的:内部的MyView布局存在缺陷。
要解决这个问题,您有几个选择:
选项A. 在viewDidLoad中使用AutoLayout约束
override func viewDidLoad() {
    super.viewDidLoad()

    // 1. View Hierarchy
    self.addChild(self.mySubview)
    self.view.addSubview(self.mySubview.view)
    self.mySubview.didMove(toParent: self)
    
    // 2. View AutoLayout Constraints
    self.mySubview.view.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        view.leadingAnchor.constraint(equalTo: self.mySubview.view.leadingAnchor),
        view.trailingAnchor.constraint(equalTo: self.mySubview.view.trailingAnchor),
        view.topAnchor.constraint(equalTo: self.mySubview.view.topAnchor),
        view.bottomAnchor.constraint(equalTo: self.mySubview.view.bottomAnchor)
    ])
}

方案B. 在viewDidLayoutSubviews中手动设置框架

在您的UIViewController中,只需在viewDidLayoutSubviews中设置子视图的框架即可。

override func viewDidLoad() {
    super.viewDidLoad()

    // 1. Add your subviews once in `viewDidLoad`
    self.addChild(self.mySubview)
    self.view.addSubview(self.mySubview.view)
    self.mySubview.didMove(toParent: self)
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // 2. Layout your subviews `viewDidLayoutSubviews`
    // Update subview frame size
    // Must be done in `viewDidLayoutSubviews`
    // Otherwise in `viewDidLoad` the bounds equal `UIScreen.main.bounds`, even if you used explicit frame within SwiftUI and used GeometryReader to pass the `CGSize` yourself to this UIViewController!
    let mySubviewFrame = self.view.bounds
    self.mySubview.view.frame = mySubviewFrame
}

补充资源

  • iOS中基本上有多种布局方法,以下按照从最旧/最差到最新/最佳的顺序排列。当然,这种排序是有主观性的:
    • 基于Frame的布局。手动操作UIView上的framebounds属性。
    • 自动调整大小掩码。在底层,autoResizingMask使用过时的弹簧和支架。请参见autoResizingMask此答案此文章
    • AutoLayout 约束
    • AutoLayout StackView
    • SwiftUI的VStackHStack!这仅适用于SwiftUI,以上所有内容仅适用于UIKit。

可能应该将此转化为一篇小型开发文章。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接