Swift5下同frame的按钮与AwesomeSpotlight矩形在iPhone11系列模拟器错位问题
错位问题的核心原因
- 参考系不匹配:计算环绕矩形时取的是登录按钮相对于直接父视图的相对坐标,但AwesomeSpotlight的rect参数要求传入的是相对于应用主窗口根视图的绝对坐标。iPhone11系列因为有独立的安全区偏移,这个坐标系的偏差会被直接放大,最终出现明显错位。
- 布局读取时机不对:如果生成环绕框的代码在AutoLayout完成最终约束计算前执行,拿到的按钮frame只是布局占位值。旧设备因为安全区高度固定,偏差小到看不见;iPhone11系列的状态栏是44pt(旧设备为20pt)、底部还有34pt的Home指示条安全区,占位值和实际值的差值本来就大,错位就特别明显。
- 计算逻辑本身存在bug:原代码计算矩形y轴位置的时候,错误叠加了整个按钮高度再向下偏移10pt,本质上就没实现“环绕按钮”的效果——只是在按钮下方生成了矩形区域,在坐标计算刚好凑齐的旧设备上看起来对齐,在iPhone11系列上偏差叠加后错位直接显现。
- 混用视图与图层的属性取值:原代码取按钮宽高用
btnSignIn2.layer.bounds,取位置用btnSignIn2.frame,在存在视图transform、安全区适配的场景下,两个属性的参考系根本不统一,会进一步放大计算误差。
修复操作步骤
- 统一坐标参考系:替换原来的矩形计算逻辑,用系统自带的坐标转换方法,把按钮的frame换算成主窗口下的绝对坐标,不要直接拿父视图内的相对坐标计算。
- 核心替换代码:
// 获取当前真正承载界面的key窗口 guard let keyWindow = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else { return } // 把按钮的bounds直接转换成窗口坐标系下的绝对位置,无需手动累加坐标 let buttonAbsoluteRect = btnSignIn2.convert(btnSignIn2.bounds, to: keyWindow) // 生成保留10pt外间距的环绕矩形 let rect = CGRect( x: buttonAbsoluteRect.minX - 10, y: buttonAbsoluteRect.minY - 10, width: buttonAbsoluteRect.width + 20, height: buttonAbsoluteRect.height + 20 ) - 调整代码执行时机:把生成Spotlight提示的逻辑挪到视图完全布局完成的节点,比如
viewDidLayoutSubviews执行结束后,或者页面展示完成后用DispatchQueue.main.async把逻辑丢到下一个主线程循环执行,确保拿到的是经过安全区适配的真实按钮frame。 - 修正提示文字的位置计算:原代码计算
labelPosition的时候用UIScreen.main.bounds,在带刘海的iPhone11系列上,这个值是包含了系统状态栏、Home指示条的全屏幕尺寸,直接用会导致文字位置偏移,换成keyWindow.bounds来计算宽度和坐标范围即可。 - 移除所有对
UIScreen.main的坐标依赖:Xcode11开始的Scene架构下,iPhone11系列的屏幕尺寸如果从UIScreen读取,会存在数值偏差,所有涉及坐标范围、宽高的计算,都要基于实际承载视图的keyWindow来算。
内容的提问来源于stack exchange,提问作者Nguyễn Nhật Trường
相关产品推荐
相关产品推荐

