问题
I'm trying to implement a dynamic view composed UI elements sent by the backend, meaning it is not known beforehand how many and which UI elements will be displayed, the server sends JSON containing the types of fields that need to be rendered.
So for example the sever sends:
- type: paragraph
- type: textfield
Then I instantiate a class I have for each of those elements and add their views as subviews to my main dynamic view:
class DynamicViewController: UIViewController {
// array of subviews, textfield is one element in this array (-> textfield not editable)
var subViews: [UIView] = []
override func viewDidLoad() {
super.viewDidLoad()
getFields(completion: { result in
for (index, element) in result!.form.enumerated() {
let initializedClass = element.getClass()
self.subViews.append(initializedClass.view)
self.addChild(initializedClass)
self.view.addSubview(self.subViews[index])
initializedClass.didMove(toParent: self)
}
})
}
}
The problem I am getting with the "textfield" element, is that the textfield doesn't seem to be editable, tapping on it does nothing.
However, when I change the code to assign the initializedClass.view
to a specific variable, it does work:
class DynamicViewController: UIViewController {
// variable specifically for the textfield's view (-> textfield is editable)
var textfieldView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
getFields(completion: { result in
for (index, element) in result!.form.enumerated() {
if (index == 0) {
let initializedClass = element.getClass()
let initializedView = initializedClass.view
self.textfieldView = initializedClass.view
self.addChild(initializedClass)
self.view.addSubview(initializedView!)
}
}
})
}
}
This second implementation works then (the picker does open), which I got from UIPickerView delegate not called in a subview.
My question is why this is happening and how I can make this work without having to declare static variables for each element, but rather work with an array or some collection that keep references to the elements.
The TextField:
class CustomTextField: UIViewController {
private let labelElement = UILabel(frame: CGRect(x: 20, y: 150, width: 350, height: 20))
private let textfieldElement = UITextField(frame: CGRect(x: 20, y: 200, width: 350, height: 40))
init(label: String) {
self.labelElement.text = label
textfieldElement.borderStyle = UITextField.BorderStyle.roundedRect
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(labelElement)
view.addSubview(textfieldElement)
}
}
回答1:
From your code it looks like getClass()
will return a subclass of UIViewController
(I can see this because you are calling addChild
). You also have to call initializedClass.didMove(toParent: self)
sometimes after addChild
otherwise the embedding of the VC is not completed and could cause issues. I am not entirely sure this is the cause of your problem but I suggest to try it. Also when implementing a container VC you need to take care of AutoLayout for the children's view, where are you managing the constraints? If you don't add any constraint the view of the VC you are adding as child will have the same size of the main UIWindow
.
You actually could also get rid of var subViews: [UIView] = []
and just rely on children
array from UIViewController
. It will contain all the VCs you add when calling addChild
.
来源:https://stackoverflow.com/questions/65882928/keeping-strong-references-with-dynamic-subviews-in-swift