ios - SWIFT aws S3 异步下载?下载图像并填充表格 View

标签 ios swift uitableview asynchronous amazon-s3

我是 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 数组后,必须调用表重新加载。

下面是我将如何去做:

  1. 您的postsimages 数组都绑定(bind)在一起;为什么不创建一个名为 Posts 的结构,其中包含参数 postInfo: getitemsimage: UIImage?,您可能有也可能没有图像那个帖子?那么 tableView 的数据模型将是 var data: [Posts]。在从 S3 加载之前,每个 Post 的图像参数都是 nil。
  2. viewDidLoad:开始向 Firebase 查询帖子及其图像键。
  3. 在 Firebase 调用的回调中,如果您返回 X 个帖子,则可以说您的 tableView 的数据模型包含 X 个帖子。将每个帖子附加到您的数组 data: [Post] 并将 .image 参数设置为 nil,因为您还没有任何图像。附加完所有帖子后,您可以重新加载 tableView。
  4. tableView:cellForRowAtIndexPath中,如果data[indexPath.row].image处的图片是nil,你可以放入一个临时的占位符 UIImage 显示,直到从 S3 加载真实图像。如果 data[indexPath.row].image 不为 nil,则设置实际图像。
  5. 开始下载 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/

相关文章:

ios - 不会将图像传递到目标 ViewController

ios - 在节点暂停的情况下开始场景

swift - 让 keydown NSEvent 在 Swift 中运行一次

ios - 在 Swift 3.0 中将 UIButton 分层到 UILabel 上是否合适?

ios - 如何在 iOS 5 Storyboard中初始化自定义原型(prototype)样式表格单元格?

iphone - 只读属性上的 Core Plot CPLineStyle 编译错误

ios - 使用 swift 从 iOS 应用程序调用特殊号码

ios - 从 ios 流式传输实时视频

ios - 启动屏幕图像未出现在 SwiftUI 应用程序中

ios - 滚动时 UITableViewCell 的奇怪行为,UIButtons 消失