Добавить / удалить подпредставление и ограничения в UITableViewCell при нажатии кнопки

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

Мое представление содержимого UITableViewCell определено в раскадровке и имеет начальный макет, подобный этому:

Добавить / удалить подпредставление и ограничения в UITableViewCell при нажатии кнопки

В моем UITableViewController, когда я удаляю свои ячейки из очереди, я вызываю на моем UITableViewCell метод, который настраивает данные, которые будут отображаться в ячейке, прежде чем я верну их. Одно из свойств, которое я проверяю, - это свойство .isNew, о котором я упоминал ранее. Если это значение истинно, то я создаю UIButton и вставляю его в качестве подпредставления в представление содержимого ячейки, поэтому я получаю что-то вроде этого:

Добавить / удалить подпредставление и ограничения в UITableViewCell при нажатии кнопки

Просто для контекста, эта кнопка покажет «новое» изображение, чтобы указать, что этот элемент является новым. Я также подключаю метод, который срабатывает при нажатии кнопки. Этот метод также определен в моем UITableViewCell и выглядит так:

@objc func newIndicatorButtonTapped(sender: UIButton!) {
   // call delegate method and pass this cell as the argument
   delegate?.newIndicatorButtonTapped(cell: self)
}

Я также создал протокол, который определяет метод делегата. Мой UITableViewController соответствует этому, и я вижу, что этот код срабатывает, когда я нажимаю кнопку в своей ячейке (ах). Вот метод делегата (определенный в расширении на моем UITableViewController):

func newIndicatorButtonTapped(cell: UITableViewCell) {        
    if let indexPath = self.tableView.indexPath(for: cell) {
        print(indexPath.row)
    }
}

Я вижу, что строка из indexPath правильно распечатывается в Xcode, когда я нажимаю на ячейку. Когда мой пользователь нажимает на эту кнопку, мне нужно удалить ее (кнопку) и обновить ограничение для моего UILabel, чтобы оно снова выровнялось с передним краем представления содержимого, как показано в первом макете выше. К сожалению, у меня, похоже, возникла проблема с переработкой ячеек, потому что UIButton исчезает и снова появляется в разных ячейках, когда я прокручиваю их. Нужно ли мне сбрасывать компоновку / внешний вид ячейки, прежде чем она будет переработана, или я что-то неправильно понимаю в том, как работает переработка элементов? Любые советы будут очень признательны!

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
615
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

В этом очень легко убедиться, изменив цвет текста базовой ячейки.

Например:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "MyID", for: indexPath) as! MyCustomCell

    if indexPath.row == 3 {
        cell.theLabel.textColor = .red
    }

    return cell
}

Как и следовало ожидать, при первой загрузке таблицы цвет текста для 4-й строки изменится (индексирование строк начинается с нуля).

Однако предположим, что у вас есть 100 строк? По мере прокрутки ячейки будут иметь вид повторно использованный ... и каждый раз, когда эта исходная 4-я ячейка будет повторно использоваться, в ней все равно будет отображаться красный текст.

Итак, как вы догадались, да ... вам нужно «сбрасывать» свою ячейку до ее исходного макета / содержимого / цветов / и т. д. Каждый раз, когда вы хотите ее использовать:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "MyID", for: indexPath) as! MyCustomCell

    if indexPath.row == 3 {
        cell.theLabel.textColor = .red
    } else {
        cell.theLabel.textColor = .black
    }

    return cell
}

Возможно, вы захотите скрыть кнопку, а затем изменить макет при ее нажатии.

Запуск действия из ячейки в tableView с помощью протокола, а затем сброс макета при повторном использовании ячейки - хороший способ сделать это.

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Cell
    cell.isNew = indexPath.row == 0 ? true : false
    cell.layoutIfNeeded()
    return cell
}

И класс ячейки должен быть похож на: (вы можете делать то, что вам нужно, изменяя ограничение Autolayout, напрямую манипулируя фреймом или используя UIStackView)

class Cell: UITableViewCell {
    var isNew: Bool = false {
        didSet {
            if isNew {
                button.isHidden = true
                leftConstraint.constant = 20
            } else {
                button.isHidden = false
                leftConstraint.constant = 100
            }                    
            self.setNeedsLayout()
        }
    }
    var button: UIButton!
    var label: UILabel!
    var leftConstraint: NSLayoutConstraint!
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        button = UIButton(type: .system)
        button.setTitle("Click", for: .normal)
        self.contentView.addSubview(button)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.widthAnchor.constraint(equalToConstant: 50).isActive = true
        button.heightAnchor.constraint(equalToConstant: 10).isActive = true
        button.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 20).isActive = true
        button.topAnchor.constraint(equalTo: self.topAnchor, constant: 20).isActive = true

        label = UILabel()
        label.text = "Label"
        self.contentView.addSubview(label)
        label.translatesAutoresizingMaskIntoConstraints = false
        label.widthAnchor.constraint(equalToConstant: 200).isActive = true
        label.heightAnchor.constraint(equalToConstant: 20).isActive = true
        label.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
        leftConstraint = label.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 100)
        leftConstraint.isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Другие вопросы по теме