You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

3D Touch实现无Peek的Pop及手动创建模糊缩放效果咨询

嘿,针对你想要实现的3D Touch导航需求(类似通讯录/Apple Music,还要无Peek直接Pop的效果),我给你梳理一套可行的方案,亲测好用!

核心思路

既然系统默认的Peek/Pop流程不符合你的场景(进入自定义VC前按钮无法点击),那我们可以手动监听3D Touch的压力变化,在压力达到阈值时触发模糊+缩放的过渡动画,最终在触摸结束时完成导航跳转。完全绕开系统默认的交互逻辑,自己掌控整个流程。

具体实现步骤
  1. 监听触摸事件的压力值:通过重写touchesBegan/touchesMoved/touchesEnded方法,判断压力是否达到触发阈值
  2. 手动构建模糊背景:用UIVisualEffectView实现和系统Peek一致的模糊效果
  3. 实现缩放过渡动画:目标VC的View从缩小状态平滑放大到正常尺寸,配合模糊背景的淡入/淡出
  4. 触摸结束时完成导航:确认用户是触发Pop操作后,执行导航并完成动画收尾
关键代码示例

第一步:定义变量和基础配置

在当前ViewController里先声明需要用到的变量:

import UIKit

class CurrentViewController: UIViewController {
    // 模糊背景视图
    private var blurBackgroundView: UIVisualEffectView?
    // 标记是否正在触发Pop操作
    private var isPopTriggered = false
    // 压力触发阈值(可以根据测试调整,0~1之间,1是设备最大压力)
    private let pressureThreshold: CGFloat = 0.7

第二步:重写触摸方法监听压力

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        guard let touch = touches.first, UITouch.isForceTouchCapabilityAvailable else { return }
        
        // 计算相对压力(当前压力/设备最大压力)
        let relativeForce = touch.force / touch.maximumForce
        if relativeForce >= pressureThreshold {
            preparePopAnimation()
            isPopTriggered = true
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)
        guard let touch = touches.first, UITouch.isForceTouchCapabilityAvailable else { return }
        
        let relativeForce = touch.force / touch.maximumForce
        if relativeForce >= pressureThreshold && !isPopTriggered {
            // 压力达到阈值,开始准备动画
            preparePopAnimation()
            isPopTriggered = true
        } else if relativeForce < pressureThreshold && isPopTriggered {
            // 压力不足,取消动画
            cancelPopAnimation()
            isPopTriggered = false
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        if isPopTriggered {
            // 确认触发Pop,执行导航
            executePopNavigation()
        } else {
            // 取消动画
            cancelPopAnimation()
        }
        isPopTriggered = false
    }

第三步:实现动画准备/取消/导航方法

// 准备Pop动画:显示模糊背景
    private func preparePopAnimation() {
        let blurEffect = UIBlurEffect(style: .systemMaterial)
        blurBackgroundView = UIVisualEffectView(effect: blurEffect)
        blurBackgroundView?.frame = view.bounds
        blurBackgroundView?.alpha = 0
        view.addSubview(blurBackgroundView!)
        
        // 模糊背景淡入动画
        UIView.animate(withDuration: 0.15) {
            self.blurBackgroundView?.alpha = 1
        }
    }

    // 取消Pop动画:隐藏并移除模糊背景
    private func cancelPopAnimation() {
        UIView.animate(withDuration: 0.15, animations: {
            self.blurBackgroundView?.alpha = 0
        }) { _ in
            self.blurBackgroundView?.removeFromSuperview()
            self.blurBackgroundView = nil
        }
    }

    // 执行Pop导航:跳转到自定义VC并播放缩放动画
    private func executePopNavigation() {
        // 替换成你的自定义ViewController
        let targetVC = YourCustomViewController()
        // 初始状态:缩小+透明
        targetVC.view.transform = CGAffineTransform(scaleX: 0.85, y: 0.85)
        targetVC.view.alpha = 0
        
        // 不使用系统动画,手动控制
        navigationController?.pushViewController(targetVC, animated: false)
        
        // 播放缩放+淡入动画,配合弹簧效果更贴近系统风格
        UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.5, options: .curveEaseOut) {
            targetVC.view.transform = .identity
            targetVC.view.alpha = 1
        } completion: { _ in
            // 动画完成后移除模糊背景
            self.blurBackgroundView?.removeFromSuperview()
            self.blurBackgroundView = nil
        }
    }
}
注意事项
  • 确保设备支持3D Touch:代码里已经用UITouch.isForceTouchCapabilityAvailable做了判断,也可以在Info.plist里添加UIRequiredDeviceCapabilities并包含forceTouchCapability,过滤不支持的设备。
  • 阈值调整:pressureThreshold可以根据实际测试调整,用相对压力(touch.force/touch.maximumForce)比固定值更适配不同设备。
  • 按钮交互处理:如果是按钮触发,记得禁用按钮的默认点击事件(或者用手势识别器代理拦截),避免和我们的压力监听冲突。
  • 动画细节:弹簧参数(usingSpringWithDamping和initialSpringVelocity)可以微调,让动画更贴近系统Peek/Pop的质感。

内容的提问来源于stack exchange,提问作者John Webb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:40:12