如何在无3D Touch的iPhone(如iPhone11)实现屏幕称重功能?
解决无3D Touch机型的屏幕称重问题
嘿,我来帮你搞定这个问题!你的原有代码依赖3D Touch专属的UIForceTouchCapability和UITouch.force API,但iPhone 11及之后的机型已经取消了3D Touch(改用Haptic Touch,而Haptic Touch不支持压力感知),这就导致原来的逻辑在这些机型上完全跑不通。不过别担心,我们可以换个思路——利用触摸接触面积来模拟压力检测,进而实现类似的称重功能。
原代码的核心问题
你的代码里有个关键判断:self.traitCollection.forceTouchCapability == .available,在无3D Touch的机型上这个判断永远不成立,而且touch.force和touch.maximumPossibleForce会返回固定的无效值,所以整个称重逻辑根本不会执行。
替代方案:基于触摸接触面积的实现
我们可以利用UITouch的majorRadius属性——这个值代表触摸区域的近似半径,按压越用力,手指接触屏幕的面积越大,majorRadius就会越大。通过校准这个值的变化,我们就能把它映射成重量。以下是修改后的完整实现:
第一步:添加校准相关的变量
先在你的ViewController里定义几个变量,用来存储校准基准和配置参数:
// 轻按屏幕时的基准触摸半径(校准用) private var calibrationRadius: CGFloat = 0.0 // 你要对应最大压力的重量(比如原来的385克) private let maxTargetWeight: CGFloat = 385.0 // 是否完成初始校准 private var isCalibrated = false // 测试得到的最大半径差值(需要你实际测试调整) private let maxRadiusDelta: CGFloat = 10.0
第二步:重写触摸事件方法
替换你原来的touchesMoved和touchesEnded,加上校准逻辑:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) guard let touch = touches.first else { return } // 首次触摸时完成校准,记录轻按的基准半径 if !isCalibrated { calibrationRadius = touch.majorRadius isCalibrated = true forceLabel.text = "校准完成,请按压" grammLabel.text = "0 grams" } } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesMoved(touches, with: event) guard let touch = touches.first, isCalibrated else { return } // 计算当前触摸半径与基准半径的差值 let radiusDelta = touch.majorRadius - calibrationRadius // 防止差值为负(比如手指抬起时的波动) guard radiusDelta >= 0 else { forceLabel.text = "0% force" grammLabel.text = "0 grams" return } // 计算压力比例(最大不超过100%) let forceRatio = min(radiusDelta / maxRadiusDelta, 1.0) let forcePercent = Int(forceRatio * 100) // 映射成对应的重量 let currentWeight = forceRatio * maxTargetWeight forceLabel.text = "\(forcePercent)% force" grammLabel.text = "\(Int(currentWeight)) grams" } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) forceLabel.text = "0% force" grammLabel.text = "0 grams" // 可选:如果需要每次使用前都校准,可以在这里重置 // isCalibrated = false }
关键细节说明
- 校准的重要性:必须让用户先轻按屏幕获取基准半径,因为不同手指的初始接触面积不一样,校准后才能准确计算按压的变化量。
maxRadiusDelta的调整:这个值需要你实际测试——比如用385克的物体按压屏幕,记录此时的touch.majorRadius减去校准半径的差值,把这个值设为maxRadiusDelta,这样重量映射才会准确。- 精度问题:这种方法的精度肯定不如原来的3D Touch,因为接触面积会受手指大小、按压角度、屏幕清洁度影响,所以可以考虑添加多次采样平滑处理,减少数值波动。
额外提示
如果想要更好的体验,可以在界面上添加一个"重新校准"按钮,让用户随时重置基准值,避免因为手指位置变化导致的误差。
内容的提问来源于stack exchange,提问作者Emma
相关产品推荐
相关产品推荐

