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

Set custom UIView frame in UIViewRepresentable SwiftUI

我正在尝试使用 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)
    }
}

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

如果MyView有正确的内部布局(只依赖于自己的bounds),则不需要外部额外限制,即

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 布局有缺陷。

如果 Asperi 的回答不适合您,那么可能就像他们所说的那样:内部 MyView 布局存在缺陷。

要解决这个问题,您有两种选择:

选项 A. 在 viewDidLoad

中使用自动布局约束
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中有多种布局方法。这里它们的顺序是从 oldest/worst 到 newest/best。这个顺序当然是自以为是的:
    • Frame-Based布局。手动操作 UIView 上的 framebounds 属性。
    • Auto-Resizing 面具。在引擎盖下,autoResizingMask 使用古老的弹簧和 struts。参见 autoResizingMask, this answer, and this article
    • AutoLayout Constraints.
    • AutoLayout StackView.
    • SwiftUI 的 VStackHStack!这仅适用于 SwiftUI,以上所有仅适用于 UIKit。

可能应该从中制作一篇小的开发文章。