iOS 11自定义转场偏移问题:视图快照匹配异常致抖动
iOS 11 自定义转场快照不匹配、按钮抖动问题解决方案
我之前也踩过iOS 11自定义转场的布局坑,结合你的代码和问题描述来看,这个问题的核心是iOS 11对视图布局时机、快照生成逻辑的处理和iOS 10存在差异,尤其是Auto Layout和Safe Area的引入让视图frame的计算时机更严格。下面是针对性的修复方案:
问题根源分析
- 快照时机过早:你在将
toVC.view添加到容器视图之前就调用了layoutIfNeeded,此时视图还不在视图层级中,Auto Layout无法正确计算最终frame,导致快照的初始位置和实际渲染位置偏差。 - 子视图快照的布局不稳定:单独对
ivBack和btnGive生成快照时,这两个子视图的frame可能还没完成Safe Area适配的布局计算,动画过程中布局引擎再次调整frame,就会出现明显抖动。 - 视图层级操作冗余:动画完成后重复添加
toView到容器,可能触发额外的布局刷新,加剧抖动。
具体修复步骤
1. 调整快照生成时机,确保视图完成布局
在presenting分支中,先将toVC.view添加到容器视图,再强制触发布局,最后生成快照。这样视图处于正确的层级中,Auto Layout能计算出准确的frame:
if isPresenting { transitionDuration = CGFloat(self.duration) // 先将toView加入容器,再处理布局 container?.addSubview(toView!) toView?.frame = endFrame // 强制完成所有布局计算 toVC.view.setNeedsLayout() toVC.view.layoutIfNeeded() // 隐藏原视图的子控件,避免和快照重叠 let detailVC = toVC as! EventDetailVC detailVC.ivBack.alpha = 0 detailVC.btnGive.alpha = 0 // 此时生成的快照frame是准确的 move = toView?.snapshotView(afterScreenUpdates: true) iconBack = detailVC.ivBack?.snapshotView(afterScreenUpdates: true) btnGive = detailVC.btnGive?.snapshotView(afterScreenUpdates: true) move?.frame = beginFrame! move?.alpha = 0.0 container?.addSubview(move!) container?.addSubview(iconBack!) container?.addSubview(btnGive!) }
2. 修正动画完成后的视图处理逻辑
动画结束后不需要重新添加toView(已经提前加入容器),只需恢复原控件的透明度即可,减少不必要的视图层级变动:
completion: {(_ finished: Bool) -> Void in // 清理快照视图 iconBack?.removeFromSuperview() btnGive?.removeFromSuperview() move?.removeFromSuperview() // 恢复原控件可见性 let detailVC = toVC as! EventDetailVC detailVC.ivBack.alpha = 1 detailVC.btnGive.alpha = 1 transitionContext.completeTransition(true) }
3. 适配iOS 11 Safe Area布局
检查btnGive的约束是否适配了Safe Area,iOS 10没有Safe Area,iOS 11下如果直接依赖view.bottomAnchor会导致frame计算偏差,建议做版本适配:
// 在EventDetailVC的布局代码中 if #available(iOS 11.0, *) { btnGive.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16).isActive = true } else { btnGive.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -16).isActive = true }
4. 可选:改用整体快照优化动画逻辑
如果单独子视图快照还是有问题,可以尝试只对toView做整体快照,动画过程中调整整体的位置和透明度,最后显示原视图,这样能避免子视图布局的不确定性:
if isPresenting { // ... 省略布局代码 ... // 只生成整体快照 move = toView?.snapshotView(afterScreenUpdates: true) toView?.alpha = 0 container?.addSubview(move!) move?.frame = beginFrame! move?.alpha = 0.0 UIView.animate(withDuration: TimeInterval(transitionDuration) , animations: {() -> Void in move?.frame = detailVC.imageView.frame move?.alpha = 1.0 cell?.alpha = 0.0 }, completion: {(_ finished: Bool) -> Void in move?.removeFromSuperview() toView?.alpha = 1 transitionContext.completeTransition(true) }) }
总结
iOS 11对视图布局的时机要求更严格,必须确保视图处于正确的层级中,且完成所有布局计算后再生成快照。同时Safe Area的适配也是容易忽略的点,这些调整后应该能解决快照不匹配和按钮抖动的问题。
内容的提问来源于stack exchange,提问作者Kalpit Seksaria
相关产品推荐
相关产品推荐

