如何在内容增长的同时为 UIView 的阴影边框设置动画

Posted

技术标签:

【中文标题】如何在内容增长的同时为 UIView 的阴影边框设置动画【英文标题】:How to animate the shadow border of an UIView at the same time its content grows 【发布时间】:2020-12-29 12:31:39 【问题描述】:

我正在开发一个项目,该项目的所有视图都嵌入在这样的视图容器中:

今天,当堆栈视图显示其排列的子视图时,我必须使其中一个容器以动画方式垂直增长。所以,第一个问题是这个容器的阴影边框没有动画,我意识到阴影路径应该以另一种方式动画,而不仅仅是在 UIView.animate 块中。

我已将问题隔离在一个小项目中:

如您所见,我正在尝试根据内部堆栈视图排列的子视图部署/取消部署容器视图。问题是影子路径,我已经在尝试了解它是如何工作的。

当您需要在动画块中更新 UIView 的高度时,您能帮我了解如何管理 UIView 阴影层吗?

容器阴影视图代码:

import UIKit

@IBDesignable final class ShadowView: UIView 
    
    @IBInspectable var cornerRadius: CGFloat = 0.0 
        didSet 
            setNeedsLayout()
        
    
    @IBInspectable var shadowColor: UIColor = UIColor.darkGray 
        didSet 
            setNeedsLayout()
        
    
    @IBInspectable var shadowOffsetWidth: CGFloat = 0.0 
        didSet 
            setNeedsLayout()
        
    
    @IBInspectable var shadowOffsetHeight: CGFloat = 1.8 
        didSet 
            setNeedsLayout()
        
    
    @IBInspectable var shadowOpacity: Float = 0.30 
        didSet 
            setNeedsLayout()
        
    
    @IBInspectable var shadowRadius: CGFloat = 3.0 
        didSet 
            setNeedsLayout()
        
    
    @IBInspectable var isAnimatable: Bool = false
    
    private var shadowLayer: CAShapeLayer = CAShapeLayer() 
        didSet 
            setNeedsLayout()
        
    
    
    private var previousPat: CGPath = CGPath(rect: CGRect(x: 0, y: 0, width: 0, height: 0), transform: .none)
    
    override func layoutSubviews() 
        super.layoutSubviews()
        layer.cornerRadius = cornerRadius
        shadowLayer.path = UIBezierPath(roundedRect: bounds,
                                        cornerRadius: cornerRadius).cgPath
        shadowLayer.fillColor = backgroundColor?.cgColor
        shadowLayer.shadowColor = shadowColor.cgColor
        shadowLayer.shadowPath = shadowLayer.path
        shadowLayer.shadowOffset = CGSize(width: shadowOffsetWidth,
                                          height: shadowOffsetHeight)
        shadowLayer.shadowOpacity = shadowOpacity
        shadowLayer.shadowRadius = shadowRadius
        if isAnimatable 
            let animation = CABasicAnimation(keyPath: "shadowPath")
            animation.fromValue = previousPat
            animation.toValue = shadowLayer.path
            animation.autoreverses = false
            animation.duration = 0.8
            shadowLayer.add(animation, forKey: "pathAnimation")
        
        layer.insertSublayer(shadowLayer, at: 0)
        previousPat = shadowLayer.path!
        
    

主视图控制器代码:

final class ViewController: UIViewController 

    @IBOutlet weak var stack: UIStackView!
    @IBOutlet weak var redContainerLabel: UILabel!
    
    override func viewDidLoad() 
        super.viewDidLoad()
    

    @IBAction func animateAction(_ sender: Any) 
        UIView.animate(withDuration: 0.8) 
            self.redContainerLabel.alpha = self.redContainerLabel.alpha == 0 ? 1 : 0
            self.stack.arrangedSubviews.forEach 
                $0.isHidden.toggle()
            
        
    
    

视图结构:

【问题讨论】:

【参考方案1】:

我正在寻找这个问题的答案,并在这里遇到了这篇文章,我相信这就是你要找的:

Animating CALayer shadow simultaneously as UITableviewCell height animates

解决方案有点冗长(我很惊讶用例如此罕见以至于没有提示苹果修复(?)它)并且在这一点上已经有几年了,但我能找到最新的。最终我决定通过在调整大小之前淡出我的阴影来解决这个问题,但你可能会发现它很有用!

我发现的其他一些较旧的帖子似乎证实了这种效果所需的努力程度(同样,Apple 有一个奇怪的列表,它认为是你的问题),所以第一篇文章可能是你最好的打赌。

https://petosoft.wordpress.com/2012/10/24/shadowpath-not-animating-with-standard-uiview-animations-on-ipad/

Smoothly rotate and change size of UIView with shadow

UIView: How to animate CALayer frame with shadow?

如果您设法找到更直接的解决方案,请务必告诉我;同时,我希望这会有所帮助。

【讨论】:

谢谢威廉,有了你的回答,我可以解决我的问题!

以上是关于如何在内容增长的同时为 UIView 的阴影边框设置动画的主要内容,如果未能解决你的问题,请参考以下文章

UIVIew 角半径和阴影?

css如何设置盒子的边框阴影不遮盖它下面的内容

如何消除PDFView“页面阴影”?

如何在 UIView 下绘制阴影?

html5如何让边框发光

说说在 Canvas 中如何添加阴影