UIView Transform动画未按时长执行:iOS动画瞬间完成问题求助
解决Action Sheet风格弹出动画失效的问题
Hey,我一眼就看出问题所在了!你遇到的核心问题是在viewDidLoad阶段,Auto Layout还没完成布局计算,导致你设置的初始位移完全无效,动画自然看起来是瞬间完成的。
问题根源拆解
在viewDidLoad里初始化containerView时,你写了:
view.transform = .init(translationX: 0, y: view.frame.height)
但此时containerView的frame还是CGRect.zero(因为你把translatesAutoresizingMaskIntoConstraints设为了false,Auto Layout还没来得及计算它的实际尺寸),所以view.frame.height的值是0——相当于你给containerView设置了一个“无偏移”的初始状态,后续动画从“无偏移”到identity(还是无偏移),自然看不到任何过渡效果。
两种靠谱的解决方案
方案一:把动画移到viewDidAppear阶段(简单直接)
viewDidAppear方法调用时,Auto Layout已经完成了所有布局计算,containerView的实际尺寸已经确定。我们只需要在视图即将显示时设置初始偏移,然后在视图显示后执行动画:
final class CustomCardViewController: UIViewController { // ... 你的其他属性 ... override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 此时containerView的frame已经有正确高度了 containerView.transform = .init(translationX: 0, y: containerView.frame.height) } override func viewDidLoad() { super.viewDidLoad() backgroundMask.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTapToDismiss))) modalPresentationStyle = .overFullScreen [backgroundMask, containerView].forEach(view.addSubview(_:)) NSLayoutConstraint.activate([ backgroundMask.topAnchor.constraint(equalTo: view.topAnchor), backgroundMask.leadingAnchor.constraint(equalTo: view.leadingAnchor), backgroundMask.bottomAnchor.constraint(equalTo: view.bottomAnchor), backgroundMask.trailingAnchor.constraint(equalTo: view.trailingAnchor), containerView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 24), containerView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24), containerView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -24), containerView.heightAnchor.constraint(greaterThanOrEqualToConstant: 200) ]) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // 执行滑入动画 UIView.animate(withDuration: 5, delay: 0.33, options: .curveEaseOut, animations: { self.containerView.transform = .identity }, completion: nil) } // ... 你的其他方法 ... }
方案二:用约束动画实现(更符合Auto Layout最佳实践)
如果想严格遵循Auto Layout的规则,我们可以通过修改约束的方式实现动画,而不是直接操作transform:
final class CustomCardViewController: UIViewController { private let backgroundMask: UIView = { let view = UIView(frame: .zero) view.translatesAutoresizingMaskIntoConstraints = false view.backgroundColor = .init(white: 0, alpha: 0.3) return view }() private lazy var containerView: UIView = { let view = UIView(frame: .zero) view.translatesAutoresizingMaskIntoConstraints = false view.backgroundColor = .red return view }() // 把bottom约束存为变量,方便后续修改 private var containerBottomConstraint: NSLayoutConstraint! override func viewDidLoad() { super.viewDidLoad() backgroundMask.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTapToDismiss))) modalPresentationStyle = .overFullScreen [backgroundMask, containerView].forEach(view.addSubview(_:)) // 初始化bottom约束,让containerView完全在屏幕下方 containerBottomConstraint = containerView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 200 + 24) // 200是最小高度,24是原来的底部间距 NSLayoutConstraint.activate([ backgroundMask.topAnchor.constraint(equalTo: view.topAnchor), backgroundMask.leadingAnchor.constraint(equalTo: view.leadingAnchor), backgroundMask.bottomAnchor.constraint(equalTo: view.bottomAnchor), backgroundMask.trailingAnchor.constraint(equalTo: view.trailingAnchor), containerView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 24), containerBottomConstraint, containerView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -24), containerView.heightAnchor.constraint(greaterThanOrEqualToConstant: 200) ]) // 先强制布局生效,确保初始位置正确 view.layoutIfNeeded() // 动画修改约束,实现滑入效果 UIView.animate(withDuration: 5, delay: 0.33, options: .curveEaseOut, animations: { self.containerBottomConstraint.constant = -24 self.view.layoutIfNeeded() // 触发约束动画 }, completion: nil) } // ... 你的其他方法 ... }
这种方式的好处是完全基于Auto Layout,不会出现transform和约束冲突的问题,适配不同屏幕也更可靠。
内容的提问来源于stack exchange,提问作者Harry Blue
相关产品推荐
相关产品推荐

