CoreData
提供了一種儲存資料到persistent store的方式, 可以將APP中的物件與資料庫中的表格相對映.
建立託管物件
被綁訂在CoreData的物件稱為託管物件. 它用來呈現APP的資料且存於托管物件內容中
託管物件是NSManagedObject的子類別, 代表一個Entity. 在Swift中, 我們在每一個所定義的property前面加上@NSManaged
, 讓他對應Restaurant Entity的屬性(attribute).
import CoreData
class Restaurant:NSManagedObject{
@NSManaged var name:String
@NSManaged var type:String
@NSManaged var location:String
@NSManaged var phoneNumber:String?
@NSManaged var image:NSData?
@NSManaged var isVisited:NSNumber?
@NSManaged var rating:String?
指定Entity給Restaurant類別
透過CoreData 儲存資料
- 從AppDelegate取得託管物件內容.
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext { ...
}
- 呼叫insertNewObjectForEntityForName來針對Restaurant Entity建立託管物件
restaurant = NSEntityDescription.insertNewObjectForEntityForName("Restaurant", inManagedObjectContext: managedObjectContext) as! Restaurant
- 呼叫save方法來告訴context要儲存物件
do {
try managedObjectContext.save()
} catch {
print(error)
return
}
透過CoreData讀取資料
最基本的方式就是用託管物件內容所提供的方法來達成:
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext {
let fetchResultController = NSFetchedRequest(entityName: "Restaurant")
do {
try managedObjectContext.executefetchRequest(fetchRequest) as! [Restaurant]
} catch {
print(error)
}
}
NSFetchedResultsController API
特別設計用來處理Core Data讀取後所回傳的結果, 並提供資料給表格視圖. 會監看託管物件內容中的物件變更, 然後將變更結果報告給其代理.
-
1.
import CoreData
class RestaurantTableViewController: UITableViewController, NSFetchedResultsControllerDelegate
- 宣告實體變數來讀取結果控制器
var fetchResultController:NSFetchedResultsController!
-
NSSortDescriptor
可以讓你指定物件間的排序.
當讀取結果建立完成後, 先初始化NSFetchedResultsController, 呼叫performFetch()
來執行讀取結果. 完成後我們存取fetchedObjects來存取Restaurant物件. fetchedObjects回傳的事AnyObject屬性所以這邊要強制轉型!
let fetchRequest = NSFetchRequest(entityName: "Restaurant")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext {
fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do {
try fetchResultController.performFetch()
restaurants = fetchResultController.fetchedObjects as! [Restaurant]
} catch {
print(error)
}
}
如果有任何內容變更, 以下方法會被呼叫.
// MARK: - NSFetchedResultsControllerDelegate
func controllerWillChangeContent(controller: NSFetchedResultsController) {
tableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Insert:
if let _newIndexPath = newIndexPath {
tableView.insertRowsAtIndexPaths([_newIndexPath], withRowAnimation: .Fade)
}
case .Delete:
if let _indexPath = indexPath {
tableView.deleteRowsAtIndexPaths([_indexPath], withRowAnimation: .Fade)
}
case .Update:
if let _indexPath = indexPath {
tableView.reloadRowsAtIndexPaths([_indexPath], withRowAnimation: .Fade)
}
default:
tableView.reloadData()
}
restaurants = controller.fetchedObjects as! [Restaurant]
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.endUpdates()
}
第一個方法是當讀取結果控制器準備開始處理內容變更時呼叫, 我們只是告訴表格視圖 “我們要更新表格了 快準備好”.
第二個方法是當託管物件內容有任何變更時會被呼叫.
第三個方始只是用來告訴表格視圖我們已經完成更新.
刪除CoreData資料
呼叫deleteObject的方法來刪除託管物件即可.
let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete",handler: { (action, indexPath) -> Void in
// Delete the row from the database
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext {
let restaurantToDelete = self.fetchResultController.objectAtIndexPath(indexPath) as! Restaurant
managedObjectContext.deleteObject(restaurantToDelete)
do {
try managedObjectContext.save()
} catch {
print(error)
}
}
})
UISearchController
簡化了建立搜尋欄位的方法以及搜尋結果的處理. 甚至提供開發者透過自訂的動畫物件, 能更彈性的改變搜尋欄的動畫.
第二行為建立instance, 傳遞nil的值表示搜尋結果會顯示於你正在搜尋的相同視圖中.另外也可以指定其他ViewController來顯示搜尋結果.
第四行為告訴searchResultsUpdater, 哪一個物件會負責更新搜尋結果
var searchController:UISearchController!
searchController = UISearchController(searchResultsController: nil)
tableView.tableHeaderView = searchController.searchBar
searchController.searchResultsUpdater = self
內容過濾
UISearchController在預設環境下並沒有提供任何過濾環境的功能, 必須自己負責實作內容的過濾規則.
在Swift中有一個內建方法叫filter
, 是用來過濾目前的陣列. 只需要在閉包中提供過濾規則即可.
在下面的程式中, 我們使用rangeOfString來檢查是否有相對應的名稱在搜尋文字內, 若是沒有找到就會回傳nil, 最後再檢查是否有找到搜尋文字, 並回傳相對應的Bool value.
func filterContentForSearchText(searchText: String) {
searchResults = restaurants.filter({ (restaurant:Restaurant) -> Bool in
let nameMatch = restaurant.name.rangeOfString(searchText, options: NSStringCompareOptions.CaseInsensitiveSearch)
return nameMatch != nil
})
}
更新搜尋結果
接著要實作如何在畫面上更新與顯示搜尋到的結果.
- 使用
UISearchResultsUpdating
Delegate. - updateSearchResultsForSearchController 為定義在協定中的一個方法. 當使用者選取搜尋欄時或者在裡面做了些變更時會被呼叫.
func updateSearchResultsForSearchController(searchController: UISearchController) {
if let searchText = searchController.searchBar.text {
filterContentForSearchText(searchText)
tableView.reloadData()
}
}
- 當完成到這一步的時候, tableView會同時顯示所有的餐廳資訊以及你的搜尋結果, 所以需要透過以下的方式來變免這種狀況發生.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.active {
return searchResults.count
} else {
return restaurants.count
}
}
自訂SearchBar外觀
// Customize the appearance of the search bar
searchController.searchBar.placeholder = "Search restaurants..."
searchController.searchBar.tintColor = UIColor(red: 100.0/255.0, green: 100.0/255.0, blue: 100.0/255.0, alpha: 1.0)
searchController.searchBar.barTintColor = UIColor(red: 240.0/255.0, green: 240.0/255.0, blue: 240.0/255.0, alpha: 0.6)
Optional Binding - if let & if var
如果無法確定變數是否為nil就貿然使用! 運算子,當該變數真的為nil時,就會發生Run-Time Error。所以當在執行Optional型別的運算的時候,最好是先判斷是否為nil值再進行運算。
var a:String = "Hello "
var b:String? = "bbb"
if b != nil {
var c = b!
print(a+c)
}
上面的寫法其實有些麻煩,所以Swift提供了一個語法 - 使用if let或if var。如下例所示,意思是如果b變數不是nil,則c變數等於b變數,並執行後面的statement。如果b變數是nil,則不執行該段statement。if let或if var就是所謂的Optional Binding語法
var a:String = "Hello "
var b:String? = "bbb"
if let c = b {
print(a+c)
}
沒有留言:
張貼留言