我是 swift 和 IOS 的新手,我可能遇到异步问题(不确定),我有一个 firebase 数据库,它为我提供了在 Amazon S3 上上传内容的 key 和 URLS。我正在尝试从 S3 下载图像,将它们存储在一个数组中,然后填充表格 View 。
代码似乎启动了下载,但它没有进展(进度断点甚至没有命中)
有人可以教我如何干净地填充我的 tableview 吗?我的图像数组总是空的,我不知道它是来 self 的 s3 函数(但不返回任何错误)还是来自调度事件。
//
// MainWitness.swift
// trainning2
//
// Created by Yann MASSARD on 2/2/16.
// Copyright © 2016 Witness. All rights reserved.
//
import UIKit
import Firebase
import FirebaseUI
class MainWitness: UIViewController, UITableViewDataSource, UITableViewDelegate {
let rootref = Firebase(url: "XXXXXXX/witness/")
let refpost = Firebase(url: "XXXXXXX/witness/POSTS")
var posts = [getitems]()
var Images = [UIImage]()
@IBOutlet weak var tableView: UITableView!
// S3 download function / called on view did appear, gets the key from firebase
func downloadImage(key:String){
var completionHandler: AWSS3TransferUtilityDownloadCompletionHandlerBlock?
let S3BucketName: String = "witnesstest/" + rootref.authData.uid
let S3DownloadKeyName: String = key
let expression = AWSS3TransferUtilityDownloadExpression()
expression.downloadProgress = {(task: AWSS3TransferUtilityTask, bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) in
dispatch_async(dispatch_get_main_queue(), {
let progress = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
print("Progress is: \(progress)")
})
}
completionHandler = { (task, location, data, error) -> Void in
dispatch_async(dispatch_get_main_queue(), {
if ((error) != nil){
print("Failed with error")
print("Error: \(error!)")
}
else{
//Set your image
self.Images.append(UIImage(data: data!)!)
}
})
}
let transferUtility = AWSS3TransferUtility.defaultS3TransferUtility()
transferUtility.downloadToURL(nil, bucket: S3BucketName, key: S3DownloadKeyName, expression: expression, completionHander: completionHandler).continueWithBlock { (task) -> AnyObject! in
if let error = task.error {
print("Error: \(error.localizedDescription)")
}
if let exception = task.exception {
print("Exception: \(exception.description)")
}
if let _ = task.result {
print("Download Starting!") // shows in console for each firebase post loop
}
return nil;
}
}
override func viewDidAppear(animated: Bool) {
// [1] Call the queryOrderedByChild function to return a reference that queries by the "author" property
self.refpost.queryOrderedByChild("author").observeEventType(.Value, withBlock: { snapshot in
var newItems = [getitems]()
for item in snapshot.children {
let postitem = getitems(snapshot: item as! FDataSnapshot)
newItems.append(postitem)
// READ IMAGE FROM S3
self.downloadImage(postitem.imagekey)
}
self.posts = newItems
self.tableView.reloadData()
})
}
override func viewDidLoad() {
//ref.unauth()
super.viewDidLoad()
//tableview datasource and delegate
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
// is any user log ?
if ((rootref.authData) != nil) {
print("user logged")
} else {
print("user not logged")
dispatch_async(dispatch_get_main_queue()){
self.performSegueWithIdentifier("log", sender: self)
}
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: MainWitnessTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("RIcell") as! MainWitnessTableViewCell
// populate the cell
let postitem = posts[indexPath.row]
cell.postOwner.text = postitem.author
cell.postContent.text = postitem.content
cell.postDate.text = postitem.createon
// cell.cellImage.image = Images[indexPath.row] // returns array index out of range
return cell
}
func tableView(tableView: UITableView, didselectRowAtIndexPath indexPath: NSIndexPath) {
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
}
最佳答案
这个问题已经有几个月了,但为了以防万一你仍然遇到问题,我会给我两分钱。
在我看来,问题在于确定何时刷新您的表格单元格(假设您从 Firebase 获取 key 和从 S3 获取数据的所有调用都工作正常;如果不是,请考虑编写一些单元测试或使用 playground以确保您可以在 View Controller 的上下文之外正确获取数据,这样会更快)。
获得 Firebase 引用后,您将调用 self.downloadImage(postitem.imagekey)
,然后调用 self.tableView.reloadData()
。由于 downloadImage
是异步的,因此当您重新加载表格时您可能还没有任何图像数据,所以这可能就是您似乎什么都没有的原因。在从 S3 填充 images
数组后,必须调用表重新加载。
下面是我将如何去做:
- 您的
posts
和images
数组都绑定(bind)在一起;为什么不创建一个名为Posts
的结构,其中包含参数postInfo: getitems
和image: UIImage?
,您可能有也可能没有图像那个帖子?那么 tableView 的数据模型将是var data: [Posts]
。在从 S3 加载之前,每个Post
的图像参数都是 nil。 viewDidLoad
:开始向 Firebase 查询帖子及其图像键。- 在 Firebase 调用的回调中,如果您返回 X 个帖子,则可以说您的 tableView 的数据模型包含 X 个帖子。将每个帖子附加到您的数组
data: [Post]
并将.image
参数设置为 nil,因为您还没有任何图像。附加完所有帖子后,您可以重新加载 tableView。 - 在
tableView:cellForRowAtIndexPath
中,如果data[indexPath.row].image
处的图片是nil
,你可以放入一个临时的占位符 UIImage 显示,直到从 S3 加载真实图像。如果data[indexPath.row].image
不为 nil,则设置实际图像。 - 开始下载 S3 图像(您仍然可以在 Firebase 调用的回调中为每个帖子执行此操作)并在每次返回图像时更新您的数组
data: [Post]
。对于您返回的每张图像,仅重新加载该表格单元格。这将再次调用tableView:cellForRowAtIndexPath
。由于.image
参数将不再为 nil,您将看到实际图像替换占位符图像。
以下是如何有效地更新特定行而不是重新加载整个表的方法:
tableView.beginUpdates()
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)
tableView.endUpdates()
如果您在数组中存储许多大型 UIImages,您可能会遇到内存问题,并且可能需要考虑保留图像缓存并在将图像滚动到屏幕上之前加载图像而不是加载所有图像立即显示图像。
关于ios - SWIFT aws S3 异步下载?下载图像并填充表格 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35447871/