繁体   English   中英

如何让 SwiftUI UIViewRepresentable 视图拥抱其内容?

[英]How do I make SwiftUI UIViewRepresentable view hug its content?

我试图让 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获得 SwiftUI 的自然大小。 这适用于诸如TextImage之类的紧凑视图,或包含此类紧凑视图的容器。 但是,我不确定GeometryReader会发生什么。

考虑以下视图 controller:

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
    }
}

这里没有什么特别有趣的。 我们关闭了UIView和 SwiftUI 的托管UIViewUIView调整掩码。 我们强制我们的视图具有与 SwiftUI 相同的自然大小。

稍后我将讨论更新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)
        }
    }
}

神奇的是,一切正常。 您可以更改水平 alignment 或向视图 controller 添加更多字符,并期望正确的布局。

需要注意的一点:

  1. 您必须指定.fixedSize()以使用自然大小。 否则视图 controller 会占用尽可能多的空间,就像 GeometryReader() 一样。 鉴于.fixedSize()的文档,这可能不足为奇,但命名很糟糕。 我永远不会期望.fixedSize()是这个意思。
  2. 您必须设置preferredContentSize并保持更新。 最初我发现它对我的代码没有视觉影响,但是水平的 alignment 似乎是错误的,因为VCRep总是将其前导锚用作VStack的布局指南。 通过.alignmentGuide检查视图尺寸后,发现VCRep的大小为零。 它仍然显示! 由于默认情况下不启用任何内容剪辑!

顺便说一句,如果您遇到视图高度取决于其宽度的情况,并且您很幸运能够获得宽度(通过GeometryReader ),您也可以尝试直接在 SwiftUI 视图的body中自己进行布局,并将您的 UIView 附加为.background以用作自定义画家。 例如,如果您使用 TextKit 来计算文本布局,则此方法有效。

请尝试下面的代码它会帮助你

// **** For getting label height ****
struct LabelInfo {

   func heightForLabel(text: String, font: UIFont, width: CGFloat) -> CGFloat {

       let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
       label.numberOfLines = 0
       label.lineBreakMode = NSLineBreakMode.byWordWrapping
       label.font = font
       label.text = text
       label.sizeToFit()
       return label.frame.height
   }
}

改变例子

struct Example: View {

   // Note : Width is fixed 200 at both place
   //      : use same font at both 

   let height = LabelInfo().heightForLabel(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.", font: UIFont.systemFont(ofSize: 17), width: 200) 

   var body: some View {
       VStack {
           Text("Hello")
           TestView().frame(width: 200, height: height, alignment: .center) //You need to do change here
       }
       .background(Color.red)
   }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM