[SwiftUI 100天] Bucket List - part3

貓克杯發表於2020-03-16
譯自 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及計算機程式設計的相關文章,以及優秀國外文章翻譯,歡迎關注~

[SwiftUI 100天] Bucket List - part3


相關文章