reloadData() вызывается, когда прокрутка влияет на внутренние данные

Таким образом, похоже, что табличное представление в Swift динамически перезагружает данные на экран в зависимости от положения прокрутки. Однако в моем случае я не хочу, чтобы механизм работал таким образом, поскольку элементы в моем табличном представлении обновляют общую стоимость заказа пользователя и излишне искажают общее значение цены. Когда я прокручиваю вверх или вниз, ячейки, в которых вызываются данные перезагрузки, повторно выполняют математику и приводят к неправильной общей цене.

Мое желание состоит в том, чтобы пересчитывать цену с помощью функции reloadData() только тогда, когда UIStepper нажимается на определенную ячейку, а не при прокрутке. Я сделал это с помощью функции IBAction, которая вызывает reloadData при нажатии шагового двигателя. Проблема с математикой и внутренними данными возникает, когда представление таблицы прокручивается и неоднократно вызывает функцию reloadData по мере изменения видимых ячеек, чего я не хочу делать для какой-либо формы прокрутки.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let add_on = JSON(self.add_ons[indexPath.row])
        let cell = tableView.dequeueReusableCell(withIdentifier: "AddOnCell", for: indexPath) as! AddOnTableViewCell

        quantities[indexPath.row] = cell.quantity
        //cell.quantityLabel.text = String(quantities[indexPath.row])

        let price = Double(add_on["price"].int!)/100.0
        let quantity:Double = Double(quantityLabel.text!)!

        //when user tries to increase add-on quantity beyond ticket quantity
        if(Int(quantity) < cell.quantity){
            print("QUANTITY UPDATED")
            cell.quantity -= 1
            quantities[indexPath.row] = cell.quantity
        }
        self.addOnPrice = updateTotal(quantities: self.quantities)
        totalPriceLabel.text = (Double(event_subtotal * quantity) + addOnPrice).dollarRound()

        cell.addonLabel?.text = add_on["name"].string! + " (+$\(price.roundTo(places: 2))" + ")"

        return cell
}

func updateTotal(quantities: [Int]) -> Double{
    var result:Double = 0.0
    for i in 0..<quantities.count{
        let curr_addon = JSON(self.add_ons[i])
        let price = Double(curr_addon["price"].int!)/100.0
        result += Double(quantities[i]) * price
    }
    return result.dollarRoundDouble()
}

@IBAction func stepperClicked(_ sender: UIStepper) {
    self.addOnPrice = 0.0
    AddOnTableView.reloadData()
}

Ниже представлена ​​модель данных ячейки:

class AddOnTableViewCell: UITableViewCell{

    @IBOutlet weak var addonLabel: UILabel!
    @IBOutlet weak var quantityLabel: UILabel!
    @IBOutlet weak var quantityStepper: UIStepper!
    var quantity : Int  = 0 {
        didSet{
            self.quantityLabel.text = String(quantity)
            self.quantityStepper.value = Double(quantity)
        }
    }

    //ref: https://stackoverflow.com/questions/42876739/swift-increment-label-with-stepper-in-tableview-cell
    @IBAction func quantityStep(_ sender: UIStepper) {
        self.quantity = Int(sender.value)
        self.quantityLabel.text = String(quantity)
    } 
}

Поведение: при прокрутке вычисленная общая цена меняется без пошагового взаимодействия. В зависимости от положения прокрутки количество для 1 элемента «заменяет» значение количества на другой элемент. Количество первого элемента, ставшее 1, по какой-то причине обновляет количество последнего элемента. Не удалось отладить это поведение.

Пользовательский интерфейс для справки введите здесь описание изображения


person btrballin    schedule 04.05.2019    source источник
comment
Это звучит как проблема, связанная с повторным использованием ячеек. Можете ли вы поделиться своим кодом делегата табличного представления?   -  person Chris    schedule 04.05.2019
comment
Спасибо за ответ. Я пошел дальше и обновил вопрос скриншотом пользовательского интерфейса, а также исходным кодом. Если какая-то часть не имеет смысла, дайте мне знать, и я могу уточнить.   -  person btrballin    schedule 04.05.2019
comment
Какая переменная содержит общую цену? Если интерфейс всегда будет выглядеть так, то со статическими ячейками может быть проще (т.е. использовать IB и не создавать ячейки в коде). Кроме того, может быть лучше не иметь материал модели данных (количество) в качестве свойства элемента пользовательского интерфейса (ячейки). Модель данных должна быть невидима за кулисами, а данные, взятые из нее, должны отображаться в ячейках. Я думаю о способе решить эту проблему.   -  person Chris    schedule 04.05.2019
comment
totalPriceLabel — это метка, содержащая общую цену. self.addOnPrice — это то, что добавляется к промежуточной сумме для расчета общей цены.   -  person btrballin    schedule 05.05.2019


Ответы (1)


В UIKit ячейки повторно используются при прокрутке списка. Предположим, что значения ячеек просто приходят и уходят. Он не должен иметь долгосрочных значений. Расчет суммы не должен быть в методе cellForRow. Прежде всего, вам не хватает модели данных для хранения всех данных. Способ сделать это заключается в том, что вы создаете структуру, которая представляет надстройку (имеет количество, цену и т. д.). Когда json получен, вы все его анализируете и сохраняете в массиве. Затем массив используется в качестве основного источника. cellForRow должен использовать данные там и передать структуру AddOnTableViewCell, чтобы все было хорошо разделено. Когда пользователь достигает шага количества, он должен вызвать созданный вами класс делегата, который реализован в контроллере представления, который обновляет массив с обновленным количеством, а затем подсчитывает итог. Предположим, что значения ячеек просто приходят и уходят.

Итак, создайте новый класс:

protocol AddOnUpdated: class {
    func quantityUpdated(label: String, value: Int)
}

Затем добавьте участника в свою ячейку {

class AddOnTableViewCell: UITableViewCell{
  weak var delegate: AddOnUpdated?

  func quantityStep {
     .. same code
     delegste?.quantityUpdated(label, value)
  }
}

Затем в контроллере представления не обновляйте итоги при создании ячейки. Просто реализуйте делегат и установите значение delegate при создании ячейки на self.

Итак, теперь делегат должен вызываться всякий раз, когда обновляется значение любой ячейки. В этот момент вам нужно обновить массив значений и обновить итог.

Надеюсь, все это имеет смысл.

person Yuval Tal    schedule 04.05.2019
comment
Если вы не возражаете, можете ли вы завершить исходный код для обновления количества и вызова делегата с обновленными значениями? Я все еще новичок, и ваша реализация не очень понятна. - person btrballin; 05.05.2019
comment
Не могли бы вы поддержать свое решение более полным фрагментом исходного кода? Я пытаюсь исправить эту ошибку в ближайшее время, чтобы перейти к другим отложенным задачам для моего приложения. - person btrballin; 06.05.2019
comment
medium.com/@ aapierce0/ - person Yuval Tal; 08.05.2019
comment
stackoverflow.com/questions/39480831/ - person Yuval Tal; 08.05.2019
comment
Итак, позвольте мне посмотреть, правильно ли я это понял... (1) создать массив объектов add_on (структуру), содержащих количество, название и цену, на основе данных, полученных из JSON. (2) Реализовать протокол AddOnUpdated в моем контроллере представления с функцией AddOnUpdated, которая обновляет количество соответствующего дополнения в массиве, который мы создали из данных JSON, а также вычисляет общее количество c. - person btrballin; 08.05.2019
comment
Я попробовал ваше решение, и я вижу, что оно куда-то идет. Ошибка, которую я вижу в своем представлении списка, заключается в том, что когда я прокручиваю вниз и нажимаю на степпер, он обновляет количество 0 до 4, если первое добавление количества элемента было установлено на 3 перед прокруткой вниз. Я использую sender.value для увеличения/уменьшения количества каждой ячейки, как вы можете видеть в моем коде выше, и по какой-то причине это значение не уникально для каждой ячейки и странным образом распределяется только между первой и последней ячейками. - person btrballin; 08.05.2019