私は写真ライブラリから画像を選択して表示できますが、私の目標は、その選択した画像またはファイルパスをコアデータに保存して、その保存されたレコードが選択されたときにその画像も表示できるようにすることです。
CoreDataが動作しており、CoreDataからのテキストを表示できます。これは、イメージだけが私を支えています。
@IBAction func addPic(sender: AnyObject) {
pickerController.delegate = self
pickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
// 2
self.presentViewController(pickerController, animated: true, completion: nil)
// Displays image
func imagePickerController(picker: UIImagePickerController!,didFinishPickingMediaWithInfo info: NSDictionary!){
image.image = info[UIImagePickerControllerOriginalImage] as? UIImage
self.dismissViewControllerAnimated(true, completion: nil)
スキップしてImageを処理し、UIImage
をNSData
(Core Dataが使用するもの)に変換する方法を見つけます。
または github からダウンロードします
コアデータ設定:
2つのエンティティを設定します:フル解像度とサムネイル。フル解像度は、元の画像を保存することです。アプリ内で使用する小さいバージョンを保存するサムネイル。たとえば、UICollectionView
概要で小さいバージョンを使用できます。
画像は_Binary Data
_に_Core Data
_として保存されます。 Foundation
の対応する型はNSData
です。 UIImage(data: newImageData)
でUIImage
に戻す
バイナリデータフィールドのAllows External Storageボックスをオンにします。これにより、イメージがファイルシステムに自動的に保存され、Core Dataで参照されます。
2つのエンティティを接続し、2つの間に1対1の関係を作成します。
Editorに移動して、CreateNSManagedObjectSubclassを選択します。これにより、管理対象オブジェクトのサブクラスを表すクラスを持つファイルが生成されます。これらはプロジェクトファイル構造に表示されます。
ViewControllerの基本設定:
以下をインポートします。
_import UIKit
import CoreData
_
UIButtons
とUIImageView
をセットアップします_class ViewController: UIViewController {
// imageview to display loaded image
@IBOutlet weak var imageView: UIImageView!
// image picker for capture / load
let imagePicker = UIImagePickerController()
// dispatch queues
let convertQueue = dispatch_queue_create("convertQueue", DISPATCH_QUEUE_CONCURRENT)
let saveQueue = dispatch_queue_create("saveQueue", DISPATCH_QUEUE_CONCURRENT)
// moc
var managedContext : NSManagedObjectContext?
override func viewDidLoad() {
super.viewDidLoad()
imagePickerSetup() // image picker delegate and settings
coreDataSetup() // set value of moc on the right thread
}
// this function displays the imagePicker
@IBAction func capture(sender: AnyObject) { // button action
presentViewController(imagePicker, animated: true, completion: nil)
}
@IBAction func load(sender: AnyObject) { // button action
loadImages { (images) -> Void in
if let thumbnailData = images?.last?.thumbnail?.imageData {
let image = UIImage(data: thumbnailData)
self.imageView.image = image
}
}
}
}
_
この関数は、正しいスレッドでmanagedContext
に値を設定します。 CoreDataは1つのNSManagedObjectContext
内のすべての操作を同じスレッドで行う必要があるためです。
_extension ViewController {
func coreDataSetup() {
dispatch_sync(saveQueue) {
self.managedContext = AppDelegate().managedObjectContext
}
}
}
_
UIViewController
とUIImagePickerControllerDelegate
に適合するようにUINavigationControllerDelegate
を拡張します。これらはUIImagePickerController
に必要です。
セットアップ関数を作成し、デリゲート関数imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?)
も作成します
_extension ViewController : UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerSetup() {
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.Camera
}
// When an image is "picked" it will return through this function
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion: nil)
prepareImageForSaving(image)
}
}
_
すぐにUIImagePickerController
を閉じると、アプリがフリーズしたように見えます。
画像の処理:
imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?)
内でこの関数を呼び出します。
最初に_timeIntervalSince1970
_で現在の日付を取得します。これは、NSTimerInterval
を秒単位で返します。これはDouble
にうまく変換されます。画像の一意のIDとして、また画像を並べ替える方法として機能します。
これで、別のキューに移動してメインキューを解放するのによいタイミングです。最初にdispatch_async(convertQueue)
を使用して、別のスレッドで重い負荷をかけました。
次に、UIImage
をNSData
に変換する必要があります。これはUIImageJPEGRepresentation(image, 1)
で行います。 _1
_は品質を表し、_1
_が最高で、_0
_が最低です。オプションを返すので、オプションのバインディングを使用しました。
画像を目的のサムネイルサイズにスケーリングし、NSData
に変換します。
コード:
_extension ViewController {
func prepareImageForSaving(image:UIImage) {
// use date as unique id
let date : Double = NSDate().timeIntervalSince1970
// dispatch with gcd.
dispatch_async(convertQueue) {
// create NSData from UIImage
guard let imageData = UIImageJPEGRepresentation(image, 1) else {
// handle failed conversion
print("jpg error")
return
}
// scale image, I chose the size of the VC because it is easy
let thumbnail = image.scale(toSize: self.view.frame.size)
guard let thumbnailData = UIImageJPEGRepresentation(thumbnail, 0.7) else {
// handle failed conversion
print("jpg error")
return
}
// send to save function
self.saveImage(imageData, thumbnailData: thumbnailData, date: date)
}
}
}
_
この関数は実際の保存を行います。
dispatch_barrier_sync(saveQueue)
を使用してCoreDataスレッドに移動しますdo try catch
_を使用して保存を試みますdispatch_barrier_sync(saveQueue)
を使用することにより、新しいイメージを安全に保存でき、新しい保存またはロードはこれが完了するまで待機することを確信しています。
コード:
_extension ViewController {
func saveImage(imageData:NSData, thumbnailData:NSData, date: Double) {
dispatch_barrier_sync(saveQueue) {
// create new objects in moc
guard let moc = self.managedContext else {
return
}
guard let fullRes = NSEntityDescription.insertNewObjectForEntityForName("FullRes", inManagedObjectContext: moc) as? FullRes, let thumbnail = NSEntityDescription.insertNewObjectForEntityForName("Thumbnail", inManagedObjectContext: moc) as? Thumbnail else {
// handle failed new object in moc
print("moc error")
return
}
//set image data of fullres
fullRes.imageData = imageData
//set image data of thumbnail
thumbnail.imageData = thumbnailData
thumbnail.id = date as NSNumber
thumbnail.fullRes = fullRes
// save the new objects
do {
try moc.save()
} catch {
fatalError("Failure to save context: \(error)")
}
// clear the moc
moc.refreshAllObjects()
}
}
}
_
画像を読み込むには:
_extension ViewController {
func loadImages(fetched:(images:[FullRes]?) -> Void) {
dispatch_async(saveQueue) {
guard let moc = self.managedContext else {
return
}
let fetchRequest = NSFetchRequest(entityName: "FullRes")
do {
let results = try moc.executeFetchRequest(fetchRequest)
let imageData = results as? [FullRes]
dispatch_async(dispatch_get_main_queue()) {
fetched(images: imageData)
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
return
}
}
}
}
_
画像のスケーリングに使用される関数:
_extension CGSize {
func resizeFill(toSize: CGSize) -> CGSize {
let scale : CGFloat = (self.height / self.width) < (toSize.height / toSize.width) ? (self.height / toSize.height) : (self.width / toSize.width)
return CGSize(width: (self.width / scale), height: (self.height / scale))
}
}
extension UIImage {
func scale(toSize newSize:CGSize) -> UIImage {
// make sure the new size has the correct aspect ratio
let aspectFill = self.size.resizeFill(newSize)
UIGraphicsBeginImageContextWithOptions(aspectFill, false, 0.0);
self.drawInRect(CGRectMake(0, 0, aspectFill.width, aspectFill.height))
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
_
Core Dataは、画像のような大きなバイナリファイルを保存するためのものではありません。代わりに、ファイルシステムのドキュメントディレクトリを使用します。
これを実現するサンプルコードを次に示します。
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask, true).first as! String
// self.fileName is whatever the filename that you need to append to base directory here.
let path = documentsDirectory.stringByAppendingPathComponent(self.fileName)
let success = data.writeToFile(path, atomically: true)
if !success { // handle error }