如何将图像从 AWS S3 加载到 UICollectionView 中?
how do I load images from AWS S3 into UICollectionView?
我的 AWS S3 后端有一个桶,里面装满了我想加载到我的 UICollectionViewCell 上的图像。我应该采用什么方法来做到这一点?我也在寻找最有效的方法。
我可能会注意到,目前在我的项目中,我拥有的框架是 Alamofire、swiftyJSON 和 Haneke(用于缓存目的),尽管我不知道如何正确使用它们来实现我的目标。我可能还要补充一点,我正在使用 Parse.com 作为我的 BaaS,所以如果有一种方法可以在其中集成解析,那也将受到欢迎。
所以有什么建议 在 Swift?
我建议的解决方案是使用 Parse.com 作为书籍的元数据源,并使用 s3 作为存储。我认为您不需要任何其他组件(而且我特别怀疑基本 iOS 东西的网络代码便利包装器)。
所以我会(并且确实这样做了,以证明它有效)设置一个像这样的解析模型......
这里是 swift 中的香草 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 "Book" 包装器并重新加载集合视图。这是 swift 本书 class ...
// 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... 方法中做了什么:如果书对象有一个缓存的 coverImage,我们将它放在图像视图中。否则,我们告诉这本书获取它的封面艺术,完成后,在当前索引路径处重新加载单元格(我们不再依赖于当前的单元格,因为在获取图像时时间会流逝)。
这是它的样子运行...
我遇到了这个问题并且有一个很好的工作解决方案,如何将图像从 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
}
}
这是一个单例 class 和 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
}
}
in Appdelegate
in func applicationDidReceiveMemoryWarning(_ application: UIApplication)
简单调用 imageCache.removeAllObjects()
我的 AWS S3 后端有一个桶,里面装满了我想加载到我的 UICollectionViewCell 上的图像。我应该采用什么方法来做到这一点?我也在寻找最有效的方法。
我可能会注意到,目前在我的项目中,我拥有的框架是 Alamofire、swiftyJSON 和 Haneke(用于缓存目的),尽管我不知道如何正确使用它们来实现我的目标。我可能还要补充一点,我正在使用 Parse.com 作为我的 BaaS,所以如果有一种方法可以在其中集成解析,那也将受到欢迎。
所以有什么建议 在 Swift?
我建议的解决方案是使用 Parse.com 作为书籍的元数据源,并使用 s3 作为存储。我认为您不需要任何其他组件(而且我特别怀疑基本 iOS 东西的网络代码便利包装器)。
所以我会(并且确实这样做了,以证明它有效)设置一个像这样的解析模型......
这里是 swift 中的香草 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 "Book" 包装器并重新加载集合视图。这是 swift 本书 class ...
// 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... 方法中做了什么:如果书对象有一个缓存的 coverImage,我们将它放在图像视图中。否则,我们告诉这本书获取它的封面艺术,完成后,在当前索引路径处重新加载单元格(我们不再依赖于当前的单元格,因为在获取图像时时间会流逝)。
这是它的样子运行...
我遇到了这个问题并且有一个很好的工作解决方案,如何将图像从 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
}
}
这是一个单例 class 和 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
}
}
in Appdelegate
in func applicationDidReceiveMemoryWarning(_ application: UIApplication)
简单调用 imageCache.removeAllObjects()