【发布时间】:2021-08-19 15:49:05
【问题描述】:
我想以编程方式将UIView 的所有子视图移动到另一个UIView,同时保持子视图之间的所有约束彼此以及(新)父视图之间的约束不变。
“正确”的方法是什么?
我当前的解决方案只是遍历子视图和当前父视图的所有约束,并检查它是在父视图和子视图之间还是在两个子视图之间。匹配约束被存储并应用到新的父级:
var constraints = [NSLayoutConstraint]()
for subview in oldParent.subviews {
for constraint in oldParent.constraints {
if let newConstraint = moveConstraint(constraint, ofView: subview, fromView: oldParent, toView: newParent) {
let active = oldConstraint.isActive
oldParent.removeConstraint(constraint)
newConstraint.isActive = active
constraints.append(newConstraint)
}
}
newParent.addSubview(subview)
}
newParent.addConstraints(constraints)
func moveConstraint(_ constraint: NSLayoutConstraint, ofView view: UIView, fromView prevSuperview: UIView, toView newSuperview: UIView) -> NSLayoutConstraint? {
// Check prevSuperview + Layout Guides
var prevSuperviewIsFirstItem: Bool = constraint.firstItem === prevSuperview
if !prevSuperviewIsFirstItem {
for layoutGuid in prevSuperview.layoutGuides {
if constraint.firstItem === layoutGuid {
prevSuperviewIsFirstItem = true
break
}
}
}
var prevSuperviewIsSecondItem: Bool = constraint.secondItem === prevSuperview
if !prevSuperviewIsSecondItem {
for layoutGuid in prevSuperview.layoutGuides {
if constraint.secondItem === layoutGuid {
prevSuperviewIsSecondItem = true
break
}
}
}
// Move constraints between prevSuperview + View
var newConstraint: NSLayoutConstraint? = nil
if prevSuperviewIsFirstItem && constraint.secondItem === view {
newConstraint = NSLayoutConstraint(item: newSuperview, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: view, attribute: constraint.secondAttribute, multiplier: constraint.multiplier, constant: constraint.constant)
} else if prevSuperviewIsSecondItem && constraint.firstItem === view {
newConstraint = NSLayoutConstraint(item: view, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: newSuperview, attribute: constraint.secondAttribute, multiplier: constraint.multiplier, constant: constraint.constant)
}
if let newConstraint = newConstraint {
// handle newConstraint.isActive only after oldConstraint has
// been removed to avoid conflicts
newConstraint.identifier = constraint.identifier
newConstraint.priority = constraint.priority
newConstraint.shouldBeArchived = constraint.shouldBeArchived
return newConstraint
}
// Move constraints between view and other subview
if constraint.firstItem === view || constraint.secondItem === view {
return constraint
}
return nil
}
虽然根据我的测试,这似乎工作正常,但我想确保这真的正确。处理约束始终是一个棘手的问题,很容易在将来的某个时间导致错误。
那么:复制过程是否足以正确传输子视图及其约束?或者还有什么可以考虑的吗?还是有更好的办法?
编辑:上下文
一个用例是UICollectionViewCell,其布局类似于卡片。我遵循了另一个答案中的提示,通过向contentView 添加两个子视图来实现这一点,它们充当包装器并通过它们的图层属性应用阴影和圆角。目标是创建一个UICollectionViewCell 子类,它会自动将这些包装子视图添加到awakeFromNib() 中,并将contentView 的所有子视图移动到此包装器视图中。
当然,可以在 InterfaceBuilder 中轻松添加这些包装器,但在不同项目中处理大量不同单元格时,自动处理此问题的通用方法将是一个很好的解决方案。
【问题讨论】:
-
我不是专家,但我知道约束与视图层次结构的紧密联系。 (仔细想想就明白了。)这里有一个想法——为什么不直接创建一个带有子视图和子类的单亲呢?约束将继续存在,维护代码更容易,您甚至可以根据需要在每个子类父视图中命名视图和约束以进行编辑。
标签: ios swift uiview nslayoutconstraint