Swift: добавление элемента SwiftUI в проект UIKit: не удается правильно применить ограничения

#swift #swiftui #autolayout #constraints #dropdown

#swift #swiftui #автоматическое описание #ограничения #выпадающий список

Вопрос:

Я пытаюсь добавить выпадающий элемент, созданный с использованием 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’. Когда в инициализации установлено значение «true», вместо того, чтобы

 [inputField, dataUnitLable]
 

Что я хотел бы иметь, так это:

 [inputField, controller.view]
 

(где controller — это UIHostingController, необходимый для ввода этого элемента SwiftUI. Итак, я попробовал следующее:

 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.

ПРИМЕЧАНИЕ: есть другие элементы пользовательского интерфейса, которые я мог бы попытаться использовать в качестве якорей (например, titleLabel, который является другим UILabel в этом пользовательском классе). Но независимо от того, какой элемент я пытаюсь ограничить, я получаю эту ошибку.

Ответ №1:

Вы никогда не добавляли UIHostingController в родительский ViewController

Обычно вы делаете что-то вроде этого:

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