简体   繁体   中英

UITableView Cells shifting positions or disappearing entirely on scroll

I am currently developing and application for the iPhone in Swift, and I have run into a very peculiar error: in one of my UITableViewControllers,enter code here cells disappear or change sections when I scroll up and down on the Table View.

I've been dealing with this issue for a few days now, and it has even prompted me to recode the entire class, but to no avail. I have researched extensively on this error, and I believe it has something to do with my data source and how the tableView handles it, and I have also noticed that other users have had the same problem before, but I cannot find a solution that applies to my problems.

For example, here seems to deal with the cell height, but I have continued to check and double check my code, and the cell height returns the correct values.

In addition, this post talks about different errors with the tableView's data source, but I have a strong pointer to the datasource's alert array and my content and heights are correct in cellForRowAtIndexPath.

This post also deals with my question, but everything I am currently doing with the tableView is on the main thread.

Currently the tableView has 4 sections: the first, second, and fourth contain only one cell and the third has a dynamic amount of cells based on the amount of alerts the user has added (for example, it has 3 alert cells plus one "Add Alert" cell always at the bottom). The only cells that are affected are those in the 2, 3, and 4 sections.

This is what my tableView should look like always:

普通视图

But, however, here is what happens when I scroll:

I first create the variables here:

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

Then I initialize them in 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
}

Below are my TableView methods:

Here is my 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()
    }
}

And my 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
    }
}

And my 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
    }
}

And 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
    }
}

I have tried editing these methods extensively, as well as checking the code in each individual cell. Nothing seems to work.

Does anyone have any solutions to this error? I can always update with more code if necessary, but I believe that either my data source could be the problem, or that the cell's resuse could be creating this error, but I cannot seem to pinpoint anything. Thanks in advance for any help!

UPDATE

Here is my AddAlertCell "refreshCell()" method as well as my UITableViewCell extension:

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:

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

        return table as? UITableView
    }
}

}

You shouldn't need to call beginUpdates / endUpdates when you refresh the cell - these methods are used if you are adding / deleting rows or sections from the tableview.

What happens if you remove the beginUpdates() and endUpdates() calls?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM