UITableViewCell скрывает часть содержимого после появления на экране с помощью пользовательских layoutSubviews ()

#ios #swift #uitableview #uikit #layoutsubviews

#iOS #swift #uitableview #uikit #layoutsubviews

Вопрос:

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

 let cell = tableView.dequeueReusableCell(withIdentifier: MultipleSelectAnswerSurveyTableViewCellIdentifier, for: indexPath) as! MultipleSelectAnswerSurveyTableViewCell
cell.setup(answer: question.answers?[indexPath.row].value ?? "", isSelected: false, style: style, isLastInSection: indexPath.row == (question.answers?.count ?? 1) - 1)
return cell
  

setup() Метод ячейки:

 func setup(answer: String, isSelected: Bool, style: Style, isLastInSection: Bool) {
    self.isLastInSection = isLastInSection
    selectionStyle = .none
    backgroundColor = style.survey.singleSelectAnswerTableViewCell.backgroundColor
    answerLabel.textColor = style.survey.singleSelectAnswerTableViewCell.answerLabelColor
    answerLabel.font = style.survey.singleSelectAnswerTableViewCell.answerLabelFont
    answerLabel.text = answer
    addSubview(answerLabel)
    addSubview(selectionIndicator)
    answerLabel.snp.makeConstraints { make in
        make.left.equalTo(8)
        make.centerY.equalTo(selectionIndicator.snp.centerY)
        make.top.equalTo(8)
        make.bottom.equalTo(-8)
        make.right.equalTo(selectionIndicator.snp.left).offset(-8)
    }
    selectionIndicator.snp.makeConstraints { make in
        make.right.equalTo(-8)
        make.top.greaterThanOrEqualTo(8)
        make.bottom.lessThanOrEqualTo(-8)
        make.width.height.equalTo(26)
    }
}
  

self.isLastInSection переменная используется внутри layoutSubviews() :

 override func layoutSubviews() {
    super.layoutSubviews()
    if isLastInSection {
        roundCorners(corners: [.bottomLeft, .bottomRight], radius: 16.0)
    }
    contentView.layoutIfNeeded()
}
  

И, наконец, roundCorners() :

 extension UIView {
    func roundCorners(corners: UIRectCorner, radius: CGFloat) {
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask
    }
}
  

Когда я удаляю ячейку из очереди с isLastInSection значением false, ячейка отображается как ожидалось (соответствующее видео). Итак, я думаю, что проблема заключается в жизненном цикле ячейки и когда layoutSubview() вызывается. Я перепробовал много решений для подобной проблемы, найденных в разных потоках, но ни одно из них мне не помогло. tableView(_:heightForRowAt:) приводит к правильному отображению третьей ячейки, но у первой закруглены нижние углы. Также все они фиксированы по высоте, и этого не может произойти.
Но что действительно странно: когда я печатаю isLastInSection ячейку при удалении из очереди, которая неожиданно округлена, отладчик возвращает мне false:

(lldb) po indexPath.row == (question.answers?.count ?? 1) - 1
false

введите описание изображения здесь

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

Иерархия представлений отладки

Комментарии:

1. Вы удаляете ячейку из очереди и каждый раз добавляете вложенные просмотры и т.д. Вы не проверяете, есть ли оно уже там, что произойдет в случае повторного использования ячейки. Это, вероятно, нарушает ограничения или что-то в этом роде. Та же проблема с округлением — вы устанавливаете закругленные углы, но вы никогда не отменяете это поведение, если повторно используемая ячейка не должна быть закруглена. Попробуйте без повторного использования ячеек (просто создавайте каждый раз новую ячейку) и проверьте, помогает ли это.

2. @WojciechKulik Создание ячейки без повторного использования решило мою проблему. Но просмотр таблицы, вероятно, теперь будет запаздывать. Конечно, я понимаю, почему ячейка не возвращается к обычному фрейму без округления. Чего я не понимаю, так это почему ячейка имеет закругленные углы, когда isLastInSection параметру присвоено значение false. То же самое с рамкой ячейки и вложенными представлениями. Я настраиваю просмотры каждый раз, когда они удаляются из очереди, но ограничения и т.д. одинаковы.

3. Я переместил добавление вложенных представлений в инициализированную часть ячейки, и проблема все еще возникает, так что это вызвано закруглением углов в layoutSubviews()

Ответ №1:

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

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

Лучшим способом решить эту проблему было бы добавить дополнительную проверку и создать вложенные просмотры только один раз:

 func setup(answer: String, isSelected: Bool, style: Style, isLastInSection: Bool) {
    if self.subviews.count == 0 {
        // adding subviews etc.
        // code that needs to be performed only once for whole cell's life
    }

    self.isLastInSection = isLastInSection
    // set up content that changes for each cell (like text)
    // for example a code depending on parameters of this method
}
  

в качестве альтернативы вы могли бы сохранить какое-либо свойство, подобное isInitialized , и проверить это в начале.

Также ваш метод layoutSubviews должен поддерживать оба случая:

 override func layoutSubviews() {
    super.layoutSubviews()
    if isLastInSection {
        roundCorners(corners: [.bottomLeft, .bottomRight], radius: 16.0)
    } else {
        layer.mask = nil
    }
    contentView.layoutIfNeeded()
}
  

Комментарии:

1. Да! Вот и все! Я должен удалить маску из слоя. Я у тебя в долгу! Также я просто хочу добавить, что мне не нужно проверять self.subviews.count == 0 , потому что теперь все подвиды добавляются в раздел инициализации 🙂