dequeueReusableCell заставляет текст менять цвет

У меня есть представление коллекции, и я использую собственный класс для ячеек. Каждая ячейка имеет два текстовых представления: ChapterTitleTextView и ChapterBodyTextView. Я добавил заполнители в оба текстовых представления следующим образом:

class CustomWriterPageCell: UICollectionViewCell, UITextViewDelegate {

    // When the user taps on a text view
    func textViewDidBeginEditing(_ textView: UITextView) {

        if textView.textColor == .gray {

            textView.text = nil
            textView.textColor = .black
        }
    }

    // When the user taps out of a text view or taps on the done button in the toolbar
    func textViewDidEndEditing(_ textView: UITextView) {

        // If the chapter title text view is empty
        if chapterTitleTextView.text.isEmpty {

            chapterTitleTextView.text = "Chapter Title"
            chapterTitleTextView.textColor = .gray

        }
        // If the chapter body text view is empty
        if chapterBodyTextView.text.isEmpty {

            chapterBodyTextView.text = "Chapter Body"
            chapterBodyTextView.textColor = .gray

        }
    }
}

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

Теперь есть эта проблема с использованием dequeueReusableCell, это то, что он повторно использует ячейки, это вызвало проблему номер 1: все, что я печатаю в текстовом представлении в первой ячейке, отображается в 4-й ячейке, чтобы решить эту проблему, мне пришлось создать 2 глобальных списка чтобы хранить все, что я печатаю, и это отображается в текстовых представлениях ячеек, вот код:

Глобальные списки:

var chapterTitleText = Array(repeating: "", count: 4) // To store the chapter title text
var chapterBodyText = Array(repeating: "", count: 4) // To store the chapter body text

Следующий фрагмент кода находится внутри textViewDidEndEditing из предыдущего

// Append the chapter body text to the chapterBodyText array
let titleText = chapterTitleTextView.text
let titleRow = textView.tag //This the indexPath.row
chapterTitleText[titleRow] = titleText!

// Append the chapter title text to the chapterTitleText array
let bodyText = chapterBodyTextView.text
let bodyRow = textView.tag
chapterBodyText[bodyRow] = bodyText!

И в ячейкеForItemAt:

cell.chapterBodyTextView.tag = indexPath.row
cell.chapterTitleTextView.tag = indexPath.row

cell.chapterTitleTextView.text = chapterTitleText[indexPath.row]
cell.chapterBodyTextView.text = chapterBodyText[indexPath.row]

Это избавило от проблемы №1 (текст в текстовом виде дублируется). Но потом у меня возникла новая проблема, помните текст-заполнитель, о котором я говорил? Когда я что-то набираю в одном из текстовых представлений первой ячейки, цвет текста текстового представления в четвертой ячейке меняется. Я добавлю GIF, воспроизводящий проблему, чтобы помочь читателям понять:

Видео, воспроизводящее проблему


person Abdullah Ajmal    schedule 21.09.2019    source источник


Ответы (2)


Следуя вашей цепочке вопросов, я предлагаю вам делать это каждый раз, когда вы имеете дело с UICollectionView или UITableView.

Определите метод в своем классе ячеек и возьмите любые данные, необходимые для отображения, в качестве аргументов:

func configure(text : String?) { //text is optional here which means it can be nil.
//However, from my previous answer you can replace it with an empty string condition.

    if let txt = text { //or if text != "" 
        self.chapterTitleTextView.text = txt
        self.chapterTitleTextView.text.textColor = .black
    }
    else {// As others mentioned make sure to always handle else because cells are reusable.
        self.chapterTitleTextView.text = "Chapter Title"
        self.chapterTitleTextView.text.textColor = .gray
    }

} 

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

Теперь в cellForItemAt:

 let cell = ...
 cell.configure(text : chapterTitleText[indexPath.row])

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

Прокомментируйте свои проблемы с этим подходом (если они есть), я постараюсь ответить терпеливо, но не ожидайте (копировать-вставить) код.

person Soroush    schedule 21.09.2019

Это происходит из-за механизма повторного использования UICollectionView и UITableView. Поскольку вы обновляете цвет только одним способом, коллекция «запоминает» предыдущий цвет и при новой ячейке воссоздает свое предыдущее состояние. Здесь у вас есть два основных решения.

Во-первых, нужно обновить состояние ячейки для обоих случаев, например:

if myCondition == true {
  color = UIColor.gray
} else {
  color = UIColor.black
}

Второй – использовать prepareForReuse метод UICollectionViewCell

func prepareForReuse() {
  // code that resets state of your cell to default 
}

Описание метода в документации Apple

person inokey    schedule 21.09.2019
comment
Эй, я пробовал это, но он обновляет все ячейки, в любом случае мы могли бы указать ячейку? - person Abdullah Ajmal; 21.09.2019
comment
Проблема с заполнителем, скорее всего, связана с вашими условиями if chapterTitleTextView.text.isEmpty проверьте, играете ли вы с becomeFirstResponder в textView и вызывается ли он до того, как вы установите текст в cellForRow или после - person inokey; 21.09.2019