Reputation: 3095
I have a UITextField
centered in my viewcontroller. I'm using a CAShapeLayer
underneath the textfield that grows in width as text is typed in the textfield. Once the user presses enter, a UITableView
that is hidden at the bottom of the view slides up. I'm able to get the tableview and textfield to slide up by changing their constraints.
func moveUp(){
searchBarTopConstraint.constant = 0
tableViewTopConstraint.constant = 0
UIView.animate(withDuration: 2.0) {
self.view.layoutSubviews()
}
}
However, I can't figure out how to get the CAShapeLayer
to slide up with the textfield. I tried creating a function that moves the line and putting it in a UIView animation with a completion handler, but the line will either move up after the animation or before.
UIView.animate(withDuration: 2.00, delay: 0.0, options: [], animations: {
self.view.layoutSubviews()
self.moveLine()
}, completion: { (finished: Bool) in
// self.moveLine()
})
The y position of the line is determined by the textfields position (this is a concise version of the moveLine()):
let y = searchField.frame.origin.y + searchField.frame.size.height + 6
linePath.move(to: CGPoint(x:x1, y:y))
linePath.addLine(to: CGPoint(x:x2, y:y))
Upvotes: 1
Views: 4527
Reputation: 437422
There are two issues:
If moving views via constraints, you call layoutIfNeeded()
in the animation block, not layoutSubviews()
As the layoutSubviews()
documentation says:
You should not call this method directly. If you want to force a layout update, call the
setNeedsLayout()
method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call thelayoutIfNeeded()
method.
In your case, put layoutIfNeeded()
inside your animate
closure, not layoutSubviews()
.
Note that when you do this properly, this moves the view in which you have the CAShapeLayer
, not moving the CAShapeLayer
itself. So, if you have a shape layer that you want to move, you'd put that shape layer with a view with the constraints, and then move the view with the above layoutIfNeeded()
call.
Theoretically, you actually can animate the moving of a CAShapeLayer
within a view using CABasicAnimation
:
let position = CGPoint(...) // the new origin of the CAShapeLayer within its view
let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = shapeLayer.position // animate from current position ...
animation.toValue = position // ... to whereever the new position is
animation.duration = 2
shapeLayer.position = position // set the shape's final position to be the new position so when the animation is done, it's at its new "home"
shapeLayer.add(animation, forKey: nil) // animate from `fromValue` to `toValue`
Personally, I think it's a little confusing to move shape layers within a view, and I prefer to put the shape layer within its own view, add that view within your view hierarchy with the associated constraints, and then animate that view's constraints. It makes debugging of views with the Xcode view debugger easier, IMHO.
Upvotes: 2