如何将AWS S3中的图像加载到UICollectionView?

时间:2015-08-31 21:34:06

标签: ios xcode swift amazon-s3 uicollectionview

我的AWS S3后端有一个存储桶,里面装满了我想加载到我的UICollectionViewCell上的图像。我应该采用什么方法来实现呢?我也在寻找最有效的方法。

我可能会注意到,目前在我的项目中,我拥有的框架是Alamofire,swiftyJSON和Haneke(用于缓存目的),虽然我不知道如何正确使用它们来实现我的目标。我还可以补充说我正在使用Parse.com作为我的BaaS,所以如果有一种方法可以将解析集成在其中,那么也会受到欢迎。

所有建议在Swift中

2 个答案:

答案 0 :(得分:2)

我建议的解决方案是使用Parse.com作为图书元数据的来源,使用s3作为存储。我认为您不需要任何其他组件(我特别怀疑基本iOS内容的网络代码便利包装)。

所以我会(并且确实证明它有效)设置这样的解析模型...... Book as described on parse.com

这里有一个swift中的vanilla ViewController,它有一个集合视图和一系列自定义swift" Book"对象...

// ViewController.swift
import UIKit
import Parse

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

    @IBOutlet weak private var collectionView : UICollectionView!
    var books : Array<Book>!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.books = []
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        self.loadBooks()
    }

    func loadBooks() {
        var query = PFQuery(className: "Book")
        query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) -> Void in
            if error == nil {
                let bookObjects = objects as! [PFObject]
                for (index, object) in enumerate(bookObjects) {
                    self.books.append(Book(pfBook: object))
                }
            }
            self.collectionView.reloadData()
        }
    }

基本的东西。当视图出现时,查询解析书籍对象,当它们被返回时,创建swift&#34; Book&#34;它们周围的包装器并重新加载集合视图。这是快速的图书班......

// Book.swift
import Parse

class Book: NSObject {

    var pfBook : PFObject
    var coverImage : UIImage!

    init(pfBook: PFObject) {
        self.pfBook = pfBook
    }

    func fetchCoverImage(completion: (image: UIImage?, error: NSError?) -> Void) {
        let urlString = self.pfBook["coverUrl"] as! String
        let url = NSURL(string: urlString)
        let request = NSURLRequest(URL: url!)
        let queue = dispatch_get_main_queue()

        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response: NSURLResponse?, data: NSData?, error: NSError?) in
            if error == nil {
                self.coverImage = UIImage(data: data!)
                completion(image: self.coverImage, error: nil)
            } else {
                completion(image: nil, error: error)
            }
        }
    }
}

有趣的方法从保存在PFObject中的url中获取封面图像。 (查看parse.com数据浏览器中的url列。我在这里使用了一个快捷方式,并在网络上使用了一个很好的虚拟图像生成器。你需要实现一个具有相同签名的方法,但让它从s3获取图像)。

唯一剩下的就是集合视图数据源。再回到ViewController.swift ...

// ViewController.swift
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return self.books.count
}

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell",
        forIndexPath:indexPath) as! UICollectionViewCell
    if cell.backgroundView == nil { // brand new cell
        let v = UIImageView(frame:cell.bounds)
        v.tag = 32
        v.contentMode = .ScaleToFill
        cell.backgroundView = v
    }

    let book = self.books[indexPath.row]
    let coverImage = book.coverImage
    if coverImage == nil {
        book.fetchCoverImage({ (image, error) -> Void in
            collectionView.reloadItemsAtIndexPaths([indexPath])
        })
    } else {
        let imageView = cell.viewWithTag(32) as! UIImageView
        imageView.image = book.coverImage
    }
    return cell
}

注意我们在cellForRow ...方法中做了什么:如果book对象有一个缓存的coverImage,我们将它放在图像视图中。否则,我们会告诉本书获取它的封面艺术,并在完成后,在当前索引路径重新加载单元格(我们不再依赖于单元格当前是正常的,因为时间过去而图像正在取出的)。

这就是跑步的样子......

enter image description here

答案 1 :(得分:0)

我正面临这个问题,并且有一个很好的解决方案,如何在单元格或其他视图中将图像从AWS S3加载到UIImageView。为此,我还使用SDWebImage SDK SDWebImage

因此,我们为UIImageView创建一个扩展名并导入SDWebImage,然后将图像缓存添加到仅缓存图像链接中

import UIKit
import SDWebImage

let imageCache = NSCache<NSString, NSString>()

extension UIImageView {

    func loadAsyncWithCacheFromAWSS3(_ link: String?, placeholder: UIImage? = UIImage(named: "placeholder")) {
        guard let unwrappedlink = link else { return self.image = placeholder }
        // Use your own image appearing style
        self.sd_imageTransition = .fade
        if !loadFromCache(unwrappedlink as NSString, placeholder: placeholder) {
            let file = File(bucket: 'YOUR BUCKET', key: unwrappedlink, region: 'YOUR REGION')
            AWSManager.shared.getFileURL(file: file) { [weak self] (string, url, error) in
            guard let validURL = url else { self?.image = placeholder; return }
            imageCache.setObject(NSString(string: validURL.absoluteString), forKey: NSString(string: unwrappedlink))
            guard unwrappedlink == link else {
                guard let key = link else { return }
                self?.loadFromCashe(key as NSString, placeholder: placeholder)
                return
            }
                self?.sd_setImage(with: validURL, placeholderImage: placeholder, options: [.scaleDownLargeImages])
            }
        }
    }
    // quick load from existing cashed link
    @discardableResult
    private func loadFromCache(_ key: NSString, placeholder: UIImage?) -> Bool {
        guard let string = imageCache.object(forKey: key) else { return false }
        let link = URL(string: String(string))
        self.sd_setImage(with: link, placeholderImage: placeholder, options: [.scaleDownLargeImages])
    
        return true
    }

}

这是具有getFileURL方法的单例类

func getFileURL(file: File, completionHandler: @escaping (String?, URL?, Error?) -> Void) {
    let getPreSignedURLRequest = AWSS3GetPreSignedURLRequest()
    getPreSignedURLRequest.bucket = file.bucket
    getPreSignedURLRequest.key = file.key
    getPreSignedURLRequest.httpMethod = .GET
    getPreSignedURLRequest.expires = Date().adjust(hour: 24, minute: 0, second: 0)
    getPreSignedURLRequest.minimumCredentialsExpirationInterval = 10
    
    AWSS3PreSignedURLBuilder.s3PreSignedURLBuilder(forKey: 'YOUR BUILDER URL').getPreSignedURL(getPreSignedURLRequest).continueWith { (task: AWSTask<NSURL>) -> Any? in
        if let error = task.error as NSError? {
            completionHandler(nil, nil, error)
            return nil
        }
        let presignedURL = task.result
        completionHandler(file.key, presignedURL as URL?, nil)
        return nil
    }
}

Appdelegate的{​​{1}}简单调用func applicationDidReceiveMemoryWarning(_ application: UIApplication)