Realm 是一個跨平臺的移動資料庫引擎,於 2014 年 7 月發佈,準確來說,它是專門為移動應用所設計的數據持久化解決方案之一。 Realm 可以輕鬆地移植到您的項目當中,並且絕大部分常用的功能(比如說插入、查詢等等)都可以用一行簡單的代碼輕鬆完成! Realm 並不是對 Core Data 的
Realm 是一個跨平臺的移動資料庫引擎,於 2014 年 7 月發佈,準確來說,它是專門為移動應用所設計的數據持久化解決方案之一。
Realm 可以輕鬆地移植到您的項目當中,並且絕大部分常用的功能(比如說插入、查詢等等)都可以用一行簡單的代碼輕鬆完成!
Realm 並不是對 Core Data 的簡單封裝,相反地, Realm 並不是基於 Core Data ,也不是基於 SQLite 所構建的。它擁有自己的資料庫存儲引擎,可以高效且快速地完成資料庫的構建操作。
之 前我們提到過,由於 Realm 使用的是自己的引擎,因此, Realm 就可以在 iOS 和 Android 平臺上共同使用(完全無縫),並且支持 Swift 、 Objective-C 以及 Java 語言來編寫( Android 平臺和 iOS 平臺使用不同的 SDK )。
數以萬計的使用 Realm 的開發者都會發現,使用 Realm 比使用 SQLite 以及 Core Data 要快很多。下麵我們給出一個例子,分別展示 Core Data 和 Realm 在執行一個斷言查詢請求並且排序結果所使用的代碼量:
1 2 3 4 5 6 7 8 |
// Core Data
let fetchRequest = NSFetchRequest(entityName: "Specimen" )
let predicate = NSPredicate(format: "name BEGINSWITH [c]%@" , searchString)
fetchRequest.predicate = predicate
let sortDescriptor = NSSortDescriptor(key: "name" , ascending: true )
fetchRequest.sortDescriptors = [sortDescriptor]
let error = NSError()
let results = managedObjectContext?.executeFetchRequest(fetchRequest, error:&error)
|
而換成了 Realm 呢?您會驚嘆於 Realm 的簡單的:
1 2 3 |
// Realm
let predicate = NSPredicate(format: "name BEGINSWITH [c]%@" , searchString);
let specimens = Specimen.objectsWithPredicate(predicate).arraySortedByProperty( "name" , ascending: true )
|
使用 Realm 可以讓代碼變得十分簡潔,從而讓您的代碼易讀易寫。
綜上所述,我們之所以使用 Realm 的理由不外乎如下幾點:
-
跨平臺 :現在絕大多數的應用開發並不僅僅只在 iOS 平臺上進行開發,還要兼顧到 Android 平臺的開發。為兩個平臺設計不同的資料庫是愚蠢的,而使用 Realm 資料庫, iOS 和 Android 無需考慮內部數據的架構,調用 Realm 提供的 API 就可以完成數據的交換,實現 “ 一個資料庫,兩個平臺無縫銜接 ” 。
-
簡單易用 : Core Data 和 SQLite 冗餘、繁雜的知識和代碼足以嚇退絕大多數剛入門的開發者,而換用 Realm ,則可以極大地減少學習代價和學習時間,讓應用及早用上數據存儲功能。
-
可視化 : Realm 還提供了一個輕量級的資料庫查看工具,藉助這個工具,開發者可以查看資料庫當中的內容,執行簡單的插入和刪除數據的操作。畢竟,很多時候,開發者使用資料庫的理由是因為要提供一些所謂的 “ 知識庫 ” 。
本教程將會向您介紹 Realm 在 iOS 平臺上的簡單應用,即導入 Realm 框架、創建數據模型、執行查詢以及插入、更新和刪除記錄,以及使用既有的資料庫。
提示:原文教程寫於 2014 年,而 Realm 的版本更新得十分快,因此,本教程並不會拘泥於原文教程所述內容,而是根據 Realm 的版本更新進行相關修改。
原文作者提到,要在 Realm 抵達 1.0 版本的時候再來更新這篇教程,大家盡請期待吧!
讓我們開始吧
我 們將會以一個實際的項目來進行教程:假設您在西雙版納自然保護區覓得了一份職位 “ 監測員 ” ,職責是記錄這個 “ 動植物王國 ” 當中所發現物種的相關信息,包括種群數量、發現區域、年齡結構等等。因此,您需要一個助手來幫忙記錄您的發現,但是很可惜的是,保護區並沒有多餘的人手來 做您的助手(主要是沒錢)。所以沒有辦法,我們必須為自己製作一個虛擬的 “ 助手 ” ,也就是一個以 “ 物種監測 ” 命名的 APP ,這樣就可以隨手記錄我們的發現了!
在 Xcode 當中打開我們的起始項目。此時, MapKit 已經在項目當中建立好了,而且項目已經擁有了一些簡單的創建、更新和刪除物種信息的功能。
提示:如果您對 MapKit 的相關知識感興趣,可以查看 Introduction to MapKit tutorial ,這篇教程將會深入闡述 MapKit 是如何工作的。
現在,讓我們前往 Realm 的官網去下載 Realm 的框架吧: http://static.realm.io/downloads/cocoa/latest
Realm 的使用需求如下:
-
iOS ≥ 7 或者 Mac OS X ≥ 10.9
-
Xcode ≥ 6
-
現在 Realm 的版本為: 0.91.5
解壓下載下來的 Realm 壓縮包。在壓縮包中,我們可以看到一個名為 iOS 的文件夾。打開這個文件夾,然後將 Realm.framework 文件拖入到我們的起始項目中,最好拖放到 “Frameworks” 文件夾中以確保文件有序(強迫症患者 ~ )。
將框架文件拖入到項目當中
之後,一定要確保勾選了 Copy Items if needed 選項,然後單擊 Finish 按鈕就完成了往項目中添加框架的操作。
之後,定位到項目設置中 SISpeciesNotes 的 General 選項卡,然後在 Link Binary with Libraries 欄目中添加 libc++.dylib 動態庫文件。
然 後回到解壓的 Realm 文件夾中,打開名為 Swift 的文件夾,然後將裡面的 RLMSupport.swift 文件拖入到項目當中。這個文件包含了用於 Realm 相關類的 Swift 簡便方法,比如說 RLMResults 中的 Generator 方法,這樣就可以像使用原生數組一樣使用 Realm 數組了。
好的,我們的準備工作就完成了!您可以嘗試運行一下起始項目,以確保沒有任何錯誤產生。如果出現錯誤的話,請仔細查看上面所述的一些步驟,確保沒有任何疏漏發生。運行成功後的基本界面如下所示:
應用界面
Realm Browser 介紹
Realm 資源包中包含了一個很有用的實用工具,可以幫助我們更好地管理 Realm 資料庫,那就是 Realm Browser 。
Realm Browser 可以讓您輕鬆地讀寫 Realm 資料庫(以 .realm 結尾),因此我們無需頭疼如何去查看 Realm 專有資料庫的邏輯結構以及其中的數據,可視化的操作就如同 SQLite 的其他資料庫查看工具一樣,十分簡單、易用(雖然 Realm Browser 的功能還十分簡陋,真的只能讀寫而已)。
Realm Browser
Realm Browser 可以在解壓的 Realm 文件夾中的 browser 文件夾中找到。您也可以訪問 Realm GitHub repository 然後在其中的 tools/RealmBrowser 目錄中找到它。
您可以嘗試在 Realm Browser 中選擇 Tools -> Generate demo database 來試著探索一下 Realm Browser 的功能。
Realm 相關術語和主要類
為了幫助您更好地理解 Realm 的使用,下麵我們將會對 Realm 的相關術語和主要類進行一個大致的介紹:
-
RLMRealm : RLMRealm 是框架的核心所在,是我們構建資料庫的訪問點,就如同 Core Data 的管理對象上下文( managed object context )一樣。出於簡單起見, realm 提供了一個名為 defaultRealm 的單例,在本教程中我們就僅使用這個單例來完成我們所需的功能。當然,我們也可以導入外部已經編寫好的 realm 資料庫文件,也可以在我們不需要將數據保存在硬碟上時使用 “ 記憶體實例對象 ” ( in-memory realm instance ),此外,還可以同時使用多個資料庫文件。
-
RLMObject :這是我們自定義的 realm 數據模型。創建數據模型的行為將會影響到資料庫的結構。要創建一個數據模型,我們只需要繼承 RLMObject ,然後設計我們想要存儲的屬性即可。
-
關係 (Relationships) :通過簡單地在數據模型中聲明一個 RLMObject 類型的屬性,我們就可以創建一個 “ 一對多 ” 的對象關係。同樣地,藉助 RLMArray 我們還可以創建 “ 多對一 ” 和 “ 多對多 ” 的關係。
-
寫操作事務 (Write Transactions) :資料庫中的所有操作,比如創建、編輯,或者刪除對象,都必須在事務中完成。 “ 事務 ” 是指位於 beginWriteTransaction() 以及 commitWriteTransaction() 操作之間的代碼段。
-
查詢 (Queries) :要在資料庫中檢索信息,我們需要用到 “ 檢索 ” 操作。檢索最簡單的形式是對 RLMObject 對象發送 allObjects() 消息。如果需要檢索更複雜的數據,那麼還可以使用斷言( predicates )、複合查詢以及結果排序等等操作。
-
RLMResults :這個類是執行任何查詢請求後所返回的類,其中包含了一系列的 RLMObjects 對象。和 NSArray 類似,我們可以用下標語法來對其進行訪問,並且還可以決定它們之間的關係。不僅如此,它還擁有許多更強大的功能,包括排序、查找等等操作。
現在您應該對 Realm 有了一個大概的瞭解了,現在是時候來試著使用 Realm 來完成起始項目的剩餘工作了。
創建第一個數據模型
好了,前面我們廢話了這麼多,現在終於要開始使用資料庫了。首先我們要創建一個數據模型,也相當於創建資料庫的一個 “ 表 ” 。
右鍵選擇 Xcode 項目導航器中的 Model 組,然後選擇 New File -> iOS -> Source -> Swift File ,創建一個新的 swift 文件,將其命名為 SpeciesModel 並且確保選中了 SISpeciesNotes 對象。
提 示:您也許查看過 Realm 的開發文檔,它裡面介紹說可以使用 “ 插件 ” 來完成數據模型的簡單創建(也就是新建文件時,可以像新建 Core Data 數據模型文件一樣創建一個既定的模板數據模型),但是很遺憾的是,現在這個功能還只支持創建 OC 版本的數據模型文件,我們為了代碼的 “ 乾凈 ” ,就不採用這種方法。
打開 SpeciesModel.swift 文件,然後用以下代碼替換文件中的內容:
1 2 3 4 5 6 7 8 9 |
import UIKit
import Realm
class SpeciesModel: RLMObject {
dynamic var name = ""
dynamic var speciesDescription = ""
dynamic var latitude: Double = 0
dynamic var longitude: Double = 0
dynamic var created = NSDate()
}
|
上面的代碼添加了一些屬性來存儲信息: name 屬性存儲物種名稱, speciesDescription 存儲物種的描述信息。對於 Realm 中的一些特定的數據類型,比如說字元串,必須要初始化。在本例中,我們使用空字元串來進行初始化。
latitude 以及 longitude 存儲了物種的經緯度信息。在這裡我們將其類型設置為 Double ( CLLocationDegrees 是 Double 的別名),並且使用 0 來進行初始化。
最後, created 存儲了這個物種所創建的時間信息。 NSDate() 將會返回當前時間,因此我們就用這個值來初始化這個屬性
好了,現在我們就成功創建了第一個 Realm 數據模型了,要不要動動腦來完成一個小小的挑戰呢?
我們知道,這些物種將會被劃分為不同的 “ 類別 ” ,您的任務就是自行創建一個 “ 類別 ” 數據模型,這個文件將被命名為 CategoryModel.swift ,然後這個新的數據模型只要一個字元串類型的屬性 ——name 。
以下是解決方案的代碼:
1 2 3 4 5 |
import UIKit
import Realm
class CategoryModel: RLMObject {
dynamic var name = ""
}
|
我們現在擁有了 CategoryModel 數據模型了,下麵我們將通過某種方式將其與 SpeciesModel 數據模型關聯起來,搭建起 “ 關係 ” 。
重新回顧一下上一節的內容,我們可以通過簡單地聲明一個屬性來創建數據模型之間的關係。
打開 SpeciesModel.swift 文件,然後在 created 屬性下麵添加如下語句:
1 |
dynamic var category = CategoryModel()
|
這個語句設置了 “ 物種 ” 和 “ 類別 ” 之間的 “ 一對多 ” 關係,這就意味著每個物種都只能夠擁有一個類別,但是一個類別可以從屬於多個物種。
好的,我們創建完了一個基礎數據模型了,現在是時候向資料庫中添加數據了!
添加數據
每當用戶添加了一個新的物種標記,用戶就可以對這個標記進行修改,比如說設置物種名字,選擇類別等等。打開 CategoriesTableViewController.swift 文件。這個視圖控制器將要在這個表視圖中顯示類別清單,以便用戶可以選擇。
因此,我們需要在應用初始運行時,給用戶提供幾個預設的類別以供選擇。
在類定義當中添加以下方法,別忘了在文件頂部導入 Realm 框架( import Realm ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private func populateDefaultCategories() {
self.results = CategoryModel.allObjects() // 1
if results.count == 0 { // 2
let realm = RLMRealm.defaultRealm() // 3
realm.beginWriteTransaction() // 4
let defaultCategories = Categories.allValues // 5
for category in defaultCategories {
// 6
let newCategory = CategoryModel()
newCategory.name = category
realm.addObject(newCategory)
}
realm.commitWriteTransaction() // 7
self.results = CategoryModel.allObjects()
}
}
|
對應的標號註釋如下:
-
allobjects() 方法將會返回指定對象的所有元素,在本例中,我們向資料庫中的 CategoryModel 對象發送了一個查詢請求,返回這個表當中的所有行信息。註意的是,這裡我們得到的是一個 RLMResults 對象,這個對象用來存放我們的查詢結果。
-
如果查詢結果中的元素數量為 0 ,那麼就說明資料庫當中沒有類別信息的相關記錄,那麼就意味著這是用戶第一次啟動應用。
-
我們訪問預設的 realm 單例對象,然後將其用 realm 變數簡單表示,以供訪問
-
這一步將在預設 realm 資料庫中啟動一個事務 —— 現在,我們就可以向資料庫當中添加記錄了。
-
這裡我們使用已經定義過的 Categories 枚舉來創建一個含有全部預設類別的數組。
-
對於每個類別名稱來說,我們創建了一個對應的 CategoryModel 實例對象,然後設置其 name 屬性,最後將這個對象添加到 realm 當中。
-
當我們添加完所有的類別之後,調用 commitWriteTransaction() 方法來關閉事務,並且向資料庫提交數據。
只 有調用了 commitWriteTransaction() 方法,我們之前做的所有關於事務的操作才能夠被成功運行,因為這涉及到 Realm 的內部處理的問題了。您可以像上面我們做的那樣,執行一些簡單的創建操作,或者您可以執行一些複雜的操作,比如說同時創建、更新、刪除多個對象等等。
然後在 viewDidLoad() 方法的底部加入以下代碼:
1 |
populateDefaultCategories()
|
這個方法將會在視圖載入的過程中,添加我們的測試用類別,並且執行向資料庫寫入數據的操作。
好了,現在我們的資料庫當中已經有了一些數據了,我們需要更新一下表試圖數據源相關方法,以顯示這些類別。找到 tableView(_:cellForRowAtIndexPath:) 方法,然後用以下代碼替換它:
1 2 3 4 5 |
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier( "CategoryCell" , forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = (results[UInt(indexPath.row)] as! CategoryModel).name
return cell
}
|
這個聲明語句從 results 對象當中讀取對應行的名稱,然後設置到單元格的文本標簽上面顯示。
接下來,添加一個新的屬性:
1 |
var selectedCategory: CategoryModel!
|
我們用這個屬性來存儲當前選中的類別。
找到 tableView(_: willSelectedRowAtIndexPath:) ,然後用以下代碼替換它:
1 2 3 4 |
override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
selectedCategories = self.results[UInt(indexPath.row)] as! CategoryModel
return indexPath
}
|
上面聲明的方法將會在用戶點擊某個單元格的時候,將用戶點擊的類別存儲在 selectedCategory 屬性當中。
編 譯並運行這個應用,然後嘗試定位到某個您感興趣的位置(使用模擬器的位置模擬),然後點擊右上角的 “+” 按鈕創建一個新的標記點。點選地圖上的這個標記點,然後點擊其彈出來的氣泡,接下來會彈出這個標記點的詳細信息。隨後,點擊類別文本框,就可以看到如下圖 所示的類別列表了:
類別列表
您可以選擇其中一個類別,不過這個操作僅僅只是將其保存到屬性當中。如果您感興趣,可以前往模擬器的 Documents 目錄下麵,使用 Realm Browser 查看我們生成的資料庫,在裡面就可以看到我們寫入的數據了,這是不是很令人激動呢?
使用 Realm Browser
通 常情況下,使用 defaultRealm() 方法生成的資料庫文件將會存放在 /Users/(Your Account)/Library/Developer/CoreSimulator/Devices/(Simulator ID)/data/Containers/Data/Application/(Application ID)/Documents/ 路徑下麵,名為 default.realm 。 Simulator ID 指的是您運行的模擬器的 ID , Application ID 指的是這個應用所分配到的 ID 。
如果您仍然不清楚這個 Realm 資料庫在哪兒的話,那麼使用如下語句,就可以列印處這個資料庫所在的完整位置了:
1 |
println(RLMRealm.defaultRealm().path)
|
在這個 Documents 目錄下麵,我們可能會看到兩個文件。一個是 default.realm 文件,這個是資料庫文件,裡面就是數據的存放點了。而另一個則是 default.realm.lock 文件,這個文件也有可能不會存在,它是用來當資料庫文件被使用時,防止其它應用對其進行修改的一個文件。
雙擊這個 default.realm 文件,就可以使用 Realm Browser 打開了:
Realm Browser 打開的 default.realm 文件
註意:如果 default.realm 已經在其它應用中打開了,那麼強行打開它就可能會出現異常。
.lock 文件就可以防止對 default.realm 文件的重覆操作,在使用 Realm Browser 打開資料庫文件前,請先確保應用沒有在運行,然後刪除 .lock 文件,才能打開。
一旦資料庫在 Realm Browser 中被打開,您將會看到 CategoryModel 類中擁有 6 個對象,這就意味著這個 “ 表 ” 中已經存放了 6 個記錄了。點擊這個類就可以查看這個類當中擁有的具體對象信息。
增加類別
好了,現在我們就可以來實現 “ 為某個物種添加類別 ” 的功能了。
打開 AddNewEntryController.swift ,然後向類中添加以下屬性:
1 |
var selectedCategory: CategoryModel!
|
我們將會用這個屬性來存儲我們在 CategoriesTableViewController 選中的類別。
接下來,找到 unwindFromCategories(segue:) 方法,然後在方法底部添加以下代碼:
1 2 |
selectedCategory = categoriesController.selectedCategories
categoryTextField.text = selectedCategory.name
|
這個方法會在用戶從 categoriesTableViewController 中選擇了一個類別後被調用。在這裡,我們獲取到了這個選擇的類別,然後將其存儲在本地屬性 selectedCategory 當中,接著,我們將它的值填充到文本框裡面。
現在,我們已經完成了類別的獲取,接下來就是要創建第一個物種了!
仍然還是在 AddNewEntryController.swift 當中,向類中再添加一個屬性:
1 |
var species: SpeciesModel!
|
這個屬性將會存儲一個新的物種數據模型對象。
接下來,導入 Realm 框架,然後向類中添加以下方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
func addNewSpecies() {
let realm = RLMRealm.defaultRealm() // 1
realm.beginWriteTransaction() // 2
let newSpecies = SpeciesModel() // 3
// 4
newSpecies.name = nameTextField.text
newSpecies.category = selectedCategory
newSpecies.speciesDescription = descriptionTextView.text
newSpecies.latitude = selectedAnnotation.coordinate.latitude
newSpecies.longitude = selectedAnnotation.coordinate.longitude
realm.addObject(newSpecies) // 5
realm.commitWriteTransaction() // 6
self.species = newSpecies
}
|
對應的標號註釋如下:
-
獲取預設的 Realm 資料庫
-
開啟一個事務序列,準備寫入數據
-
創建一個 Species 對象實例
-
接著,設置這個對象的相關值。這些值來自於用戶界面的文本輸入框。
-
向 realm 中寫入新的 Species 對象
-
最後,使用 commitWriteTransaction() 提交寫操作事務
在這裡,我們需要使用 “ 輸入驗證 ” ,來確保用戶的輸入是正確的。在工程中已經有了一個存在的 validateFields() 方法來執行輸入驗證的工作,以確保物種名稱和描述不能為空。我們剛剛增加了設置類別的功能,那麼我們應該也要確保類別選擇不能為空。
在 validateFields() 方法中找到以下代碼:
1 |
if nameTextField.text.isEmpty || descriptionTextView.text.isEmpty {
|
將其變更為:
1 |
if nameTextField.text.isEmpty || descriptionTextView.text.isEmpty || selectedCategory == nil {
|
這個方法經能夠確保所有的文本框都有值,並且用戶也已經選擇了一個類別。
接下來,向類中添加以下方法:
1 2 3 4 5 6 7 8 9 10 |
override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool {
if validateFields() {
if species == nil {
addNewSpecies()
}
return true
} else {
return false
}
}
|
在上面的代碼中,我們調用了輸入驗證的方法,如果所有文本框都有值的話,那麼就可以添加一個新的物種。
編譯並運行您的應用,單擊 “+” 按鈕來創建一個新的物種。然後輸入其名稱和描述,選擇一個類別,接著單擊 “ 保存 ” 按鈕來將這個物種添加到資料庫中。
添加新的數據
視圖消失了 —— 等等,怎麼什麼都沒有發生呢?什麼情況?
哦對了,我們已經向 Realm 資料庫提交了一個數據,但是我們還沒有在地圖上做出相應的設置和改變。
檢索數據
既然我們已經向資料庫中添加了一個物種了,那麼現在我們希望它能夠在地圖上顯示出來。
如果您想要檢視這個心數據,那麼打開 Realm Browser 就可以查看數據了。記住要先退出模擬器。
添加的物種信息
我 們僅僅只能夠看見孤零零的一條記錄,裡面存儲了記錄的名稱、描述信息、經緯度信息、添加的時間。還有最重要的,就是我們看到了連接到 CategoryModel 的 category 記錄,這就意味著我們已經創建好了物種和類別的 “ 一對多 ” 關係。點擊這個藍色的超鏈接,我們就可以查看 CategoryModel 的相關數據了。
好的,回到正題,我們現在需要在地圖上顯示新添加的數據。
打開 SpeciesAnnotation.swift ,然後向類中添加一個新的屬性:
1 |
var species: SpeciesModel?
|
這個屬性將會為這個標記點保存它所擁有的物種信息。
接下來,用以下代碼替換構造器:
1 2 3 4 5 6 |
init(coordinate: CLLocationCoordinate2D, title: String, sub: Categories, species: SpeciesModel? = nil) {
self.coordinate = coordinate
self.title = title
self.subtitle = sub.rawValue
self.species = species
}
|
我們所做的改變,就是給這個構造器方法添加了一個帶預設值的構造器參數,以便可以對 species 屬性進行賦值。預設值為 nil ,這意味著我們可以忽略這個參數,使用前面三個參數進行初始化也是沒有任何問題的。
打開 MapViewController.swift ,然後向類中添加一個新屬性(同樣地,別忘了導入 Realm ):
1 |
var results: RLMResults?
|
如果我們想要在用屬性來存儲一系列物種,那麼我們需要將這個屬性聲明為 RLMResults 類型。要記住,我們是不能夠初始化 RLMResults 對象的,我們必須要通過查詢操作來獲取它的值。
現在我們需要一些方法來獲取所有的物種數據。仍然還是在 MapViewController.swift 當中,向類中添加如下方法:
1 2 3 4 5 6 7 8 9 10 11 12 |
func populateMap() {
mapView.removeAnnotations(mapView.annotations) // 1
if let results = SpeciesModel.allObjects() { // 2
self.results = results
for result in results {
let species = result as! SpeciesModel
let coordinate = CLLocationCoordinate2DMake(species.latitude, species.longitude)
let speciesAnnotation = SpeciesAnnotation(coordinate: coordinate, title: species.name, sub: Categories(rawValue: species.category.name)!, species: species) // 3
mapView.addAnnotation(speciesAnnotation) // 4
}
}
}
|
對應的標號註釋如下:
-
首先,我們先清除了地圖上所有存在的標記點,這樣我們就不用考慮其他的要素
-
然後,我們從 Realm 資料庫中獲取 Species 的全部數據
-
我們在此創建了一個自定義的 SpeciesAnnotation
-
最後,我們往 MKMapView 上添加這個標記點
好的,現在我們可以在某處地方弔用這個方法了。找到 viewDidLoad() 然後將這個方法加入到這個方法底部:
1 |
populateMap()
|
這樣就確保了每當地圖視圖控制器載入的時候,地圖就能夠顯示 Species 標記點。
接著,我們僅需要修改標記點的名稱和類別即可。找到 unwindFromAddNewEntry() ,然後使用下列代碼替換掉該方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@IBAction func unwindFromAddNewEntry(segue: UIStoryboardSegue) {
let addNewEntryController = segue.sourceViewController as! AddNewEntryController
let addedSpecies = addNewEntryController.species
let addedSpeciesCoordinate = CLLocationCoordinate2DMake(addedSpecies.latitude, addedSpecies.longitude)
if lastAnnotation != nil {
mapView.removeAnnotation(lastAnnotation)
} else {
for annotation in mapView.annotations {
let currentAnnotation = annotation as! SpeciesAnnotation
if currentAnnotation.coordinate.latitude == addedSpeciesCoordinate.latitude && currentAnnotation.coordinate.longitude == addedSpeciesCoordinate.longitude {
mapView.removeAnnotation(currentAnnotation)
break
}
}
}
let annotation = SpeciesAnnotation(coordinate: addedSpeciesCoordinate, title: addedSpecies.name, sub: Categories(rawValue: addedSpecies.category.name)!, species: addedSpecies)
mapView.addAnnotation(annotation)
lastAnnotation = nil
}
|
這 個方法將會在我們從 AddNewEntryController 返回的時候被調用,然後這時候就會有一個新的物種被添加到地圖上方。當我們添加了一個新的物種到地圖上,那麼就會產生一個標記圖標。然後我們想要根據物種 的類別來改變其圖標的樣式,在這個代碼裡面,我們就是簡單的移除了最後添加的這個標記點,然後將其替換為有名稱和類別的標記點。
編譯並運行您的應用,創建一些不同的物種種類來查看現在地圖是什麼樣式的吧!
添加的標記點效果
另外一個視圖
您 或許已經註意到在地圖視圖的左上角有一個 “ 編輯 ” 的按鈕。為了更好地管理地圖上的記錄點,我們這個應用設置了一個基於文本的表視圖,用來列出地圖上所有的記錄點,這個視圖我們現在命名為 “ 記錄 ” 視圖。現在,這個表視圖仍然還是空的,現在我們就來向裡面填充數據吧!
打開 LogViewController.swift ,然後將 species 屬性替換成以下形式(同樣地,要導入 Realm ):
1 |
var species: RLMResults!
|
在上面的代碼中,我們用 RLMResults 替換掉了之前的一個空數組占位符,這個操作和我們在 MapViewController 所做的一樣。
接下來,找到 viewDidLoad() 方法,然後在 super.viewDidLoad() 語句下添加以下代碼:
1 |
species = SpeciesModel.allObjects().sortedResultsUsingProperty( "name" , ascending: true )
|
這行代碼會將資料庫中的所有物種全部輸出到 species 當中,並且按照名字進行排列。
接下來,用以下代碼替換 tableView(_:cellForRowAtIndexPath:) :
1 2 3 4 5 6 7 8 9 |
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier( "LogCell" ) as! LogCell
var speciesModel: SpeciesModel!
speciesModel = species[UInt(indexPath.row)] as! SpeciesModel
cell.titleLabel.text = speciesModel.name
cell.subtitleLabel.text = speciesModel.category.name
cell.iconImageView.image = getImageOfSpecies(speciesModel.category.name)
return cell
}
|
這個方法將會展示物種的名字和物種的類別,以及其圖標。
編譯並運行應用,單擊左上角的 “ 編輯 ” 按鈕,然後您就會在表視圖中看到我們之前錄入的物種信息,如圖所示:
記錄界面
刪除記錄
現在我們已經學習瞭如何在 Realm 中創建記錄數據,但是如果我們不小心添加了錯誤的標記點,或者想要移除之前添加過的物種數據,那麼我們應該要怎麼做呢?因此,我們就需要添加從 Realm 中刪除數據的功能。您會發現這是一個非常簡單的操作。
打開 LogViewController.swift 文件,然後添加以下方法: