【问题标题】:Array index out of range - Downloading images from Parse数组索引超出范围 - 从 Parse 下载图像
【发布时间】:2016-02-29 21:33:37
【问题描述】:

我正在尝试从解析中查询图像,当我打开我的应用程序时一切正常,但如果我尝试刷新,我会得到如下所示的崩溃...

不太清楚是什么原因造成的...解释一下我是如何设置的:

我有一个带有 1 个单元格的 tableView,在那个单元格中有三个 imageView 连接到一个集合 Outlet。然后我从解析中获取我的图像并将它们放在我的 imageViews 中,然后在 numberOfRowsInSection 中我将它除以 3,这样它就不会重复图像 3 次......!

下面是我的代码:

    var countries = [PFObject]()



    override func viewDidLoad() {
        super.viewDidLoad()

       loadPosts()


        // Do any additional setup after loading the view.
    }

 func loadPosts() {
        PFUser.query()
        // Build a parse query object
        let query = PFQuery(className:"Post")
        query.whereKey("user", equalTo: PFUser.currentUser()!)
        query.orderByDescending("createdAt")


        // Fetch data from the parse platform
        query.findObjectsInBackgroundWithBlock {
            (objects: [PFObject]?, error: NSError?) -> Void in

            // The find succeeded now rocess the found objects into the countries array
            if error == nil {

                self.countries.removeAll(keepCapacity: true)

                if let objects = objects {
                    self.countries = Array(objects.generate())
                }

                // reload our data into the collection view
               self.tableView.reloadData()

            } else {
                // Log details of the failure
                print("Error: \(error!) \(error!.userInfo)")
            }
        }


    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {

       return countries.count / 3

    }

    var counter = 0
     override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
     {

      let cell = tableView.dequeueReusableCellWithIdentifier("cell2") as! bdbTableViewCell


        for imageView in cell.threeImages {
            let placeHolder = UIImage(named: "camera")
             imageView.image = placeHolder

            let finalImage = countries[counter++]["imageFile"]
            finalImage!.getDataInBackgroundWithBlock {
                (imageData: NSData?, error: NSError?) -> Void in
                if error == nil {
                    if let imageData = imageData {
                        imageView.image = UIImage(data:imageData)
                    }
                }
            }}
            return cell
    }

【问题讨论】:

  • 您还应该学习如何调试。您的代码第一次工作主要是因为运气,但如果您刷新,或者当您滚动并且单元格离开屏幕并重新打开时,它真的不会工作。您为 counter 使用一个类变量,每次显示或重新显示单元格时,它都会简单地增加 3。这将导致它大于图像数组。您为单元格加载的图像应该基于传递给方法的 indexPath,而不是永远不会重置的计数器。有关详细信息,请参阅下面的答案。

标签: ios swift parse-platform tableview


【解决方案1】:

如果我了解您的数据,则需要移动到数组中包含您所在国家/地区的第一张图片的位置,然后遍历接下来的 3 张图片。

基本上,您应该将计数器声明移动到函数体内,并使用第一张图像的位置对其进行初始化。有关完整实现,请参阅下面的方法定义。

您的代码第一次运行是靠运气。基本上,iOS 将请求第 1 行的单元格,并且您的计数器将通过获取索引 0、1 和 2 处的图像来增加自身。然后 iOS 将请求第 2 行的单元格,它将拉取图像 3、4、5。计数器将不断增加。然后,当您刷新表格时,计数器仍设置为高于图像数组大小的数字,因此您会崩溃。如果 iOS 出于某种原因以不同的顺序要求单元格,您也会错误地显示图像。您需要使用 indexPath.row 参数来获取正确的图像。

您的代码也不会考虑 3 的倍数结束后的其他图像。因此,如果有 17 个图像,则不会在 tableview 中为图像 16 1nd 17 提供一行。要更正此问题,请添加以下函数:

func roundUp(value: Double) -> Int {
    if value == Double(Int(value)) {
        return Int(value)
    } else if value < 0 {
        return Int(value)
    } else {
        return Int(value) + 1
    }
}

然后将tableView的numberOfRowsInSection方法改成如下:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
    return roundUp(Double(countries.count / 3.0))
}

最后,在您的 cellForRowAtIndexPath 函数中,您将不得不处理潜在的尝试查找超过数组大小的图像:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    var imageIndex = (indexPath.row * 3)

    let cell = tableView.dequeueReusableCellWithIdentifier("cell2") as! bdbTableViewCell

    for imageView in cell.threeImages {
        let placeHolder = UIImage(named: "camera")
        imageView.image = placeHolder

        if imageIndex < countries.count
        {
            let finalImage = countries[imageIndex++]["imageFile"]
            finalImage!.getDataInBackgroundWithBlock {
                (imageData: NSData?, error: NSError?) -> Void in
                if error == nil {
                    if let imageData = imageData {
                        imageView.image = UIImage(data:imageData)
                    }
                }
            }
        }
    return cell
}

我相信这将解决您的所有问题。我确实建议您学习有关 UITableView 如何工作以及如何逐步执行您自己的代码进行故障排除的教程。这样你就可以理解为什么你最初的尝试行不通了。

【讨论】:

  • 有趣的方法,不确定我是否理解它。我通常会使用 colelctionView 来处理这类事情,但由于设计原因我不能帮助我设置这个计数器!所以说实话,我真的不明白它是如何充分发挥作用的......我现在就给你一个答案。
  • 我建议您学习如何使用断点,这样您就可以在 tableview 加载时看到您的代码在做什么。能够单步执行您的代码将使您深入了解正在发生的事情,这将使您能够自己解决这些问题。 (jeffreysambells.com/2014/01/14/using-breakpoints-in-xcode)
  • 准确!您是否知道我如何显示例如 16,因为目前它仅显示可被 3 整除的图像...
  • 所以基本上,如果它们是 15,它会显示 5 行...如果它是 16 或 17,它仍然会显示 5,直到它达到 18 并显示 6...如果你这样做,我会打开一个新问题
  • 在您的 numberOfRows 方法中,使用 return ceil(countries.count / 3)
【解决方案2】:
var counter = 0 

应该在函数之前重置

cellForRowAtIndexPath

【讨论】:

  • 之前如何重置?如果我把它放在 cellForRow 中,图像不会改变!它在第一行显示 3 个不同的图像,它们重复同一行的图像多次...
  • @Jack 在你调用刷新函数之前?
  • 但是如何将其重置为 0?
  • @Jack 在 self.tableView.reloadData() 之前可以设置 counter = 0?
  • @Jack 计数器变量的作用域是什么,它在函数中仍然是全局的还是局部的?如果可能的话,在访问数组之前尝试打印国家/地区的大小和内容?
【解决方案3】:

问题

您将 counter 变量声明为静态变量。这就是为什么它不会在 cellForRowAtIndexPath 调用之间被重置,而是在每次调用该方法时递增。

解决办法:

counter 声明移动到函数体。如下:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
     
    var counter = 0

    let cell = tableView.dequeueReusableCellWithIdentifier("cell2") as! bdbTableViewCell

        for imageView in cell.threeImages {
            let placeHolder = UIImage(named: "camera")
             imageView.image = placeHolder

            let finalImage = countries[counter++]["imageFile"]
            finalImage!.getDataInBackgroundWithBlock {
                (imageData: NSData?, error: NSError?) -> Void in
                if error == nil {
                    if let imageData = imageData {
                        imageView.image = UIImage(data:imageData)
                    }
                }
            }}
            return cell
    }

【讨论】:

  • 嘿,谢谢你,但是当我把它放在 cellForRow 中时,图像并没有改变!它在第一行显示 3 个不同的图像,它们在同一行重复图像的次数...
  • 如果您只想拥有问题中所写的一个单元格,请在numberOfRowsInSection 中返回1
  • 我不只是想要 1 个单元格,图像可以是任意数字,但每个 imageView 需要不同...如果我将计数器放在 cellforRow 之外,它可以工作,但如果我更新它崩溃...如果我把它放在 cellForRow 中,它只会更改前 3 个并为所有图像重复它们
  • 那是因为您将不断地获得阵列中的前 3 张图像。你想要的是国家的 3 幅图像。因此,如果单元格行是 2,您需要索引为 3、4、5 的图像。对于单元格第 3 行,您需要索引为 6、7、8 的图像。等等。请参阅我的答案以获得正确的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多