Swift:将 SwiftUI 元素添加到 UIKit 项目:无法正确应用约束

Posted

技术标签:

【中文标题】Swift:将 SwiftUI 元素添加到 UIKit 项目:无法正确应用约束【英文标题】:Swift: adding SwiftUI element to UIKit project: unable to apply constraints correctly 【发布时间】:2020-12-07 16:00:37 【问题描述】:

我正在尝试将使用 SwiftUI 构建的下拉元素添加到我的 UIKit 项目中。

我有一个自定义 UIView,其中包含多个堆栈视图,包括这个:

internal lazy var valueWithDataUnitStack: UIStackView = 
    
    var stackSubviews = [inputField, dataUnitLabel]
    
    let stack = UIStackView(arrangedSubviews: stackSubviews)
    stack.axis = .horizontal
    stack.spacing = Constants.FontAttributes.valueUnitStackSpacing

    return stack
()

我已经使用 SwiftUI 构建了以下下拉列表:

import SwiftUI

@available(ios 14.0.0, *)
struct DataUnitDropDown: View 
    var units = ["ltr", "usg", "impg"]
    
    @State private var selectedDataUnit = 0
    @State private var isExpanded = false
    
    var body: some View 
        VStack(alignment: .leading, spacing: 15) 
            DisclosureGroup(units[selectedDataUnit], isExpanded: $isExpanded) 
                VStack 
                    ForEach(0 ..< units.count, id: \.self)  index in
                        Text("\(units[index])")
                            .font(.system(size: 14))
                            .padding(.all)
                            .onTapGesture 
                                self.selectedDataUnit = index
                                self.isExpanded.toggle()
                            
                        
                    
                
            
        
    

我还有一个名为“isDynamic”的变量。当它在 init 中设置为 'true' 时,而不是有

[inputField, dataUnitLable]

我想要的是:

[inputField, controller.view]

(其中控制器是注入此 SwiftUI 元素所需的 UIHostingController。所以我尝试了以下方法:

internal lazy var valueWithDataUnitStack: UIStackView = 
    
    var stackSubviews = [UIView]()
    if let isDynamic = isDynamicField, isDynamic 
                    stackSubviews = [inputField]
        if #available(iOS 14.0.0, *) 
            let controller = UIHostingController(rootView: DataUnitDropDown())
            controller.view.translatesAutoresizingMaskIntoConstraints = false
            controller.view.clipsToBounds = false

            addSubview(controller.view)
            controller.view.heightAnchor.constraint(equalToConstant: titleLabel.heightAnchor).isActive = true
            controller.view.topAnchor.constraint(equalTo: titleLabel.topAnchor).isActive = true
            stackSubviews = [inputField, controller.view]
        
     else 
        stackSubviews = [inputField, dataUnitLabel]
    
    let stack = UIStackView(arrangedSubviews: stackSubviews)
    stack.axis = .horizontal
    stack.spacing = Constants.FontAttributes.valueUnitStackSpacing

    return stack
()

所以上面的想法是我需要强制下拉保持与顶部的其他元素对齐。我还需要允许下拉菜单展开而不影响它所在的​​堆栈的高度,因此我将 clipToBounds 设置为 false。

但是,每当我尝试访问这些约束中的任何一个时,我都会崩溃:

Thread 1: "Unable to activate constraint with anchors <NSLayoutDimension:0x60000352f880 \"_TtGC7SwiftUI14_UIHostingViewV10eHandshake16DataUnitDropDown_:0x7fbbdd381960.height\"> and <NSLayoutDimension:0x60000352f8c0 \"UITextField:0x7fbbdb4c33d0.height\"> because they have no common ancestor.  Does the constraint or its anchors reference items in different view hierarchies?  That's illegal."

如何将此元素注入堆栈并利用自定义 UIView 类中的现有约束。

注意:我可以尝试将其他 UI 元素用作锚点(例如,titleLabel 是此自定义类中的另一个 UILabel)。但无论我尝试限制什么元素,我都会收到此错误。

【问题讨论】:

【参考方案1】:

您从未将 UIHostingController 添加到父 ViewController

通常你会这样做:

addChild(controller)
view.addSubview(controller.view)
controller.didMove(toParent: self)

【讨论】:

以上是关于Swift:将 SwiftUI 元素添加到 UIKit 项目:无法正确应用约束的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 中的 forEach 期间将文本字段输入添加到数组

如何为 Swift UI 的字符添加最大长度

如何在swift ui中使用autoid将带有字段的新文档添加到firebase集合

将核心数据从 Swift 迁移到 SwiftUI

SwiftUI 如何创建一个将项目添加到包含导航链接的列表的按钮

将数据从委托 Swift 类传递到 SwiftUI 结构中的 EnvironmentObject