SwiftUI 长按后将视图移至中央并模糊背景 实现信息App同类效果
iOS实现苹果信息App长按消息弹窗效果方案
该效果完全可以通过原生API实现,不需要引入第三方库,以下是UIKit和SwiftUI两个场景的实现方案:
UIKit实现
实现逻辑分为3个核心步骤:
- 给目标消息视图添加
UILongPressGestureRecognizer监听长按事件 - 长按触发时在当前Window层添加
UIVisualEffectView实现背景模糊效果,同步做透明度渐入动画 - 对原消息视图生成快照,将快照从原位置动画移动到屏幕中央,同时叠加缩放动效,隐藏原视图
核心代码参考:
@objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) { guard gesture.state == .began, let targetView = gesture.view, let window = view.window else { return } // 初始化模糊背景 let blurEffect = UIBlurEffect(style: .systemUltraThinMaterial) let blurView = UIVisualEffectView(effect: blurEffect) blurView.frame = window.bounds blurView.alpha = 0 window.addSubview(blurView) // 生成原消息视图快照 guard let snapshot = targetView.snapshotView(afterScreenUpdates: false) else { return } let originFrame = targetView.convert(targetView.bounds, to: window) snapshot.frame = originFrame window.addSubview(snapshot) targetView.isHidden = true // 执行同步动画 UIView.animate(withDuration: 0.28) { blurView.alpha = 1 snapshot.center = window.center snapshot.transform = .init(scaleX: 1.12, y: 1.12) } }
手势结束恢复时只需反向执行动画,移除临时的模糊层和快照,恢复原消息视图的显示即可。
SwiftUI实现
可以通过matchedGeometryEffect转场API快速实现,逻辑和UIKit一致:
- 用
@State变量记录长按触发状态、选中的消息ID - 列表层根据长按状态叠加
blur模糊效果,做动画过渡 - 选中的消息通过
matchedGeometryEffect匹配原位置和中央弹窗位置,做无缝动效
核心代码参考:
struct MessageListView: View { @Namespace var msgAnimationNamespace @State private var isLongPressActive = false @State private var selectedMsgID: String? // 你的消息列表数据 let messages: [MessageItem] = [] var body: some View { ScrollView { LazyVStack(spacing: 12) { ForEach(messages) { msg in Text(msg.content) .padding(.horizontal, 16) .padding(.vertical, 10) .background(Color(UIColor.systemBlue)) .foregroundColor(.white) .cornerRadius(16) .matchedGeometryEffect(id: msg.id, in: msgAnimationNamespace, isSource: selectedMsgID != msg.id) .onLongPressGesture(minimumDuration: 0.3) { withAnimation(.easeInOut(duration: 0.28)) { selectedMsgID = msg.id isLongPressActive = true } } } } .padding() } .blur(radius: isLongPressActive ? 8 : 0) .overlay { if isLongPressActive, let targetID = selectedMsgID, let targetMsg = messages.first(where: {$0.id == targetID}) { Text(targetMsg.content) .padding(.horizontal, 16) .padding(.vertical, 10) .background(Color(UIColor.systemBlue)) .foregroundColor(.white) .cornerRadius(16) .scaleEffect(1.12) .matchedGeometryEffect(id: targetID, in: msgAnimationNamespace, isSource: false) .onTapGesture { withAnimation(.easeInOut(duration: 0.28)) { isLongPressActive = false selectedMsgID = nil } } } } } }
可以根据需求调整模糊半径、动画时长、缩放比例,就能完全对齐原生信息App的效果。
内容的提问来源于stack exchange,提问作者alionthego
相关产品推荐
相关产品推荐

