Swift:长按结束后从父视图移除视图及动画失效问题求助
这种长按缩放后回退再移除的需求我之前也踩过坑,大概率是动画执行时机、视图层级或者手势状态处理的问题没捋顺,咱们一步步来解决:
问题排查&核心解决方案
先拆解下你当前的问题:既无动画又没移除视图,通常是这几个原因:
- 没有正确识别长按手势的
ended状态,或者动画逻辑没写在正确的闭包里 - 临时添加的
blackBackgroundView和zoomingImageView没加到正确的父视图(比如没加到全屏容器) - 移除操作没有等缩放回退动画完成就执行,导致视觉上没效果
完整代码示例
下面是经过验证的实现逻辑,你可以参考调整:
1. 给Cell里的ImageView添加长按手势
// 在Cell初始化或者配置方法里添加 let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleImageLongPress(_:))) cell.imageView.addGestureRecognizer(longPress) cell.imageView.isUserInteractionEnabled = true
2. 手势处理核心逻辑
@objc private func handleImageLongPress(_ gesture: UILongPressGestureRecognizer) { guard let tappedImageView = gesture.view as? UIImageView, let cell = tappedImageView.superview?.superview as? UICollectionViewCell, // 根据你的Cell结构调整层级 let window = UIApplication.shared.connectedScenes .filter({$0.activationState == .foregroundActive}) .compactMap({$0 as? UIWindowScene}) .first?.windows.first else { return } // 获取ImageView在全屏Window中的原始位置,用于缩放动画的起始/结束帧 let originalFrame = tappedImageView.convert(tappedImageView.bounds, to: window) switch gesture.state { case .began: // 创建全屏背景遮罩 let blackBackgroundView = UIView(frame: window.bounds) blackBackgroundView.backgroundColor = UIColor.black.withAlphaComponent(0.8) blackBackgroundView.tag = 999 // 用Tag标记方便后续查找 window.addSubview(blackBackgroundView) // 创建放大用的ImageView let zoomingImageView = UIImageView(image: tappedImageView.image) zoomingImageView.contentMode = .scaleAspectFit zoomingImageView.frame = originalFrame zoomingImageView.tag = 1000 window.addSubview(zoomingImageView) // 执行放大动画 UIView.animate(withDuration: 0.3) { zoomingImageView.frame = window.bounds blackBackgroundView.alpha = 1 } case .ended, .cancelled: // 找到之前添加的临时视图 guard let blackBackgroundView = window.viewWithTag(999), let zoomingImageView = window.viewWithTag(1000) else { return } // 先执行缩回到原位置的动画,动画完成后再移除视图 UIView.animate(withDuration: 0.3, animations: { zoomingImageView.frame = originalFrame blackBackgroundView.alpha = 0 }) { _ in // 动画完成回调里执行移除 zoomingImageView.removeFromSuperview() blackBackgroundView.removeFromSuperview() } default: break } }
关键注意点
- 视图层级:iOS13+之后
keyWindow可能为空,所以改用场景获取当前Window,确保临时视图加到全屏容器里 - 动画顺序:移除操作必须放在缩放回退动画的
completion闭包里,保证动画完成后再执行移除 - 手势状态:除了
ended,还要处理cancelled状态(比如用户长按后滑动取消手势),避免视图残留 - Cell层级适配:
tappedImageView.superview?.superview要根据你的Cell结构调整,如果ImageView直接在contentView里,那应该是tappedImageView.superview?.superview(contentView的父视图是Cell)
内容的提问来源于stack exchange,提问作者Koh
相关产品推荐
相关产品推荐

