ios - UITableView 单元格在滚动时移动位置或完全消失

标签 ios iphone swift uitableview

我目前正在 Swift 中为 iPhone 开发和应用程序,我遇到了一个非常奇怪的错误:在我的一个 UITableViewControllers 中,当我在 TableView 上上下滚动时,在此处输入代码单元格消失或更改部分。

我已经处理这个问题好几天了,它甚至促使我重新编码整个类,但无济于事。我对这个错误进行了广泛的研究,我相信它与我的数据源以及 tableView 处理它的方式有关,而且我也注意到其他用户之前也遇到过同样的问题,但我找不到适用的解决方案解决我的问题。

例如,here似乎处理单元格高度,但我继续检查并仔细检查我的代码,单元格高度返回正确的值。

此外,this post讨论了 tableView 数据源的不同错误,但我有一个指向数据源警报数组的强指针,并且 cellForRowAtIndexPath 中的内容和高度是正确的。

This post也处理我的问题,但我目前对 tableView 所做的一切都在主线程上。

当前 tableView 有 4 个部分:第一、第二和第四部分仅包含一个单元格,第三部分具有基于用户添加的警报数量的动态单元格数量(例如,它有 3 个警报单元格加上一个“添加警报”单元始终位于底部)。唯一受影响的单元格是第 2、3 和 4 部分中的单元格。

这就是我的 tableView 应该看起来的样子:

Normal View

但是,当我滚动时会发生以下情况:

我首先在这里创建变量:

var currentPrayer: Prayer! // This is the prayer that the user is currently editing
var prayerAlerts: NSMutableOrderedSet! // This is the mutable set of the prayer alerts that are included in the prayer

然后我在 viewDidLoad 中初始化它们:

override func viewDidLoad() {
    super.viewDidLoad()

    if currentPrayer == nil {
        NSException(name: "PrayerException", reason: "Current Prayer is nil! Unable to show prayer details!", userInfo: nil).raise()
    }

    navItem.title = currentPrayer.name // Sets the Nav Bar title to the current prayer name
    prayerAlerts = currentPrayer.alerts.mutableCopy() as! NSMutableOrderedSet // This passes the currentPrayer alerts to a copy called prayerAlerts
    prayerAlertsCount = prayerAlerts.count + 1
}

以下是我的 TableView 方法:

这是我的 cellForRowAtIndexPath:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    println("cellForRowAtIndexPath called for the \(cellForRowRefreshCount) time")
    cellForRowRefreshCount += 1

    switch indexPath.section {
    case 0:
        var cell = tableView.dequeueReusableCellWithIdentifier(DetailsExtendedCellID, forIndexPath: indexPath) as! PrayerDetailsExtendedCell
        cell.currentPrayer = currentPrayer
        cell.refreshCell()

        return cell

    case 1:
        var cell = tableView.dequeueReusableCellWithIdentifier(SetPrayerDateCellID, forIndexPath: indexPath) as! AddPrayerDateCell

        cell.currentPrayer = currentPrayer
        cell.refreshCell(false, selectedPrayer: cell.currentPrayer)

        return cell

    case 2:
        if indexPath.row == prayerAlerts.count {
            var cell = tableView.dequeueReusableCellWithIdentifier(AddNewAlertCellID, forIndexPath: indexPath) as! AddPrayerAlertCell
            cell.currentPrayer = currentPrayer
            cell.refreshCell(false, selectedPrayer: currentPrayer)
            cell.saveButton.addTarget(self, action: "didSaveNewAlert", forControlEvents: .TouchDown)

            return cell
        } else {
            var cell = tableView.dequeueReusableCellWithIdentifier(PrayerAlertCellID, forIndexPath: indexPath) as! PrayerAlertCell

            let currentAlert = prayerAlerts[indexPath.row] as! Alert
            cell.alertLabel.text = AlertStore.sharedInstance.convertDateToString(currentAlert.alertDate)

            return cell
        }

    case 3:
        var cell = tableView.dequeueReusableCellWithIdentifier(AnsweredPrayerCellID, forIndexPath: indexPath) as! PrayerAnsweredCell
        cell.accessoryType = currentPrayer.answered == true ? .Checkmark : .None

        return cell

    default:
        return UITableViewCell()
    }
}

还有我的 numberOfRowsInSection:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    switch section {
    case 0: println("Returning 1 Row for section 0"); return 1
    case 1: println("Returning 1 Row for section 1"); return 1
    case 2: println("Returning \(prayerAlertsCount) Rows for section 2"); return prayerAlertsCount
    case 3: println("Returning 1 Row for section 3"); return 1
    default: println("Returning 0 Rows for section Default"); return 0
    }
}

还有我的 heightForRowAtIndexPath:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    switch indexPath.section {
    case 0: return UITableViewAutomaticDimension

    case 1:
        let cell = tableView.cellForRowAtIndexPath(indexPath) as? AddPrayerDateCell

        if let thisCell = cell {
            let isAdding = thisCell.isAddingDate

            if isAdding {
                if thisCell.selectedType == PrayerType.None || thisCell.selectedType == PrayerType.Daily {
                    println("Selected Type is None or Daily")
                    println("Returning a height of 89 for AddPrayerDateCell")
                    return 89
                } else {
                    println("Returning a height of 309 for AddPrayerDateCell")
                    return 309
                }
            } else {
                println("Returning a height of 44 for AddPrayerDateCell")
                return 44
            }
        } else {
            println("Returning a default height of 44 for AddPrayerDateCell")
            return 44
        }

    case 2:
        if indexPath.row == prayerAlerts.count {
            let cell = tableView.cellForRowAtIndexPath(indexPath) as? AddPrayerAlertCell

            if let thisCell = cell {
                let isAdding = thisCell.isAddingAlert

                if isAdding { return 309 }; return 44
            } else {
                return 44
            }
        } else {
            return 44
        }

    case 3: return 44

    default: return 44
    }
}

和estimatedHeightForRowAtIndexPath:

override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    switch indexPath.section {
    case 0: return 130
    case 1: return 44
    case 2: return 44
    case 3: return 44
    default: return 44
    }
}

我尝试广泛编辑这些方法,并检查每个单元格中的代码。似乎没有任何作用。

有没有人能解决这个错误?如有必要,我总是可以使用更多代码进行更新,但我相信我的数据源可能是问题所在,或者单元格的重新使用可能会造成此错误,但我似乎无法查明任何内容。预先感谢您的帮助!

更新

这是我的 AddAlertCell“refreshCell()”方法以及我的 UITableViewCell 扩展:

func refreshCell(didSelect: Bool, selectedPrayer: Prayer!) {
    tableView?.beginUpdates()

    selectionStyle = didSelect == true ? .None : .Default

    saveButton.hidden = !didSelect
    cancelButton.hidden = !didSelect
    addNewAlertLabel.hidden = didSelect

    isAddingAlert = didSelect

    dateLabel.text = AlertStore.sharedInstance.convertDateToString(datePicker.date)

    println("AddPrayerAlertCell: Cell Refreshed")

    tableView?.scrollEnabled = !didSelect
    tableView?.endUpdates()
}

UITableViewCell 扩展:

extension UITableViewCell {
var tableView: UITableView? {
    get {
        var table: UIView? = superview
        while !(table is UITableView) && table != nil {
            table = table?.superview
        }

        return table as? UITableView
    }
}

}

最佳答案

刷新单元格时,您不需要调用 beginUpdates/endUpdates - 如果您要从表格 View 中添加/删除行或部分,则使用这些方法。

如果删除 beginUpdates()endUpdates() 调用,会发生什么?

关于ios - UITableView 单元格在滚动时移动位置或完全消失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31304076/

相关文章:

ios - 仅在部分 View 上接收点击手势

ios - 以编程方式(使用 SnapKit)构建 UI 时如何正确使用大小类?

ios - MVVMCross 绑定(bind)属性在 iOS 模拟器中工作正常,但在设备上失败

ios - 获取给出属性名称的属性类

iphone - 绘制速度不够快,无法跟上 TouchMoved 的速度?

iphone - 如何获取通讯录图片数据的裁剪框?

ios - 如何在 objective-c 中的单个 View Controller 上加载多个 UIView

ios - 向以编程方式添加的 subview 添加右对齐约束

swift - 函数 opaque 返回类型被推断为 ... 它根据自身定义了 opaque 类型

IOS map (谷歌地图 API 与 MapKit?)