優於CoreData的Realm資料庫基礎教程

来源:http://www.cnblogs.com/jgCho/archive/2016/03/17/5286444.html
-Advertisement-
Play Games

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” 文件夾中以確保文件有序(強迫症患者 ~ )。

2.jpg

將框架文件拖入到項目當中

之後,一定要確保勾選了 Copy Items if needed 選項,然後單擊 Finish 按鈕就完成了往項目中添加框架的操作。

之後,定位到項目設置中 SISpeciesNotes 的 General 選項卡,然後在 Link Binary with Libraries 欄目中添加 libc++.dylib 動態庫文件。

然 後回到解壓的 Realm 文件夾中,打開名為 Swift 的文件夾,然後將裡面的 RLMSupport.swift 文件拖入到項目當中。這個文件包含了用於 Realm 相關類的 Swift 簡便方法,比如說 RLMResults 中的 Generator 方法,這樣就可以像使用原生數組一樣使用 Realm 數組了。

好的,我們的準備工作就完成了!您可以嘗試運行一下起始項目,以確保沒有任何錯誤產生。如果出現錯誤的話,請仔細查看上面所述的一些步驟,確保沒有任何疏漏發生。運行成功後的基本界面如下所示:

3.jpg

應用界面

Realm Browser 介紹

Realm 資源包中包含了一個很有用的實用工具,可以幫助我們更好地管理 Realm 資料庫,那就是 Realm Browser

Realm Browser 可以讓您輕鬆地讀寫 Realm 資料庫(以 .realm 結尾),因此我們無需頭疼如何去查看 Realm 專有資料庫的邏輯結構以及其中的數據,可視化的操作就如同 SQLite 的其他資料庫查看工具一樣,十分簡單、易用(雖然 Realm Browser 的功能還十分簡陋,真的只能讀寫而已)。

4.jpg

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() } }

對應的標號註釋如下:

  1. allobjects() 方法將會返回指定對象的所有元素,在本例中,我們向資料庫中的 CategoryModel 對象發送了一個查詢請求,返回這個表當中的所有行信息。註意的是,這裡我們得到的是一個 RLMResults 對象,這個對象用來存放我們的查詢結果。

  2. 如果查詢結果中的元素數量為 0 ,那麼就說明資料庫當中沒有類別信息的相關記錄,那麼就意味著這是用戶第一次啟動應用。

  3. 我們訪問預設的 realm 單例對象,然後將其用 realm 變數簡單表示,以供訪問

  4. 這一步將在預設 realm 資料庫中啟動一個事務 —— 現在,我們就可以向資料庫當中添加記錄了。

  5. 這裡我們使用已經定義過的 Categories 枚舉來創建一個含有全部預設類別的數組。

  6. 對於每個類別名稱來說,我們創建了一個對應的 CategoryModel 實例對象,然後設置其 name 屬性,最後將這個對象添加到 realm 當中。

  7. 當我們添加完所有的類別之後,調用 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 屬性當中。

編 譯並運行這個應用,然後嘗試定位到某個您感興趣的位置(使用模擬器的位置模擬),然後點擊右上角的 “+” 按鈕創建一個新的標記點。點選地圖上的這個標記點,然後點擊其彈出來的氣泡,接下來會彈出這個標記點的詳細信息。隨後,點擊類別文本框,就可以看到如下圖 所示的類別列表了:

5.jpg

類別列表

您可以選擇其中一個類別,不過這個操作僅僅只是將其保存到屬性當中。如果您感興趣,可以前往模擬器的 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 打開了:

6.jpg

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 }

對應的標號註釋如下:

  1. 獲取預設的 Realm 資料庫

  2. 開啟一個事務序列,準備寫入數據

  3. 創建一個 Species 對象實例

  4. 接著,設置這個對象的相關值。這些值來自於用戶界面的文本輸入框。

  5. 向 realm 中寫入新的 Species 對象

  6. 最後,使用 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 } }

在上面的代碼中,我們調用了輸入驗證的方法,如果所有文本框都有值的話,那麼就可以添加一個新的物種。

編譯並運行您的應用,單擊 “+” 按鈕來創建一個新的物種。然後輸入其名稱和描述,選擇一個類別,接著單擊 “ 保存 ” 按鈕來將這個物種添加到資料庫中。

7.jpg

添加新的數據

視圖消失了 —— 等等,怎麼什麼都沒有發生呢?什麼情況?

哦對了,我們已經向 Realm 資料庫提交了一個數據,但是我們還沒有在地圖上做出相應的設置和改變。

檢索數據

既然我們已經向資料庫中添加了一個物種了,那麼現在我們希望它能夠在地圖上顯示出來。

如果您想要檢視這個心數據,那麼打開 Realm Browser 就可以查看數據了。記住要先退出模擬器。

添加的物種信息

8.jpg

添加的物種信息

我 們僅僅只能夠看見孤零零的一條記錄,裡面存儲了記錄的名稱、描述信息、經緯度信息、添加的時間。還有最重要的,就是我們看到了連接到 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 } } }

對應的標號註釋如下:

  1. 首先,我們先清除了地圖上所有存在的標記點,這樣我們就不用考慮其他的要素

  2. 然後,我們從 Realm 資料庫中獲取 Species 的全部數據

  3. 我們在此創建了一個自定義的 SpeciesAnnotation

  4. 最後,我們往 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 返回的時候被調用,然後這時候就會有一個新的物種被添加到地圖上方。當我們添加了一個新的物種到地圖上,那麼就會產生一個標記圖標。然後我們想要根據物種 的類別來改變其圖標的樣式,在這個代碼裡面,我們就是簡單的移除了最後添加的這個標記點,然後將其替換為有名稱和類別的標記點。

編譯並運行您的應用,創建一些不同的物種種類來查看現在地圖是什麼樣式的吧!

9.jpg

添加的標記點效果

另外一個視圖

您 或許已經註意到在地圖視圖的左上角有一個 “ 編輯 ” 的按鈕。為了更好地管理地圖上的記錄點,我們這個應用設置了一個基於文本的表視圖,用來列出地圖上所有的記錄點,這個視圖我們現在命名為 “ 記錄 ” 視圖。現在,這個表視圖仍然還是空的,現在我們就來向裡面填充數據吧!

打開 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 }

這個方法將會展示物種的名字和物種的類別,以及其圖標。

編譯並運行應用,單擊左上角的 “ 編輯 ” 按鈕,然後您就會在表視圖中看到我們之前錄入的物種信息,如圖所示:

10.jpg

記錄界面

刪除記錄

現在我們已經學習瞭如何在 Realm 中創建記錄數據,但是如果我們不小心添加了錯誤的標記點,或者想要移除之前添加過的物種數據,那麼我們應該要怎麼做呢?因此,我們就需要添加從 Realm 中刪除數據的功能。您會發現這是一個非常簡單的操作。

打開 LogViewController.swift 文件,然後添加以下方法:

1 2 3 4 5 6 7 8 9 func deleteRowAtIndexPath(indexPath: NSIndexPath) { let realm = RLMRealm.defaultRealm() // 1 let objectToDelete = species[UInt(indexPath.row)] as! SpeciesModel // 2 realm.beginWriteTransaction() // 3 realm.deleteObject(objectToDelete) // 4 realm.commitWriteTransaction() // 5 species = SpeciesModel.allObjects().sortedResultsUsingProperty("name", ascending: true// 6 tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)&
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 註:toString方法會去調用每個值的toString方法,toLocaleString會去調用每個值的toLocaleString方法 由於IE7及更早版本會返回HTML中所有可能的特性,所以對上述函數加以改進,讓它返回指定特性。每個特性節點都有一個specified屬性,為true,要麼在HT
  • 響應式設計是指在不同解析度的設備中,網頁佈局可以自適應的調整。這種彈性化的佈局使網站在不同設備中的佈局都比較合理,可以為不同終端的用戶提供更加舒適的界面和更好的用戶體驗,其根本理念是使原本 PC 上的網站相容手機和平板。簡單理解就是可以讓一個網站相容多個終端,而不是為每個終端做一個特定的版本,響應式
  • 3.7函數 3.7.1 理解參數 arguments對象與命名參數的關係: 3.7.2 沒有重載 沒有重載 模擬重載
  • h5 canvas 畫圖
  • CSS選擇器包括標簽選擇器、ID選擇器、類選擇器、偽類和偽對象選擇器、子選擇器、相鄰選擇器、屬性選擇器、通用選擇器、包含選擇器、分組選擇器、指定選擇器等選擇器,分為標簽選擇器、ID選擇器、類選擇器、特殊選擇器這四類。 以下是我總結的5種常用的CSS選擇器。
  • 前段時間,同事又來咨詢一個問題了,說手機端動不動拍照就好幾M高清大圖,上傳伺服器太慢,問問我有沒有可以壓縮圖片並上傳的js插件,當然
  • 這是一個12306火車票訂票項目源碼,火車票訂票 所有功能均已實現,我們都知道12306是一個偉大的項目,但是在APP方面做得還是不怎麼樣,不過這個項目只是一個參考吧。 源碼下載:http://code.662p.com/view/12876.html 詳細說明:http://android.662
  • 這是一個不錯的Android美女的秘密項目。 源碼下載:http://code.662p.com/list/11_1.html<ignore_js_op> 詳細說明:http://android.662p.com/thread-6463-1-1.html
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...