autolayout下设置layer的anchorPoint后如何让uiview不移动
How to make the uiview not move after setting its layer's anchorPoint under Autolayout
我有一个需求需要改变UIView层的anchorPoint,但是改变anchorPoint后视图无法移动。
我知道当视图由 frame(CGRect:...) 定义时是可能的。像这样:
let width = SCREEN_WIDTH - 40
let view2 = UIView(frame: CGRect(x: 20, y: 300, width: width, height: 200))
view2.backgroundColor = .blue
self.view.addSubview(view2)
let oldFrame2 = view2.frame
view2.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view2.frame = oldFrame2
这有效。
但我的视图是由 Autolayout 定义的,我尝试了上面代码的解决方案,但它不起作用。代码:
let view1 = UIView()
view1.backgroundColor = .orange
self.view.addSubview(view1)
view1.snp.makeConstraints { (maker) in
maker.top.equalToSuperview().offset(50)
maker.leading.equalToSuperview().offset(20)
maker.trailing.equalToSuperview().offset(-20)
maker.height.equalTo(200)
}
let oldFrame1 = view1.frame
view1.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view1.frame = oldFrame1
结果是:橙色的view1被移动了,改变anchorPoint后应该和蓝色的view2一样。
所以有人可以给我一些建议吗?
----------------------------更新答案------------ --------------
正如@DonMag 回答的那样,我们可以通过在使用自动布局时更新视图而非框架的约束来实现此要求。这是 SnapKit 的代码:
let view1 = UIView()
view1.backgroundColor = .orange
view1.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(view1)
view1.snp.makeConstraints { (maker) in
maker.top.equalToSuperview().offset(100)
maker.leading.equalToSuperview().offset(20)
maker.trailing.equalToSuperview().offset(-20)
maker.height.equalTo(200)
}
// important!!!
view1.layoutIfNeeded()
let oldFrame1 = view1.frame
view1.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
// update constraints by updateConstraints function
// if you use @IBOutlet NSLayoutConstraint from xib,
// you can also just set xxx.constant = yyy to update the constraints.
view1.snp.updateConstraints { (maker) in
let subOffset = oldFrame1.width * 0.5
maker.leading.equalToSuperview().offset(20 - subOffset)
maker.trailing.equalToSuperview().offset(-20 - subOffset)
}
let width = SCREEN_WIDTH - 40
let view2 = UIView(frame: CGRect(x: 20, y: 300, width: width, height: 200))
view2.backgroundColor = .blue
self.view.addSubview(view2)
let oldFrame2 = view2.frame
view2.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view2.frame = oldFrame2
另一种解决方案是通过设置 translatesAutoresizingMaskIntoConstraints = true
将自动布局视图更改为框架,如下所示:
let width = SCREEN_WIDTH - 40
let view1 = UIView()
view1.backgroundColor = .orange
self.view.addSubview(view1)
// Autolayout
view1.snp.makeConstraints { (maker) in
maker.top.equalToSuperview().offset(100)
maker.leading.equalToSuperview().offset(20)
maker.trailing.equalToSuperview().offset(-20)
maker.height.equalTo(200)
}
// change autolayout to frame
view1.translatesAutoresizingMaskIntoConstraints = true
view1.frame = CGRect(x: 20, y: 100, width: width, height: 200)
let oldFrame1 = view1.frame
view1.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view1.frame = oldFrame1
let view2 = UIView(frame: CGRect(x: 20, y: 300, width: width, height: 200))
view2.backgroundColor = .blue
self.view.addSubview(view2)
let oldFrame2 = view2.frame
view2.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view2.frame = oldFrame2
首先,当使用自动布局/约束时,直接设置视图的 .frame
不会得到预期的结果。一旦自动布局更新 UI,就会重新应用约束。
当您更改 .anchorPoint
时,您会更改视图的几何形状。因此,您最好使用 .frame
而不是自动布局。
如果您需要/想要使用自动布局,您将需要更新约束的 .constant
值以说明几何变化。
我不知道如何使用 SnapKit 做到这一点,但这里有一个使用“标准”约束语法的示例。
- 声明前导和尾随约束变量
- 分配并激活约束
- 告诉自动布局计算框架
- 更改锚点
- 更新 Leading 和 Trailing 约束常量以反映几何变化
注意:这只是示例代码!:
class ViewController: UIViewController {
// these will have their .constant values changed
// to account for layer.anchorPoint change
var leadingConstraint: NSLayoutConstraint!
var trailingConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// NOT using auto-layout constraints
let width = view.frame.width - 40
let view2 = UIView(frame: CGRect(x: 20, y: 300, width: width, height: 200))
view2.backgroundColor = .blue
self.view.addSubview(view2)
let oldFrame2 = view2.frame
view2.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view2.frame = oldFrame2
// USING auto-layout constraints
let view1 = UIView()
view1.backgroundColor = .orange
view1.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(view1)
// create leading and trailing constraints
leadingConstraint = view1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0)
trailingConstraint = view1.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0)
// activate constraints
NSLayoutConstraint.activate([
view1.topAnchor.constraint(equalTo: view.topAnchor, constant: 80.0),
view1.heightAnchor.constraint(equalToConstant: 200.0),
leadingConstraint,
trailingConstraint,
])
// auto-layout has not run yet, so force it to layout
// the view frame
view1.layoutIfNeeded()
// get the auto-layout generated frame
let oldFrame1 = view1.frame
// change the anchorPoint
view1.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
// we've move the X anchorPoint from 0.5 to 0.0, so
// we need to adjust the leading and trailing constants
// by 0.5 * the frame width
leadingConstraint.constant -= oldFrame1.width * 0.5
trailingConstraint.constant -= oldFrame1.width * 0.5
}
}
结果:
我有一个需求需要改变UIView层的anchorPoint,但是改变anchorPoint后视图无法移动。 我知道当视图由 frame(CGRect:...) 定义时是可能的。像这样:
let width = SCREEN_WIDTH - 40
let view2 = UIView(frame: CGRect(x: 20, y: 300, width: width, height: 200))
view2.backgroundColor = .blue
self.view.addSubview(view2)
let oldFrame2 = view2.frame
view2.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view2.frame = oldFrame2
这有效。
但我的视图是由 Autolayout 定义的,我尝试了上面代码的解决方案,但它不起作用。代码:
let view1 = UIView()
view1.backgroundColor = .orange
self.view.addSubview(view1)
view1.snp.makeConstraints { (maker) in
maker.top.equalToSuperview().offset(50)
maker.leading.equalToSuperview().offset(20)
maker.trailing.equalToSuperview().offset(-20)
maker.height.equalTo(200)
}
let oldFrame1 = view1.frame
view1.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view1.frame = oldFrame1
结果是:橙色的view1被移动了,改变anchorPoint后应该和蓝色的view2一样。
所以有人可以给我一些建议吗?
----------------------------更新答案------------ --------------
正如@DonMag 回答的那样,我们可以通过在使用自动布局时更新视图而非框架的约束来实现此要求。这是 SnapKit 的代码:
let view1 = UIView()
view1.backgroundColor = .orange
view1.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(view1)
view1.snp.makeConstraints { (maker) in
maker.top.equalToSuperview().offset(100)
maker.leading.equalToSuperview().offset(20)
maker.trailing.equalToSuperview().offset(-20)
maker.height.equalTo(200)
}
// important!!!
view1.layoutIfNeeded()
let oldFrame1 = view1.frame
view1.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
// update constraints by updateConstraints function
// if you use @IBOutlet NSLayoutConstraint from xib,
// you can also just set xxx.constant = yyy to update the constraints.
view1.snp.updateConstraints { (maker) in
let subOffset = oldFrame1.width * 0.5
maker.leading.equalToSuperview().offset(20 - subOffset)
maker.trailing.equalToSuperview().offset(-20 - subOffset)
}
let width = SCREEN_WIDTH - 40
let view2 = UIView(frame: CGRect(x: 20, y: 300, width: width, height: 200))
view2.backgroundColor = .blue
self.view.addSubview(view2)
let oldFrame2 = view2.frame
view2.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view2.frame = oldFrame2
另一种解决方案是通过设置 translatesAutoresizingMaskIntoConstraints = true
将自动布局视图更改为框架,如下所示:
let width = SCREEN_WIDTH - 40
let view1 = UIView()
view1.backgroundColor = .orange
self.view.addSubview(view1)
// Autolayout
view1.snp.makeConstraints { (maker) in
maker.top.equalToSuperview().offset(100)
maker.leading.equalToSuperview().offset(20)
maker.trailing.equalToSuperview().offset(-20)
maker.height.equalTo(200)
}
// change autolayout to frame
view1.translatesAutoresizingMaskIntoConstraints = true
view1.frame = CGRect(x: 20, y: 100, width: width, height: 200)
let oldFrame1 = view1.frame
view1.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view1.frame = oldFrame1
let view2 = UIView(frame: CGRect(x: 20, y: 300, width: width, height: 200))
view2.backgroundColor = .blue
self.view.addSubview(view2)
let oldFrame2 = view2.frame
view2.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view2.frame = oldFrame2
首先,当使用自动布局/约束时,直接设置视图的 .frame
不会得到预期的结果。一旦自动布局更新 UI,就会重新应用约束。
当您更改 .anchorPoint
时,您会更改视图的几何形状。因此,您最好使用 .frame
而不是自动布局。
如果您需要/想要使用自动布局,您将需要更新约束的 .constant
值以说明几何变化。
我不知道如何使用 SnapKit 做到这一点,但这里有一个使用“标准”约束语法的示例。
- 声明前导和尾随约束变量
- 分配并激活约束
- 告诉自动布局计算框架
- 更改锚点
- 更新 Leading 和 Trailing 约束常量以反映几何变化
注意:这只是示例代码!:
class ViewController: UIViewController {
// these will have their .constant values changed
// to account for layer.anchorPoint change
var leadingConstraint: NSLayoutConstraint!
var trailingConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// NOT using auto-layout constraints
let width = view.frame.width - 40
let view2 = UIView(frame: CGRect(x: 20, y: 300, width: width, height: 200))
view2.backgroundColor = .blue
self.view.addSubview(view2)
let oldFrame2 = view2.frame
view2.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
view2.frame = oldFrame2
// USING auto-layout constraints
let view1 = UIView()
view1.backgroundColor = .orange
view1.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(view1)
// create leading and trailing constraints
leadingConstraint = view1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0)
trailingConstraint = view1.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0)
// activate constraints
NSLayoutConstraint.activate([
view1.topAnchor.constraint(equalTo: view.topAnchor, constant: 80.0),
view1.heightAnchor.constraint(equalToConstant: 200.0),
leadingConstraint,
trailingConstraint,
])
// auto-layout has not run yet, so force it to layout
// the view frame
view1.layoutIfNeeded()
// get the auto-layout generated frame
let oldFrame1 = view1.frame
// change the anchorPoint
view1.layer.anchorPoint = CGPoint(x: 0, y: 0.5)
// we've move the X anchorPoint from 0.5 to 0.0, so
// we need to adjust the leading and trailing constants
// by 0.5 * the frame width
leadingConstraint.constant -= oldFrame1.width * 0.5
trailingConstraint.constant -= oldFrame1.width * 0.5
}
}
结果: