iOS 8中UIView动画异常,iOS8.4/9.2动画兼容问题如何解决?
Hey there, let's tackle this issue with your RoundedShadowButton on iOS 8.4 and 9.2. The problem likely stems from how older iOS versions handle AutoLayout constraints alongside layer modifications and animations—here are the key fixes to get your Storyboard and animations working correctly:
1. Stop modifying frames directly with AutoLayout
In older iOS versions, manually setting the button's frame in viewDidAppear clashes hard with AutoLayout. The layout system might overwrite your frame changes after you set them, leading to broken Storyboard behavior. Instead:
- Create outlet connections for your button's width/height constraints in Storyboard.
- Update the
constantvalues of these constraints inviewDidAppearinstead of changing the frame. - Call
view.layoutIfNeeded()right after to trigger the layout update properly.
Example snippet:
// In HomeVC @IBOutlet weak var buttonWidthConstraint: NSLayoutConstraint! @IBOutlet weak var buttonHeightConstraint: NSLayoutConstraint! override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) buttonWidthConstraint.constant = 200 buttonHeightConstraint.constant = 60 view.layoutIfNeeded() // You can still set other button properties here, just skip frame changes button.layer.cornerRadius = buttonHeightConstraint.constant / 2 }
2. Move layer setup to layoutSubviews() in your button subclass
Older iOS versions don't guarantee that viewDidAppear runs after AutoLayout has fully settled on the button's final bounds. Instead, handle corner radius and shadow setup in your RoundedShadowButton's layoutSubviews() method—this is the reliable spot where you know the button's size is final.
Also, note that masksToBounds = true will cut off shadows in all iOS versions. Use a separate shadow layer to avoid this:
class RoundedShadowButton: UIButton { private let shadowLayer = CAShapeLayer() override func layoutSubviews() { super.layoutSubviews() // Set corner radius for the button content self.layer.cornerRadius = self.bounds.height / 2 self.layer.masksToBounds = true // Configure shadow layer shadowLayer.path = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).cgPath shadowLayer.fillColor = self.backgroundColor?.cgColor shadowLayer.shadowColor = UIColor.black.cgColor shadowLayer.shadowOffset = CGSize(width: 0, height: 2) shadowLayer.shadowOpacity = 0.3 shadowLayer.shadowRadius = 4 // Insert shadow below the button's content layer (only once) if self.layer.sublayers?.first != shadowLayer { self.layer.insertSublayer(shadowLayer, at: 0) } } }
3. Adjust your animation logic for older iOS
If your animateButton function modifies layer properties directly, wrap it in a CATransaction to ensure compatibility with older iOS. Also, make sure animations run after layout is fully updated:
func animateButton() { CATransaction.begin() CATransaction.setAnimationDuration(0.3) // Example animation: scale down then bounce back UIView.animate(withDuration: 0.15, animations: { self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95) }) { _ in UIView.animate(withDuration: 0.15) { self.transform = .identity } } CATransaction.commit() }
4. Double-check Storyboard settings for older iOS
- Confirm your button's custom class is set to
RoundedShadowButtonin the Identity Inspector (no typos!). - Ensure all constraints are valid (no orange/red warnings in Storyboard—older iOS is less forgiving of ambiguous layouts).
- Don't enable
Clips to Boundson the button in Storyboard; we handle that in code to preserve shadows.
Putting it all together, the core issues are conflicting frame changes with AutoLayout, incorrect layer setup timing, and animation compatibility gaps. By following these steps, your button should render and animate correctly across iOS 8.4, 9.2, and newer versions.
内容的提问来源于stack exchange,提问作者Alwin Vazhappilly

