翻译翻译,什么叫不成熟的 UI 框架 —— SwiftUI 的奇葩 bug

1,476 阅读2分钟

事情是这样的。

我最近在尝试用 SwiftUI 做一个相对复杂的应用,想看看不使用 UIKit 的转化工具,也就是不用 UIViewRepresentableUIViewControllerRepresentable,仅靠 SwiftUI 自己能做到什么程度。

刚开始的时候,SwiftUI 写起来的确很爽。DSL 形式的代码让我可以快速地把模糊的概念转化成 UI。但是项目逐渐复杂起来后,就会遇到各种小的问题,比如子视图不更新、手势没反应之类的。这些问题基本可以靠 stackoverflow 或者国外的一些博客解决,它们也在我的预期之中,毕竟资料不详细也应该是新框架的通病。

直到今天,在遇到了这样的一个 bug,我有点忍不住了...

为了展现这个 bug,让我们先写一段正常的代码:

class Team: ObservableObject {
    @Published var members: [Soldier] = [Soldier(name: "Soldier 0")]

    func addSoldier() {
        members.append(Soldier(name: "Soldier \(members.count)"))
    }

    func removeCurrentSoldier() {
        if members.count > 1 {
            members.removeLast()
        }
    }
}

struct Soldier {
    var name: String
}

struct ContentView: View {
    @ObservedObject var team = Team()
    
    var body: some View {
            VStack {
                ForEach(team.members.indices, id: \.self) { i in
                    TextField("", text: $team.members[i].name)
                }
                HStack {
                    Button("+") { team.addSoldier() }
                    Button("-") { team.removeCurrentSoldier() }
                }
            }
    }
}

这段代码应该很好理解。ContentView 会展示 team 中保存的士兵小队的名单,通过下面的两个按钮可以增减小队的成员,在 TextField 中也可以更新成员的名字。运行起来大致像这样:

2021-03-12 17.33.13.gif

在上面这段代码的基础上,如果我们要给每个士兵加一个头像,把

TextField("", text: $team.members[i].name)

改为:

HStack {
    Image(systemName: "person.fill")
    TextField("", text: $team.members[i].name)
}

再次运行程序,尝试删除操作,程序会直接报错退出。。。

2021-03-12 17.40.28.gif

甚至当我们不加上这个 Image,仅仅是在 TextField 外面套一个 HStackVStack,也会出现同样的问题。在 stackoverflow 也有不少人遇到了类似的问题,但回答中提出的 work around 都不适用。

为什么在外面套一个布局容器会影响控件的行为呀!

如果是一般的开源 UI 框架,出现这种奇葩问题我们可以跑去社区提 issue,或者尝试自己用源码修 bug。但是因为 SwiftUI 是个闭源的框架,貌似就只能给苹果官方提 bug report 了... 这种情况下的反馈速度,实在是不敢让人有所期望... 最高效的方法可能是改掉自己的设计,然后祈求新的方案不会再遇到类似的错误。

虽然我应该还是会继续完成手头的这个项目,但是这个 bug 极大损伤了我对 SwiftUI 在短期内能够实际应用的信心,因为这样的问题说明 SwiftUI 缺少的可能不仅仅是完善的文档与配套工具,它内部的一些核心机制还处在不太稳定的状态。在这样的框架上制作应用,无疑是在刀尖上跳舞。不过另一个角度,如果苹果没有很快的修复这些潜在的问题,可能社区中会出现一系列为 SwiftUI 定制的用于维护状态,从而间接调控生命周期的库,所以说不好这也是开发者们的一些机会。

本文就到这里了,如果你也觉得这个 bug 有点匪夷所思,不妨点个赞~