Swift iOS开发:如何在不同父视图间实现卡片UIImage的跨视图移动动画
嘿,很高兴看到你在探索iOS动画!针对你的问题,CGAffineTransform并不是处理这种跨父视图位置动画的最优选择——因为你需要解决的核心问题是不同视图层级的坐标系统一,直接对frame做转换动画会更直观可靠。下面给你详细的实现思路和步骤:
核心思路:用“动画替身”解决跨父视图问题
因为你的卡片和目标位置分属不同的superView,直接移动原视图会破坏原有布局结构,所以最佳实践是创建一个和原卡片一模一样的临时视图(替身),把它放到一个能覆盖所有相关视图的顶层容器(比如ViewController的根view,或者window),然后让这个替身从牌堆位置飞到目标位置,动画结束后再显示真实的目标卡片。
具体实现步骤
创建动画替身
复制原卡片的样式和内容,生成一个临时的UIImageView,确保它和原卡片看起来完全一致:let animatingCard = UIImageView(image: cardBackImage) animatingCard.contentMode = cardBackImage.contentMode animatingCard.clipsToBounds = true // 把替身加到顶层容器,比如当前VC的view self.view.addSubview(animatingCard)统一坐标系统
不同父视图的frame是基于自身坐标系的,所以需要把起始位置(牌堆里的卡片)和目标位置(棋盘上的位置)都转换成顶层容器的坐标:// 转换起始位置:从deckPileImage的坐标系转到顶层容器(self.view) let startFrame = deckPileImage.convert(cardBackImage.frame, to: self.view) animatingCard.frame = startFrame // 转换目标位置:假设boardView里的目标卡片位置是targetCardFrame(相对于boardView) let targetCardFrame = CGRect(x: 100, y: 150, width: 90, height: 130) // 示例位置 let targetFrame = boardView.convert(targetCardFrame, to: self.view)执行飞入动画
用UIView的基础动画让替身从起始位置移动到目标位置,动画结束后清理替身并显示真实卡片:UIView.animate(withDuration: 0.6, // 动画时长,可根据需求调整 delay: 0, options: .curveEaseInOut, // 缓动效果,让动画更自然 animations: { animatingCard.frame = targetFrame }) { [weak self] finished in guard finished else { return } // 动画完成后移除替身 animatingCard.removeFromSuperview() // 显示棋盘上的真实卡片 self?.showTargetCardInBoard() // 这里替换成你显示目标卡片的逻辑 }
为什么不用CGAffineTransform?
CGAffineTransform是基于视图自身的坐标系做变换(平移、缩放、旋转),当视图所在的父视图有自己的变换或布局约束时,transform的计算会变得复杂,而且跨父视图时,父视图的层级关系会影响transform的最终显示效果。直接转换frame到统一坐标系的方式更直观,也更容易控制动画的起始和结束位置。
进阶优化:添加动画细节
如果想让动画更生动,可以在动画闭包里添加额外的效果,比如卡片的旋转、透明度变化:
UIView.animate(withDuration: 0.6, animations: { animatingCard.frame = targetFrame // 添加轻微旋转效果,模拟卡片飞行的自然姿态 animatingCard.transform = CGAffineTransform(rotationAngle: .pi / 10) // 动画结束前稍微降低透明度,和真实卡片衔接更自然 animatingCard.alpha = 0.8 })
内容的提问来源于stack exchange,提问作者KnightOfTheSun

