Swift 3 使用 CKAsset 将图像发送到 CloudKit

Posted

技术标签:

【中文标题】Swift 3 使用 CKAsset 将图像发送到 CloudKit【英文标题】:Swift 3 Send image to CloudKit with CKAsset 【发布时间】:2017-01-13 21:25:18 【问题描述】:

注意:这不是一个重复的问题,我已经查看了其他解决方案,但没有一个在 Swift 3 中有效,但它们在 Swift 2 中有效。

我需要使用 UIImagePickerController 从照片库中获取图像,这可以正常工作,然后我需要做的就是以某种方式将其保存到 CloudKit 中的公共记录中,我对此非常开放.如果可能,请清楚我需要更改/添加什么以及在哪里。

为了确定,我已经提供了我的整个视图控制器文件。

import UIKit
import CloudKit

var email: String = ""



class ViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate 


    //MARK: Properties

    @IBOutlet weak var firstNameField: UITextField!
    @IBOutlet weak var lastNameField: UITextField!
    @IBOutlet weak var emailField: UITextField!
    @IBOutlet weak var passwordField: UITextField!
    @IBOutlet weak var confirmPasswordField: UITextField!
    @IBOutlet weak var notMatching: UILabel!
    @IBOutlet weak var emailLabel: UILabel!
    @IBOutlet weak var errorLabel: UILabel!

    @IBOutlet weak var photoImageView: UIImageView!



    override func viewDidLoad() 
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.firstNameField.autocorrectionType = .no
        self.lastNameField.autocorrectionType = .no
        self.emailField.autocorrectionType = .no
        self.passwordField.autocorrectionType = .no
        self.confirmPasswordField.autocorrectionType = .no
        self.notMatching.isHidden = true

        firstNameField.delegate = self
        lastNameField.delegate = self
        emailField.delegate = self
        passwordField.delegate = self
        confirmPasswordField.delegate = self


    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    

    //MARK: UIImagePickerControllerDelegate

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) 

        // Dismiss the picker if the user canceled.
        dismiss(animated: true, completion: nil)

    

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) 

        // The info dictionary may contain multiple representations of the image. You want to use the original.
        guard let selectedImage = info[UIImagePickerControllerOriginalImage] as? UIImage else 
            fatalError("Expected a dictionary containing an image, but was provided the following: \(info)")
        

        // Set photoImageView to display the selected image.
        photoImageView.image = selectedImage



        // Dismiss the picker.
        dismiss(animated: true, completion: nil)


    


    //MARK: Actions

    @IBAction func selectImageFromPhotoLibrary(_ sender: UITapGestureRecognizer) 

        //Hide keyboards
        firstNameField.resignFirstResponder()
        lastNameField.resignFirstResponder()
        emailField.resignFirstResponder()
        passwordField.resignFirstResponder()
        confirmPasswordField.resignFirstResponder()

        let imagePickerController = UIImagePickerController()

        imagePickerController.sourceType = .photoLibrary


        imagePickerController.delegate = self
        present(imagePickerController, animated: true, completion: nil)

    



    @IBAction func signUpPressed(_ sender: UIButton) 

        let container = CKContainer.default()
        let pubDB = container.publicCloudDatabase
        //let privDB = container.privateCloudDatabase

        //Check if users exist
        let query = CKQuery(recordType: "MyUsers", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
        pubDB.perform(query, inZoneWith: nil, completionHandler:  (records, error) in

            //error code 11 is no objects found
            if error == nil || error?._code == 11 

                var emailExists = false

                for record in records! 

                    if record.object(forKey: "email") as? String == self.emailField.text 
                        //other user with the same username exists - don't allow user to create account
                        emailExists = true

                    

                

                if emailExists == true 


                    self.emailLabel.text = "\(self.emailField.text!) is taken. Please choose another one."

                 else 

                    if self.firstNameField.text != nil && self.lastNameField.text != nil && self.passwordField.text == self.confirmPasswordField.text 

                        //user can sign up


                        let record = CKRecord(recordType: "MyUsers")
                        record.setObject(self.emailField.text! as CKRecordValue?, forKey: "email")
                        record.setObject(self.passwordField.text! as CKRecordValue?, forKey: "password")
                        record.setObject(self.firstNameField.text! as CKRecordValue?, forKey: "firstName")
                        record.setObject(self.lastNameField.text! as CKRecordValue?, forKey: "lastName")



                        print("all good")

                        pubDB.save(record, completionHandler:  (record, error) in

                            if error == nil 

                                OperationQueue.main.addOperation 

                                    UserDefaults.standard.set(self.emailField.text!, forKey: "Email")
                                    email = self.emailField.text!
                                    //self.performSegue(withIdentifier: "Games", sender: self)

                                

                             else 

                                print(error)

                            

                        )


                     else 



                    


                



             else 

                print(error)

            

        )

    



    // MARK: UITextFieldDelegate

    func textFieldShouldReturn(_ textField: UITextField) -> Bool 
        // Hide the keyboard.
        textField.resignFirstResponder()
        return true
    




谢谢!

【问题讨论】:

请看下面我的回答。您还应该知道如何在某个时候检索CKAsset。如果您要存储资产,您也需要获取它。 【参考方案1】:

这是将图像另存为CKAssetCloudKit 的简单方法。请确保从您设置记录时更改您的记录名称和资产的字段名称。

let documentsDirectoryPath:NSString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
var imageURL: URL!
let tempImageName = "Image2.jpg"


func saveImage(_ image: UIImage?) 

    // Create a CKRecord
    let newRecord:CKRecord = CKRecord(recordType: "<INSERT_RECORD_NAME")

    if let image = image 

        let imageData:Data = UIImageJPEGRepresentation(image, 1.0)!
        let path:String = self.documentsDirectoryPath.appendingPathComponent(self.tempImageName)
        try? UIImageJPEGRepresentation(image, 1.0)!.write(to: URL(fileURLWithPath: path), options: [.atomic])
        self.imageURL = URL(fileURLWithPath: path)
        try? imageData.write(to: self.imageURL, options: [.atomic])

        let File:CKAsset?  = CKAsset(fileURL: URL(fileURLWithPath: path))
        newRecord.setObject(File, forKey: "<INSERT_RECORD_ASSET_FIELD_NAME")
    

    if let database = self.publicDatabase 

        database.save(newRecord, completionHandler:  (record:CKRecord?, error:Error?) in

            // Check if there was an error
            if error != nil 
                NSLog((error?.localizedDescription)!)
            
            else if let record = record 

                // Do whatever you want with the record, but image record was saved, asset should be saved.

            


        )


    



如果您不能使用JPEG 格式,并且需要另存为.png,您可以将UIImageJPEGRepresentation 部分替换为:

let imageData:Data = UIImagePNGRepresentation(image)!
try? UIImagePNGRepresentation(image)!.write(to: URL(fileURLWithPath: path), options: [.atomic])

tempImageName 类似于let tempImageName = "Image2.png"

希望对你有帮助

【讨论】:

非常感谢,我有两个应用程序无法执行此操作,并且已经卡了将近 4 个月,这是第一个可行的解决方案! @RufusV - 不客气。如果您有任何问题,请随时问我。我有很多使用 CloudKit 的经验 嗨 Pierce,如果你有时间,我有一个关于 CloudKit 的问题。这是链接:***.com/questions/42370112/… 谢谢! :) @JackRobson - 我刚刚检查过了,看起来你解决了!干得好 感谢 Pierce 的检查!非常感谢:)【参考方案2】:

Swift 5 版本,但被抽象为一个函数,我可以在其中传递 UI Image 对象并返回要使用的 URL。在我将图像保存在 CoreData 中并想稍后上传到 CloudKit 的情况下很有用。

func getImageURL(for image: UIImage?) -> URL 
    let documentsDirectoryPath:NSString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
    let tempImageName = "tempImage.jpg"
    var imageURL: URL?

    if let image = image 

        let imageData:Data = image.jpegData(compressionQuality: 1.0)!
        let path:String = documentsDirectoryPath.appendingPathComponent(tempImageName)
        try? image.jpegData(compressionQuality: 1.0)!.write(to: URL(fileURLWithPath: path), options: [.atomic])
        imageURL = URL(fileURLWithPath: path)
        try? imageData.write(to: imageURL!, options: [.atomic])
    
    return imageURL!

然后是我如何使用它:

  let imageURL = getImageURL(for: UIImage(data: itemToPublish!.Photo!)!)
  let imageAsset = CKAsset(fileURL: imageURL)
            itemBeingUpdated["photo"] = imageAsset  

  CKContainer.default().publicCloudDatabase.save(challengeRecord)  ...

【讨论】:

以上是关于Swift 3 使用 CKAsset 将图像发送到 CloudKit的主要内容,如果未能解决你的问题,请参考以下文章

通过 segue 将 CKAsset 图像传递给 ViewController?

CKAsset 获取 PDF 并呈现在 UIWebView Swift3 中

将 CKAsset 异步加载到 UICollectionView

在 CloudKit 中将图像存储为 CKAsset,图像倒置

CKAsset 不会显示在 tableview 图像中

在 Swift 中使用参数将图像发送到服务器