web-dev-qa-db-ja.com

選択した画像をCoreDataに保存する

私は写真ライブラリから画像を選択して表示できますが、私の目標は、その選択した画像またはファイルパスをコアデータに保存して、その保存されたレコードが選択されたときにその画像も表示できるようにすることです。

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)
31
turtle02

スキップしてImageを処理し、UIImageNSData(Core Dataが使用するもの)に変換する方法を見つけます。

または github からダウンロードします

コアデータ設定:

2つのエンティティを設定します:フル解像度とサムネイル。フル解像度は、元の画像を保存することです。アプリ内で使用する小さいバージョンを保存するサムネイル。たとえば、UICollectionView概要で小さいバージョンを使用できます。

画像は_Binary Data_に_Core Data_として保存されます。 Foundationの対応する型はNSDataです。 UIImage(data: newImageData)UIImageに戻す

enter image description here


enter image description here


バイナリデータフィールドのAllows External Storageボックスをオンにします。これにより、イメージがファイルシステムに自動的に保存され、Core Dataで参照されます。

enter image description here

2つのエンティティを接続し、2つの間に1対1の関係を作成します。

enter image description here

Editorに移動して、CreateNSManagedObjectSubclassを選択します。これにより、管理対象オブジェクトのサブクラスを表すクラスを持つファイルが生成されます。これらはプロジェクトファイル構造に表示されます。

enter image description here


ViewControllerの基本設定:

以下をインポートします。

_import UIKit
import CoreData
_

  • Interface Builderで2つのUIButtonsUIImageViewをセットアップします
  • CoreData用とUIImage変換用の2つのディスパッチキューを作成します

_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
        }
    }
}
_

UIViewControllerUIImagePickerControllerDelegateに適合するように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)を使用して、別のスレッドで重い負荷をかけました。

  • 次に、UIImageNSDataに変換する必要があります。これは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スレッドに移動します
  • 最初に、新しいFullResおよび新しいThumbnailオブジェクトをManaged Object Contextに挿入します。
  • 値を設定する
  • FullResとサムネイルの関係を設定します
  • _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
    }

}
_
95
R Menke

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 }
0
Himanshu