Ios 如何将所有子视图移动到新的父视图,同时保持所有约束不变?
我希望以编程方式将Ios 如何将所有子视图移动到新的父视图,同时保持所有约束不变?,ios,swift,uiview,nslayoutconstraint,Ios,Swift,Uiview,Nslayoutconstraint,我希望以编程方式将UIView的所有子视图移动到另一个UIView中,同时保持子视图之间以及(新)父视图之间的所有约束不变 “正确”的方法是什么 我当前的解决方案只是在当前父视图的子视图和所有约束之间循环,并检查它是在父视图和子视图之间还是在两个子视图之间。匹配约束被存储并应用于新父项: var constraints = [NSLayoutConstraint]() for subview in oldParent.subviews { for constraint in oldPar
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中添加这些包装器,但是当在不同的项目中处理许多不同的单元格时,一种自动处理这些内容的通用方法将是一个很好的解决方案。@dfd是正确的,当涉及到约束时,视图之间有着紧密的联系。如果从其超级视图中删除视图,则这些约束将被打破,但如果视图中有一个视图,并且仅更改父视图,则子视图将保留其约束。哇,听起来比实际情况要混乱得多。但是,总的来说,你做这件事的方式很好,我想不出一个更好的方法,也许可以在UIView上做一个扩展?类似于view.moveTo(newView)?免责声明:这绝不是一个完整的答案 以下是我注意到的一些事情-
#1不建议访问这些属性。改用
firstAnchor
和secondAnchor
属性。
/* accessors
firstItem.firstAttribute {==,<=,>=} secondItem.secondAttribute * multiplier + constant
Access to these properties is not recommended. Use the `firstAnchor` and `secondAnchor` properties instead.
*/
unowned(unsafe) open var firstItem: AnyObject? { get }
unowned(unsafe) open var secondItem: AnyObject? { get }
#2当前代码不考虑
优先级
值
open var priority: UILayoutPriority
open var shouldBeArchived: Bool
open var isActive: Bool
open var identifier: String?
#3当前代码不考虑
应保留的值
值
open var priority: UILayoutPriority
open var shouldBeArchived: Bool
open var isActive: Bool
open var identifier: String?
而且直到今天我才知道这件事,我也不确定什么是正确的处理方法
#4当前代码不考虑
firstAnchor
和secondAnchor
值
/* accessors
firstAnchor{==,<=,>=} secondAnchor * multiplier + constant
*/
@available(iOS 10.0, *)
@NSCopying open var firstAnchor: NSLayoutAnchor<AnyObject> { get }
@available(iOS 10.0, *)
@NSCopying open var secondAnchor: NSLayoutAnchor<AnyObject>? { get }
#6当前代码不考虑
标识符
值
open var priority: UILayoutPriority
open var shouldBeArchived: Bool
open var isActive: Bool
open var identifier: String?
如果您正在为自己的应用程序解决此问题,并且可以确保您没有在最初创建的约束上使用这些值中的某些值,那么应该可以 如果您计划将其放入一个库中,该库将在其他应用程序中使用,该库将为所有当前问题+未来更改/问题打开
我强烈建议,如果可能的话,你应该尽量避免这样做,并重新考虑应该做些什么来实现同样的目标。也许您希望有一个包含所有这些子视图的包装器
UIView
实例,并且该包装器UIView
实例被传输到另一个视图。这将最大限度地减少此代码必须接触/重新创建的约束数量
在任何情况下-您必须执行以下操作(如果您计划继续执行此操作)
firstAnchor
和secondAnchor
值从旧约束复制到新约束。-不可能,这些是GET only,请参见评论。优先级
值从旧约束复制到新约束标识符值从旧约束复制到新约束
值从旧约束复制到新约束
isActive=false
激活任何约束我还不是专家,但我知道约束与视图层次结构的关系有多紧密。(只要你想一想,这是有道理的。)这里有一个想法——为什么不创建一个带有子视图的单亲视图并将其子类化呢?约束将继续存在,维护代码更容易,您甚至可以根据需要在每个子类父视图中命名视图和约束进行编辑。非常感谢您提供的详细答案!我已更新代码以复制您提到的属性:
priority
,identifier
,shouldBeArchived
,isActive
。但是我不确定锚是什么?我认为nslayoutAcho
只是一个更容易使用NSLayoutConstraint
s的API,不是吗?锚属性对应于NSLayoutConstraint.Attribute
s,是只读属性。因此,当使用与oldConstraint
相同的项目和属性创建newConstraint
时,锚定应该是相同的,不是吗?是的,应该是。我从中粘贴的文档通过{get}部分清楚地说明了这一点。我现在将更新答案。