I have a custom UIView using UIKit Dynamics to perform an animation when the user taps on a button. The view in question is a simple one, that I lay out manually in layoutSubviews()
. However, layoutSubviews()
gets called for each frame of animation while UIKit Dynamics are in action, and any layout changes I make in that time (responding, for instance, to a taller status bar) result in distortion of my dynamic views.
How can I respond to a change in view size while a UIKit Dynamics animation is in progress?
Update
I created a demo project (which very closely matches my use case, though it's stripped down), and posted it on GitHub. The storyboard uses AutoLayout, but the view opts out of AutoLayout for laying out its own subviews with translatesAutoresizingMaskIntoConstraints = false
. To reproduce the behavior, run in the simulator (I chose iPhone 5) and then hit ⌘Y as the star swings to witness the distortion. This is the view code:
import UIKit
class CustomView: UIView {
var swingingView: UIView!
var animator: UIDynamicAnimator!
var attachment: UIAttachmentBehavior!
var lastViewFrame = CGRectZero
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.translatesAutoresizingMaskIntoConstraints = false
swingingView = UIImageView(image: UIImage(named: "Star"))
self.addSubview(swingingView)
}
override func layoutSubviews() {
// Don't run for every frame of the animation. Only when responding to a layout change
guard self.frame != lastViewFrame else {
return
}
lastViewFrame = self.frame
swingingView.frame = CGRect(x: 0, y: self.frame.size.height / 2, width: 100, height: 100)
// Only run this setup code once
if animator == nil {
animator = UIDynamicAnimator(referenceView: self)
let gravity = UIGravityBehavior(items: [swingingView])
gravity.magnitude = 1.5
animator.addBehavior(gravity)
attachment = UIAttachmentBehavior(item: swingingView,
offsetFromCenter: UIOffset(horizontal: 0, vertical: swingingView.frame.size.height / -2),
attachedToAnchor: CGPoint(x: self.bounds.size.width / 2, y: 0))
attachment.length = CGFloat(250.0)
animator.addBehavior(attachment)
}
animator.updateItemUsingCurrentState(swingingView)
}
}