无法使 UIViewRepresentable 的 CustomView 在 SwiftUI 中正常工作

Posted

技术标签:

【中文标题】无法使 UIViewRepresentable 的 CustomView 在 SwiftUI 中正常工作【英文标题】:Cannot make CustomView for UIViewRepresentable to work correctly in SwiftUI 【发布时间】:2021-04-09 09:08:47 【问题描述】:

我有这个 SwiftUI 组件

var body: some View 
    let image = TypographyImage.image(for: typography, kind: kind)
    let width = image.size.width + 4
    let height = TypographyImage.height(for: typography) * (kind == .oneLine ? 1.0 : 2.0)

    ScrollView(.horizontal) 
        HStack 
            Image(uiImage: image)
                    .frame(width: width, height: height, alignment: Alignment(horizontal: .center, vertical: .top))
                    .offset(y: TypographyImage.offset(for: typography))
                    .background(Color.red)
            if isSwiftUI 
                Text(text)
                        .typography(typography, theme: AmityTheme.shared)
                        .background(Color.red)
             else 
                MyUILabel(text: text, typography: typography, theme: AmityTheme.shared).background(Color.red)
            
        
    

显示此 UI

请注意,在MyUILabel 中,我应用了背景红色。 HStack 内的 MyUILabel 的背景颜色和对齐方式在 MyUILabel 返回 UILabel 作为第一个子视图时正确显示

如下图

struct MyUILabel: UIViewRepresentable 
    var text: String
    var typography: AmityTheme.Typography
    var theme: AmityTheme

    func makeUIView(context: Context) -> UILabel 
        let view = UILabel(frame: .zero)
        view.translatesAutoresizingMaskIntoConstraints = false
        view.setContentHuggingPriority(.defaultHigh, for: .vertical)
        view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        updateUIView(view, context: context)
        return view
    

    func updateUIView(_ view: UILabel, context: Context) 
        view.attributedText = NSAttributedString(string: text, attributes: theme.textAttributes(for: typography, palette: .black))
    

但是,如果我将其更改为返回我的自定义视图,例如

struct MyUILabel: UIViewRepresentable 
    var text: String
    var typography: AmityTheme.Typography
    var theme: AmityTheme

    func makeUIView(context: Context) -> PaddedUILabel 
        let view = PaddedUILabel(frame: .zero)
        view.translatesAutoresizingMaskIntoConstraints = false
        view.setContentHuggingPriority(.defaultHigh, for: .vertical)
        view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        updateUIView(view, context: context)
        return view
    

    func updateUIView(_ view: PaddedUILabel, context: Context) 
        view.update(text: text, typography: typography, theme: theme)
    


class PaddedUILabel: UIView 
    private var label: UILabel!
    private var topConstraint: NSLayoutConstraint!
    private var bottomConstraint: NSLayoutConstraint!

    override init(frame: CGRect) 
        super.init(frame: frame)
        commonInit()
    

    required init?(coder: NSCoder) 
        super.init(coder: coder)
        commonInit()
    

    private func commonInit() 
        let label = UILabel(frame: .zero)
        label.translatesAutoresizingMaskIntoConstraints = false
        label.numberOfLines = 0
        label.setContentHuggingPriority(.defaultHigh, for: .vertical)
        label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        self.label = label

        addSubview(label)

        topConstraint = label.topAnchor.constraint(equalTo: topAnchor)
        bottomConstraint = label.bottomAnchor.constraint(equalTo: bottomAnchor)
        let constraints: [NSLayoutConstraint] = [
            label.leadingAnchor.constraint(equalTo: leadingAnchor),
            label.trailingAnchor.constraint(equalTo: trailingAnchor),
            topConstraint,
            bottomConstraint
        ]
        NSLayoutConstraint.activate(constraints)
    

    func update(text: String, typography: AmityTheme.Typography, theme: AmityTheme) 
        let attributes = theme.textAttributes(for: typography, palette: .black)
        label.attributedText = NSAttributedString(string: text, attributes: attributes)
    

结果是

背景颜色消失了,HStack 中心垂直对齐不被尊重。我似乎不知道为什么会这样。

谁能建议我如何解决这个问题?

谢谢

【问题讨论】:

为什么要使用自定义视图 PaddedUILabel (UILabel)? 因为这是我们应用程序的一种中心框架,我必须确保我们的 NSAttributedString 主题在 SwiftUI 和 UIKit 上都能正常工作。 PaddedUILabel 将在框架端实现,以使显示的文本与设计者的预期完全相同(NSAttributedString 的 key 属性是不够的)。这就是PaddedUILabel的原因 【参考方案1】:

似乎在CustomView 中布局任何约束并使其UIViewRepresentable 根据Here 将不起作用

在 SwiftUI 中,父母问孩子:你想要多大的尺寸?孩子告诉父母,父母设定尺寸。完毕。 然而,AutoLayout 需要不止一次通过才能确定完整的布局。它根本没有机会。

我对 UIViewRepresentable 的经验法则是:让它超级简单,只包装一个视图。不止一个视图要求布局问题。

另外this 指出 SwiftUI 布局的工作方式存在限制,与原版布局约束和intrinsincContentSize 不兼容

【讨论】:

以上是关于无法使 UIViewRepresentable 的 CustomView 在 SwiftUI 中正常工作的主要内容,如果未能解决你的问题,请参考以下文章

如何使 UIViewRepresentable @ViewBuilder 与动态内容一起使用?

无法使用 UIViewRepresentable 和 UIDocumentPicker 选择文件夹

SwiftUI MapKit UIViewRepresentable 无法显示警报

无法将点击手势识别器添加到 SwiftUI MKMapView UIViewRepresentable

如何在 SwiftUI 中使 UIViewRepresentable 在 tvOS 上具有焦点?

更新 UIViewRepresentable 中的绑定