异步加载图像不断导致图像闪烁?

Posted

技术标签:

【中文标题】异步加载图像不断导致图像闪烁?【英文标题】:Asynchronously Loading Image constantly causes image to flicker? 【发布时间】:2015-05-20 18:17:38 【问题描述】:

每个UITableViewCell 都有一个标题视图。在这个标题视图中,我通过 Facebook API 中的异步函数加载了个人的图片。但是,由于函数是异步的,我相信该函数被一遍又一遍地调用多次,导致图像不断闪烁。我想解决这个问题的方法是首先将viewDidLoad 中的图像加载到一个数组中,然后在UITableViewCell 的标题视图中显示数组内容。但是,由于该函数的异步性质,我无法实现这一点:我似乎无法抓取每张照片,然后继续执行我的程序。这是我的尝试:

    //Function to get a user's profile picture
    func getProfilePicture(completion: (result: Bool, image: UIImage?) -> Void)
        // Get user profile pic
        let url = NSURL(string: "https://graph.facebook.com/1234567890/picture?type=large")
        let urlRequest = NSURLRequest(URL: url!)
        //Asynchronous request to display image
        NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue())  (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in
            if error != nil
                println("Error: \(error)")
            
            // Display the image
            let image = UIImage(data: data)
            if(image != nil)
                completion(result: true, image: image)
            
        
    
    override func viewDidLoad() 
      self.getProfilePicture  (result, image) -> Void in
          if(result == true)
              println("Loading Photo")
              self.creatorImages.append(image!)
          
          else
              println("False")
          
      
    

    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? 
      //Show section header cell with image
      var cellIdentifier = "SectionHeaderCell"
      var headerView = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as! SectionHeaderCell
      headerView.headerImage.image = self.creatorImages[section]
      headerView.headerImage.clipsToBounds = true
      headerView.headerImage.layer.cornerRadius = headerView.headerImage.frame.size.width / 2
      return headerView
    

如上面的程序所示,我创建的名为self.creatorImages 的全局数组包含我从 Facebook API 获取的图像数组,它始终是空的,我需要“等待”所有图片来填充实际使用之前的数组。我不确定如何实现这一点,因为我确实在我的 getProfilePicture 函数中尝试了一个完成处理程序,但这似乎没有帮助,这是我学会处理异步函数的一种方法。还有其他想法吗?谢谢!

【问题讨论】:

您的viewForHeaderInSection 没有返回视图。 哦,对不起,我把那部分遗漏了,最后应该有一个返回声明。 好的。当人们异步更新他们的图像视图时,“闪烁”的图像视图很常见,但你在这里没有这样做(你只是异步更新数组)。我看不出标题视图图像会闪烁的原因。我可能会建议dequeueReusableHeaderFooterViewWithIdentifier 而不是dequeueReusableCellWithIdentifier(请参阅***.com/a/27843634/1271826),但我不会认为这会导致您描述的问题。我想知道您是否正在处理cellForRowAtIndexPath 中的图像。我在这里看不到任何会导致闪烁的东西。 @Rob 对不起,我忘了说上面的代码是我的代码的更新版本。我试图做的是首先将getProfilePicture 函数放入viewForHeaderInSection 函数中,结果,它不断调用getProfilePicture 函数,导致它闪烁。然后,我尝试了另一种方法,即预先在viewDidLoad 中获取照片,这就是我上面的代码中显示的内容。我现在在抓取viewDidLoad 中的图像时遇到问题,想知道人们是否可以提供帮助。对于误导性标题,我深表歉意。 好的,那么我显然无法评论闪烁。关于“预取”的想法,我不建议这样做(您真的想在应用程序检索图像时冻结应用程序,还是显示它可以做什么并在下载图像时弹出图像)。我宁愿看到你有viewForHeaderInSection 请求它需要的图像。最简单的方法是使用 SDWebImage 或 AFNetworking 提供的 UIImageView 异步图像检索类别​​。 【参考方案1】:

我有同样的问题,但我的是在 Objective-C 好吧,结构并没有那么不同,我所做的是添加条件: headerView.headerImage.image

这是我认为适合您的实施的改进解决方案.. 因为您将self.getProfilePicture 放在viewDidLoad 中,所以它只会被调用一次,section==0 只会包含一张图片,

如果self.creatorImages的索引超出范围/界限,下面的代码将请求添加图像

func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? 
      //Show section header cell with image
      var cellIdentifier = "SectionHeaderCell"
      var headerView = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as! SectionHeaderCell


      if (section < self.creatorImages.count) // validate self.creatorImages index to prevent 'Array index out of range' error
      
          if (headerView.headerImage.image == nil) // prevents the blinks
          
              headerView.headerImage.image = self.creatorImages[section];
          
      
      else // requests for additional image at section
      
          // this will be called more than expected because of tableView.reloadData()
          println("Loading Photo")

          self.getProfilePicture  (result, image) -> Void in
              if(result == true) 
                  //simply appending will do the work but i suggest something like:

                  if (self.creatorImages.count <= section)
                  
                      self.creatorImages.append(image!)

                      tableView.reloadData()

                      println("self.creatorImages.count \(self.creatorImages.count)")
                  
                 //that will prevent appending excessively to data source
              
              else
                  println("Error loading image")
              
          
      

      headerView.headerImage.clipsToBounds = true
      headerView.headerImage.layer.cornerRadius = headerView.headerImage.frame.size.width / 2
      return headerView
    

您的实现肯定与我的想法不同,但编辑历史记录中的代码并没有白费,对吧?.. 哈哈哈.. ;) 希望我对你有所帮助.. 干杯!

【讨论】:

我仍然收到错误消息。我在viewForHeaderInSection 函数中添加了该条件,但我仍然收到错误消息fatal error: Array index out of range。我似乎能够加载第一张图片,但在第一张图片之后我无法加载任何内容。不过谢谢。还有其他想法吗? 在尝试访问索引之前检查数组计数 我的答案是删除闪烁/闪烁..关于那个致命错误..您需要检查数组的索引..因为您正在使用异步...我已经编辑我的答案看看.. 所以你是说我只需要检查一下索引是否已填充?这样就可以解决所有问题? 是的!没错..实际上他的代码(sendAsynchronousRequest)需要做很多工作才能完美运行,他必须有一个缓存 url 和 id 以便它不会知道错误的图像......因为他是很多验证不为此使用外部/现成的框架..我也在在这里学习东西,所以谢谢你指出来.. :)

以上是关于异步加载图像不断导致图像闪烁?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决由于加载图像而导致屏幕闪烁的问题?

使用 jQuery 异步加载图像

如何使用 UIImageViewExtension 与 Swift 异步加载图像并防止重复图像或错误图像加载到单元格

在PySide中异步加载图像

异步 NSImage 下载:第一个图像无限加载

使用 Autolayout 在 UITableView 中异步加载图像