I have a requirement that need change the UIView layer's anchorPoint, but the view cannot be moved after changing anchorPoint. I know it is possible when the view is defined by frame(CGRect:...). like this:
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
This works.
But my view is defined by Autolayout, I try the solution like above code, but it doesn't work. Code:
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
The result is: the orange view1 be moved, it should be like the blue view2 after changing anchorPoint.
So can anyone give me some suggestions?
------------------------------Update Answer-----------------------------
Just as @DonMag answer, we can implement this requirement by updating the constraints of view not frame when using Autolayout. Here is the code by 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
Another solution is to change the autolayout view to frame by setting translatesAutoresizingMaskIntoConstraints = true
, like this:
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
First, when using auto-layout / constraints, setting the view's .frame
directly will not give desired results. As soon as auto-layout updates the UI, the constraints will be re-applied.
When you change the .anchorPoint
you change the geometry of the view. For that reason, you may be better off using .frame
instead of auto-layout.
If you do need / want to use auto-layout, you'll need to update the .constant
values of the constraints to account for the geometry changes.
I don't know how to do that with SnapKit, but here is an example using "standard" constraint syntax.
Note: this is example code only!:
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
}
}
Result: