[SwiftUI 100天] Bucket List - part3

429 阅读3分钟
译自 Extending existing types to support ObservableObject
更多内容欢迎关注公众号 「Swift花园」

扩展已知类型支持 ObservableObject

用户现在可以地图上放置饰针,但是还不能对这些饰针做任何操作 —— 他们不能附加标题和副标题。解决这个问题需要费一些思考,因为MKPointAnnotation 的标题和副标题是用可选字符串,而 SwiftUI 并不允许我们绑定可选型到文本控件。

解决的思路有多种,但最简单的一种是给 MKPointAnnotation 写一个扩展,为 titlesubtitle 封装计算属性,这意味着我们需要让这个类遵循 ObservableObject 协议。 计算属性的名称可以由你任起,比如 nameinfodetail等等。不过你会发现简单地在原名称前加一个 wrapped 前缀会更方便记忆。

新建一个叫 MKPointAnnotation-ObservableObject.swift 的 Swift 文件,把 import 语句由 Foundation 改为 MapKit,代码如下:

extension MKPointAnnotation: ObservableObject {
    public var wrappedTitle: String {
        get {
            self.title ?? "Unknown value"
        }

        set {
            title = newValue
        }
    }

    public var wrappedSubtitle: String {
        get {
            self.subtitle ?? "Unknown value"
        }

        set {
            subtitle = newValue
        }
    }
}

注意,我并没有把上面的属性标记为 @Published。这样做没问题的原因在于实际上我们并不需要在这些属性改变的时候发布变化,因为并不需要在用户输入的时候一直刷新视图。

有了上面这个扩展,我们就在 MKPointAnnotation 拥有了两个非可选型的属性,可以用它们和 SwiftUI 视图里的 UI 控件做绑定。于是我们就可以创建编辑地点标记的 UI 了。

创建一个新的 SwiftUI 视图,名字叫 “EditView”,然后引入 MapKit ,代码如下 :

struct EditView: View {
    @Environment(\.presentationMode) var presentationMode
    @ObservedObject var placemark: MKPointAnnotation

    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("地点名称", text: $placemark.wrappedTitle)
                    TextField("描述", text: $placemark.wrappedSubtitle)
                }
            }
            .navigationBarTitle("编辑地点")
            .navigationBarItems(trailing: Button("完成") {
                self.presentationMode.wrappedValue.dismiss()
            })
        }
    }
}

同时修改预览类的代码以便通过编译:

struct EditView_Previews: PreviewProvider {
    static var previews: some View {
        EditView(placemark: MKPointAnnotation.example)
    }
}

我们需要在两个地方显示这个视图。第一个是在 ContentView:当用户新增一个地点时,我们希望他们立即编辑地点信息,第二个是在他们点击 alert 弹窗编辑按钮时展示。

这两种情况都通过一个布尔条件来触发,我们添加一个@State 属性到 ContentView

@State private var showingEditScreen = false

把之前 // edit this place 的注释替换为下面的代码:

self.showingEditScreen = true

新增地点时不仅要设置这个属性为 true ,还需要设置选定的地点,也就是 selectedPlace 属性,在self.locations.append(newLocation) 代码下面添加以下代码:

self.selectedPlace = newLocation
self.showingEditScreen = true

最后,我们需要绑定showingEditScreen到一个 sheet ,以便我们的 EditView 能够展示。注意,我们不能用来 if let 来解包 selectedPlace ,所以我们就做一个简单的非空检查,然后直接使用。

添加下面的 sheet() modifier 到 ContentView,在 alert 之后:

.sheet(isPresented: $showingEditScreen) {
    if self.selectedPlace != nil {
        EditView(placemark: self.selectedPlace!)
    }
}

饰针标题和副标题的编辑功能到此为止。


相关文章:

[SwiftUI 100 天] Bucket List - part1

[SwiftUI 100 天] Bucket List - part2

[SwiftUI 100 天] Bucket List - part4


我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~