Swift/SwiftUI 踩坑记

这里记录 Swift/SwiftUI 开发过程中踩过的坑。

注:目前的项目混合使用 SwiftUI 和 UIKit,不能保证纯 SwiftUI 也有类似问题。

弹出键盘导致布局混乱

坑:

使用 push 弹出全屏幕的 View,该 View 中有一个 TextField,获得焦点后返回,主屏布局混乱,底部多了一块空白区域,所有元素事件响应失效,主屏是一个 ScrollView。逐步盘查后发现,注释掉子页面的 view.becomeFirstResponder() 后问题不复现。

合理的行为:

子屏幕不应该影响主屏幕的行为。

修复方案:

禁用键盘的安全区域。

.ignoresSafeArea(.keyboard)

启发

  • 新开发的功能出问题后,应逐步盘查,用简单的 View 代替复杂的页面,逐步缩小范围,来定位问题。
  • 如果确定是事件响应引起的问题,可能与键盘相关。
  • 如果页面底部多了一块空白区域,可能是键盘收起后布局没有重新计算。

@AppStorage 的 key 不能以 @ 开头

坑:

@AppStorage 能非常方便的存取 UserDefaults 中的数据,但是如果 key 使用 @ 开头的话,会报 crash。

合理的行为:

不应该报 crash,或者文档中应该有类似的说明。

修复方案:

不使用 @ 开头的字符串做为 @AppStorage 的 key。如果是组合的字符串,保证 @ 之前的字符串不能为空。

启发

  • @ 是 objc 中字符串的关键字,目前 SwiftUI 的底层使用 UIKit,甚至 objc 的关键字
  • 这个问题很难发现,只有踩坑才能知道
  • 需要测试所有逻辑的路径,即使业务场景不存在

菜单 Menu

坑:

SwiftUI Menu 使用非常方便,但是在点开 Menu 菜单没有合上的时候,去点击/滑动同一个页面的其它元素 (使用 onTapGesture 响应),这时页面卡死。

合理的行为:

此时,应该自动收起 Menu 的菜单,然后响应接下来的点击或者滑动事件。

修复方案:

当 Menu 打开时,给整个页面添加透明的蒙层,在 Menu 开启的时候,点击/滑动其它元素,隐藏蒙层。代码片段如下:

YourScreen {}
    .overlay {
      if menuIsOpen {
        Color.white.opacity(0.001)
          .ignoresSafeArea()
          .frame(maxWidth: .infinity, maxHeight: .infinity)
          .onTapGesture {
            menuIsOpen = false
          }
          .gesture(DragGesture(minimumDistance: 0)
            .onEnded { _ in
              menuIsOpen = false
            }
          )
      }
    }

启发

  • 测试 Menu 时,记得在菜单打开的时候点击页面其它元素,或者滑动页面
  • 测试 Menu 时,记得在菜单打开的时候点击页面上的另一个 Menu 元素
  • 测试 Menu 时,记得旋转屏幕
  • 透明蒙层时一个好的方案

如果你喜欢这篇文章,欢迎赞赏作者以示鼓励