UITableView:延迟重新加载,直到所有数据都在后台下载

时间:2015-08-16 02:03:56

标签: swift uitableview

我一直在努力让我的tableview正确加载.count。我必须找到一种方法来告诉tableview只在我的图像和后期数组完全填充后才加载。

否则我会继续得到

fatal error: Array index out of range

cell.cellImage?.image = imagesArray[indexPath.row]

里面

cellForRowAtIndexPath

输出:

    NUMBER OF POSTS->0
    NUMBER OF IMAGES->0
    NUMBER OF POSTS->0
    NUMBER OF IMAGES->0
    NUMBER OF POSTS->0
    NUMBER OF IMAGES->0
    POSTSARRAY COUNT->1
    POSTSARRAY COUNT->2
    POSTSARRAY COUNT->3
    POSTSARRAY COUNT->4

    NUMBER OF POSTS->4
    NUMBER OF IMAGES->0

    IMAGESARRAY COUNT->1
    IMAGESARRAY COUNT->2
    IMAGESARRAY COUNT->3
    IMAGESARRAY COUNT->4

    NUMBER OF POSTS->4
    NUMBER OF IMAGES->4

代码

    override func viewDidLoad() {
        super.viewDidLoad()

//        myTableView.estimatedRowHeight = 312.0
//        myTableView.rowHeight = UITableViewAutomaticDimension

        var query = PFQuery(className: "Post")
        query.whereKey("hobbieTag", equalTo:"\(selectedHobbie)")
        query.orderByAscending("description")
        query.findObjectsInBackgroundWithBlock
            {
                (objects: [AnyObject]?, error: NSError?) -> Void in

                if error == nil
                {
                    //                println("HOBBIES.COUNT->\(hobbies?.count)")
                    for post in objects!
                    {
                        //GET POST TITLE
                        self.posts.append(post["postText"] as! String)
                        println("POSTSARRAY COUNT->\(self.posts.count)")

                        //TEST IMAGE
                        //var appendImage = UIImage(named: "logoPDF")
                        //self.imagesArray.append(appendImage!)

                        //GET IMAGE FILE
                        let postImageFile = post["postImage"] as? PFFile
                        postImageFile?.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in

                            var image = UIImage(data: imageData!)
                            self.imagesArray.append(image!)
                            println("IMAGESARRAY COUNT->\(self.imagesArray.count)")

                            }, progressBlock: { (progress: Int32) -> Void in
//                                println("PROGRESS->\(progress)")
                        })
                    }
                    self.myTableView.reloadData()
                }
                else
                {
                    println(error?.localizedDescription)
                    println(error?.code)
                }
//                self.myTableView.reloadData()
        }//END query
    }

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


    override func viewDidAppear(animated: Bool) {
        myTableView.reloadData()
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        println("NUMBER OF POSTS->\(posts.count)")
        println("NUMBER OF IMAGES->\(imagesArray.count)")
        return posts.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cellIdentifier = "Cell"
        let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! HobbieFeedTableViewCell

        cell.cellTitle.text = posts[indexPath.row]
        cell.cellSubtitle.text = posts[indexPath.row]


        //        cell.cellImage.image = UIImage(named: "logoPDF")
        //cell.cellImage?.image = imagesArray[indexPath.row]

        return cell
    }

更新了代码

        var query = PFQuery(className: "HobbieFeed")
        query.whereKey("hobbieTag", equalTo:"\(selectedHobbie)")
        query.orderByAscending("description")
        query.findObjectsInBackgroundWithBlock
            {
                (objects: [AnyObject]?, error: NSError?) -> Void in

                if error == nil
                {
                    let semaphore = dispatch_semaphore_create(0)

                    //                println("HOBBIES.COUNT->\(hobbies?.count)")
                    for post in objects!
                    {
                        //GET POST TITLE
                        self.posts.append(post["postText"] as! String)
                        println("POSTSARRAY COUNT->\(self.posts.count)")

                        //TEST IMAGE
                        //var appendImage = UIImage(named: "logoPDF")
                        //self.imagesArray.append(appendImage!)

                        //GET IMAGE FILE
                        let postImageFile = post["postImage"] as? PFFile
                        postImageFile?.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in

                            var image = UIImage(data: imageData!)
                            self.imagesArray.append(image!)
                            println("IMAGESARRAY COUNT->\(self.imagesArray.count)")

                            dispatch_semaphore_signal(semaphore)

                            }, progressBlock: { (progress: Int32) -> Void in
                                println("PROGRESS->\(progress)")
                        })


                    }

                    // Wait for all image loading tasks to complete
                    for post in objects! {
                        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
                        println("SHAPHORE POSTS COUNT->\(self.posts.count)")
                        println("SEMAPHORE IMAGES COUNT->\(self.imagesArray.count)")
                    }

                    self.myTableView.reloadData()
                }

        }//END query

3 个答案:

答案 0 :(得分:4)

您在循环中在背景中加载图像,并在循环后立即调用reloadData。但是,此时后台任务尚未完成。

for post in objects! {
    ...
    // This starts a background operation
    postImageFile?.getDataInBackgroundWithBlock(...)
    ...
}
// The background tasks are not necessarily completed at this point
self.myTableView.reloadData()

要等到所有后台任务完成,您才能使用信号量。这是一个基本的例子。

// Create a new semaphore with a value of 0
let semaphore = dispatch_semaphore_create(0)

// Kick off a bunch of background tasks
for i in 0...10 {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        sleep(1) // Do some work or sleep

        // Signal the semaphore when the task is done. This increments
        // the semaphore.
        dispatch_semaphore_signal(semaphore)
    }
}

// Wait until all background tasks are done
for i in 0...10 {
    // This waits until the semaphore has a positive value
    // and then decrements it
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}

// This is only executed after all background tasks are done
println("all tasks are done")

请注意,使用调度组可以简化此示例。但是,由于您使用完成处理程序调用函数而不是直接在队列上执行块,因此这不是您的选项。

将上述方法应用于您的代码将如下所示。

let semaphore = dispatch_semaphore_create(0) // Create a semaphore (value: 0)

for post in objects! {
    ...
    postImageFile?.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
        ... // Do your work

        // Increment the semaphore when the image loading is completed
        dispatch_semaphore_signal(semaphore)
    }, progressBlock: {
        ...
    })
    ...
}

// Wait for all image loading tasks to complete
for post in objects! {
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}

// This is only called after all images have loaded
self.myTableView.reloadData()

答案 1 :(得分:1)

嗨,据我所知,你做的正确,在后台异步加载数据。我认为它失败的地方是在你重新加载表的异步块中。在那里试试这个代码:

dispatch_async(dispatch_get_main_queue()){self.myTableView.reloadData()}

该块实际上是在不同的线程中运行,因此它与您的其他代码完全不同步。如果您正在使用测试,那么正确测试异步块也是一个问题。在这里,您可以使用waitForExpectationsWithTimeout(秒)来正确测试代码

答案 2 :(得分:0)

最后解决了这个问题。可能对其他用户有帮助,所以会在这里发布答案。

采用不同的方法,我相信更简单。

查询在视图上加载时发生,但在该阶段没有单独的图像或内容数组。单元格获取整个查询对象数组,然后检索每行所需的内容。如果有图像也更容易检测。

全球阵列:

var timelineData = NSMutableArray()

查看了加载查询:

 var query = PFQuery(className: "Feed")
    query.whereKey("Tag", equalTo:"\(selected)")
    query.orderByAscending("description")
    query.findObjectsInBackgroundWithBlock
        {
            (objects: [AnyObject]?, error: NSError?) -> Void in

            if error == nil
            {
                for object in objects!
                {
                    self.timelineData.addObject(object)
                }
                self.myTableView.reloadData()
            }
    }//END query

tableview cellforrow

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        //Cell identifiers
        let standardCellIdentifier = "StandardCell"
        let imageCellIdentifier = "ImageCell"

        //get object, text and votes
        let object = self.timelineData.objectAtIndex(indexPath.row) as! PFObject
        var myText = object.objectForKey("postText") as? String
        var hasImage = object.objectForKey("hasImage") as? Bool
        var myVotes = object.objectForKey("votes") as! Int
        let imageFromParse = object.objectForKey("postImage") as? PFFile

        //if image
        if hasImage == true
        {
            let imageCell = tableView.dequeueReusableCellWithIdentifier(imageCellIdentifier, forIndexPath: indexPath) as! FeedImageTVCell

            //set image for cell
            imageFromParse!.getDataInBackgroundWithBlock({ (imageData:NSData?, error:NSError?) -> Void in
                if error == nil {

                    if let myImageData = imageData {
                        let image = UIImage(data:myImageData)
                        imageCell.cellImage!.image = image
                    }
                }
                }, progressBlock: { (percent: Int32) -> Void in
            })
            imageCell.cellVotes.text = "Votes - \(myVotes)"
            imageCell.cellText.text = myText
            return imageCell
        }
        //if no image
        else
        {
            let standardCell = tableView.dequeueReusableCellWithIdentifier(standardCellIdentifier, forIndexPath: indexPath) as! FeedStandardTVCell
            standardCell.cellVotes.text = "Votes - \(myVotes)"
            standardCell.cellText.text = myText
            return standardCell

        }
    }