Created
June 27, 2016 04:34
-
-
Save MadeByDouglas/bbeab4caccb79c768bc2bfbaaa33f6b7 to your computer and use it in GitHub Desktop.
Simple Core Data Photo Implementation the Apple way
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MasterViewController.swift | |
// coreDataTest | |
// | |
// Created by Douglas Hewitt on 6/26/16. | |
// Copyright © 2016 madebydouglas. All rights reserved. | |
// | |
import UIKit | |
import CoreData | |
class MasterViewController: UITableViewController, NSFetchedResultsControllerDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate { | |
var detailViewController: DetailViewController? = nil | |
var managedObjectContext: NSManagedObjectContext? = nil | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
// Do any additional setup after loading the view, typically from a nib. | |
self.navigationItem.leftBarButtonItem = self.editButtonItem() | |
let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: #selector(openCamera(_:))) | |
self.navigationItem.rightBarButtonItem = addButton | |
if let split = self.splitViewController { | |
let controllers = split.viewControllers | |
self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController | |
} | |
} | |
override func viewWillAppear(animated: Bool) { | |
self.clearsSelectionOnViewWillAppear = self.splitViewController!.collapsed | |
super.viewWillAppear(animated) | |
} | |
override func didReceiveMemoryWarning() { | |
super.didReceiveMemoryWarning() | |
// Dispose of any resources that can be recreated. | |
} | |
func openCamera(sender: AnyObject) { | |
let picker = UIImagePickerController() | |
picker.delegate = self | |
picker.sourceType = .PhotoLibrary | |
presentViewController(picker, animated: true, completion: nil) | |
} | |
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) { | |
insertNewObject(image) | |
self.dismissViewControllerAnimated(true, completion: nil) | |
} | |
func insertNewObject(sender: AnyObject) { | |
let context = self.fetchedResultsController.managedObjectContext | |
let entity = self.fetchedResultsController.fetchRequest.entity! | |
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context) | |
// If appropriate, configure the new managed object. | |
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template. | |
newManagedObject.setValue(NSDate(), forKey: "timeStamp") | |
if let image = sender as? UIImage { | |
newManagedObject.setValue(image, forKey: "photo") | |
} | |
// Save the context. | |
do { | |
try context.save() | |
} catch { | |
// Replace this implementation with code to handle the error appropriately. | |
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. | |
//print("Unresolved error \(error), \(error.userInfo)") | |
abort() | |
} | |
} | |
// MARK: - Segues | |
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { | |
if segue.identifier == "showDetail" { | |
if let indexPath = self.tableView.indexPathForSelectedRow { | |
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) | |
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController | |
controller.detailItem = object | |
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem() | |
controller.navigationItem.leftItemsSupplementBackButton = true | |
} | |
} | |
} | |
// MARK: - Table View | |
override func numberOfSectionsInTableView(tableView: UITableView) -> Int { | |
return self.fetchedResultsController.sections?.count ?? 0 | |
} | |
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | |
let sectionInfo = self.fetchedResultsController.sections![section] | |
return sectionInfo.numberOfObjects | |
} | |
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { | |
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) | |
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject | |
self.configureCell(cell, withObject: object) | |
return cell | |
} | |
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { | |
// Return false if you do not want the specified item to be editable. | |
return true | |
} | |
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { | |
if editingStyle == .Delete { | |
let context = self.fetchedResultsController.managedObjectContext | |
context.deleteObject(self.fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject) | |
do { | |
try context.save() | |
} catch { | |
// Replace this implementation with code to handle the error appropriately. | |
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. | |
//print("Unresolved error \(error), \(error.userInfo)") | |
abort() | |
} | |
} | |
} | |
func configureCell(cell: UITableViewCell, withObject object: NSManagedObject) { | |
cell.textLabel!.text = object.valueForKey("timeStamp")!.description | |
cell.imageView!.image = object.valueForKey("photo") as? UIImage | |
} | |
// MARK: - Fetched results controller | |
var fetchedResultsController: NSFetchedResultsController { | |
if _fetchedResultsController != nil { | |
return _fetchedResultsController! | |
} | |
let fetchRequest = NSFetchRequest() | |
// Edit the entity name as appropriate. | |
let entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: self.managedObjectContext!) | |
fetchRequest.entity = entity | |
// Set the batch size to a suitable number. | |
fetchRequest.fetchBatchSize = 20 | |
// Edit the sort key as appropriate. | |
let sortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false) | |
fetchRequest.sortDescriptors = [sortDescriptor] | |
// Edit the section name key path and cache name if appropriate. | |
// nil for section name key path means "no sections". | |
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext!, sectionNameKeyPath: nil, cacheName: "Master") | |
aFetchedResultsController.delegate = self | |
_fetchedResultsController = aFetchedResultsController | |
do { | |
try _fetchedResultsController!.performFetch() | |
} catch { | |
// Replace this implementation with code to handle the error appropriately. | |
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. | |
//print("Unresolved error \(error), \(error.userInfo)") | |
abort() | |
} | |
return _fetchedResultsController! | |
} | |
var _fetchedResultsController: NSFetchedResultsController? = nil | |
func controllerWillChangeContent(controller: NSFetchedResultsController) { | |
self.tableView.beginUpdates() | |
} | |
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { | |
switch type { | |
case .Insert: | |
self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) | |
case .Delete: | |
self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade) | |
default: | |
return | |
} | |
} | |
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { | |
switch type { | |
case .Insert: | |
tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade) | |
case .Delete: | |
tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade) | |
case .Update: | |
self.configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, withObject: anObject as! NSManagedObject) | |
case .Move: | |
tableView.moveRowAtIndexPath(indexPath!, toIndexPath: newIndexPath!) | |
} | |
} | |
func controllerDidChangeContent(controller: NSFetchedResultsController) { | |
self.tableView.endUpdates() | |
} | |
/* | |
// Implementing the above methods to update the table view in response to individual changes may have performance implications if a large number of changes are made simultaneously. If this proves to be an issue, you can instead just implement controllerDidChangeContent: which notifies the delegate that all section and object changes have been processed. | |
func controllerDidChangeContent(controller: NSFetchedResultsController) { | |
// In the simplest, most efficient, case, reload the table view. | |
self.tableView.reloadData() | |
} | |
*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Basic core data implementation as per apple boiler plate code. What makes this controller fun is it connects to your photo library and saves the photo to core data for you.