Обновление метки в UITableViewCell с помощью UIStepper в Swift

Я новичок в Swift и пытаюсь сделать простое приложение для заказа еды. Пользователь может добавить новый заказ, установив продукты name, price и serving. После добавления заказа этот заказ будет отображаться в tableView как FoodTableViewCell, и пользователь может изменить обслуживание с помощью UIStepper с именем stepper в каждой ячейке. Каждый заказ представляет собой FoodItem, хранящийся в массиве с именем foodList, и вы можете увидеть все заказы, перечисленные в tableView, в ShoppingListVC.

Моя проблема: Когда я нажимаю кнопку + или - на stepper, мое servingLabel не меняется на соответствующее значение. Я пытался использовать NotificationCenter, чтобы передать значение обслуживания в stepper и сохранить новое значение обратно в food.serving после stepperValueChanged с шаблоном делегата. Тем не менее, кажется, что все еще есть некоторые ошибки. Я немного запутался после просмотра множества решений в Интернете. Любая помощь приветствуется.

Обновить

Я удалил методы, связанные с NotificationCenter и addTarget, как предложил @Tarun Tyagi. Теперь мое значение UIStepper возвращается к 1, тогда как serveLabels показывает разные количества обслуживания. Поскольку NotificationCenter не помогает, как я могу связать метку и значение шагового двигателя вместе? Рекомендуется ли реализовать еще один делегат?

Вот мои коды (обновлено 8 июля):

Еда

class FoodItem: Equatable {
    
    static func == (lhs: FoodItem, rhs: FoodItem) -> Bool {
        return lhs === rhs
    }
    
    var name: String
    var price: Int
    var serving: Int
    var foodID: String
    
    init(name: String, price: Int, serving: Int) {
        self.name = name
        self.price = price
        self.serving = serving
        self.foodID = UUID().uuidString
    }
}

Вьюконтроллер

import UIKit

class ShoppingListVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    var foodList = [FoodItem]()
    
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.delegate = self
        tableView.dataSource = self
       
        ...
        
        for i in 1...5 {
            let testItem = FoodItem(name: "Food\(i)", price: Int.random(in: 60...100), serving: Int.random(in: 1...10))
            self.foodList.append(testItem)
        }
    }
    
    // MARK: - Table view data source
    
    ...
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "foodCell", for: indexPath) as! FoodTableViewCell
        
        let food = foodList[indexPath.row]
        cell.nameLabel.text = food.name
        cell.priceLabel.text = "$\(String(food.price)) / serving"
        cell.servingLabel.text = "\(String(food.serving)) serving"
        
        cell.stepper.tag = indexPath.row
        
        cell.delegate = self

        return cell
    }
}

// MARK: - FoodTableViewCellDelegate Method.

extension ShoppingListVC: FoodTableViewCellDelegate {
    
    func stepper(_ stepper: UIStepper, at index: Int, didChangeValueTo newValue: Double) {
        let indexPath = IndexPath(item: index, section: 0)
        guard let cell = tableView.cellForRow(at: indexPath) as? FoodTableViewCell else { return }
        let foodToBeUpdated = foodList[indexPath.row]
        
        print("foodToBeUpdated.serving: \(foodToBeUpdated.serving)")
        
        foodToBeUpdated.serving = Int(newValue)
        print("Value changed in VC: \(newValue)")
        
        cell.servingLabel.text = "\(String(format: "%.0f", newValue)) serving"
    }
}

TableViewCell

import UIKit
    
    protocol FoodTableViewCellDelegate: AnyObject {
      func stepper(_ stepper: UIStepper, at index: Int, didChangeValueTo newValue: Double)
    }
    
    class FoodTableViewCell: UITableViewCell {
        
        @IBOutlet weak var nameLabel: UILabel!
        @IBOutlet weak var priceLabel: UILabel!
        @IBOutlet weak var servingLabel: UILabel!
        @IBOutlet weak var stepper: UIStepper!
        
        weak var delegate: FoodTableViewCellDelegate?
        
        @IBAction func stepperValueChanged(_ sender: UIStepper) {
            sender.minimumValue = 1
            servingLabel.text = "\(String(format: "%.0f", sender.value)) serving"
            // Pass the new value to ShoppingListVC and notify which cell to update using tag.
            print("sender.value: \(sender.value)")
            delegate?.stepper(stepper, at: stepper.tag, didChangeValueTo: sender.value)
        }
        
        override func awakeFromNib() {
            super.awakeFromNib()
            print(stepper.value)
        }
    }

person Judy Tsai    schedule 07.07.2021    source источник
comment
Связано: stackoverflow.com/questions/42876739/ и stackoverflow.com/questions/49832352/   -  person vadian    schedule 08.07.2021


Ответы (3)


Изначально FoodTableViewCell является ЕДИНСТВЕННОЙ целью для изменения значения UIStepper (если смотреть на @IBAction внутри FoodTableViewCell).

Когда вы удаляете ячейку из очереди для отображения на экране, вы вызываете -

cell.stepper.addTarget(self, action: #selector(stepperValueChanged(_:)), for: .valueChanged)

что приводит к тому, что ваш экземпляр ShoppingListVC добавляется в качестве дополнительной цели каждый раз при выполнении вызова cellForRow.

Что нужно исправить:

  1. Удалите весь код, связанный с NotificationCenter, из обоих классов.
  2. Также удалите строку cell.stepper.addTarget().

Это даст вам лучшее представление о том, почему это происходит именно так. Обновите свой вопрос с этими изменениями, если у вас все еще нет того, что вы хотите.


ОБНОВИТЬ

// Inside cellForRow
cell.stepper.value = food.serving
person Tarun Tyagi    schedule 07.07.2021
comment
Спасибо за ответ. Я только что удалил две вещи, которые вы упомянули в моем коде, что возвращает мое значение UIStepper к 1, тогда как serveLabels показывает разные числа. Поскольку NotificationCenter не помогает, как я могу связать метку и значение шагового двигателя вместе? Рекомендуется ли реализовать еще один делегат? - person Judy Tsai; 07.07.2021
comment
Пожалуйста, обновите описание вашего вопроса, чтобы показать последний обновленный код после внесения предложенных изменений. - person Tarun Tyagi; 07.07.2021
comment
Я только что обновил описание вопроса. Спасибо. - person Judy Tsai; 08.07.2021
comment
См. ОБНОВЛЕНИЕ. - person Tarun Tyagi; 08.07.2021
comment
Видимо, я слишком все усложнил. Спасибо за вашу помощь! - person Judy Tsai; 08.07.2021

Конфигурация ячейки:

  protocol FoodTableViewCellDelegate: AnyObject {
     func stepper(sender: FoodTableViewCell)
    }

@IBAction func stepperButtonTapped(sender: UIStepper) {
    delegate?.stepperButton(sender: self)
    stepperLabel.text = "\(Int(countStepper.value))"
}

Конфигурация контроллера:

ячейкаForRow:

    cell.countStepper.value = Double(foodList[indexPath.row].serving);
    cell.stepperLabel.text = "\(Int(cell.countStepper.value))"

Метод делегата:

func stepperButton(sender: FoodTableViewCell) {
    if let indexPath = tableView.indexPath(for: sender){
        print(indexPath)
        foodList[sender.tag].serving = Int(sender.countStepper.value)
    }
}
person RTXGamer    schedule 07.07.2021

Пожалуйста, проверьте модуль степпера значений, он вам поможет: степпер значений

Интегрируйте модуль шагового двигателя и используйте приведенный ниже код для базовой реализации.

import ValueStepper

let valueStepper: ValueStepper = {
let stepper = ValueStepper()
stepper.tintColor = .whiteColor()
stepper.minimumValue = 0
stepper.maximumValue = 1000
stepper.stepValue = 100
return stepper
}()

override func viewDidLoad() {
super.viewDidLoad()       
valueStepper.addTarget(self, action: "valueChanged:",     forControlEvents: .ValueChanged)
}

@IBAction func valueChanged1(sender: ValueStepper) {
// Use sender.value to do whatever you want
}

Это упрощает имплантацию настраиваемого шагового двигателя. Возьмите вывод значения шагового представления в виде таблицы и используйте его.

person Vinayak Bhor    schedule 08.07.2021
comment
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. – Из обзора - person Helge Becker; 08.07.2021