#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)