#ios #uitableview #autolayout #uistackview
#iOS #uitableview #автоматическое описание #uistackview
Вопрос:
Я создал пользовательский интерфейс для ячейки в классе ячеек ниже:
final class OptionTVCell: UITableViewCell {
fileprivate static let id = String(describing: OptionTVCell.self)
private var defaultTintColor: UIColor {
let color = AppConfiguration.sharedAppConfiguration.appTextColor
return Utility.hexStringToUIColor(hex: color ?? "ffffff")
}
// MARK: - Subviews
lazy private(set) var optionImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.heightAnchor.constraint(equalToConstant: 24).isActive = true
imageView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
imageView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
let widthConstraint = imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor)
widthConstraint.priority = UILayoutPriority(998)
widthConstraint.isActive = true
imageView.contentMode = .scaleAspectFit
imageView.tintColor = defaultTintColor
return imageView
}()
lazy private(set) var optionNameLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.setContentHuggingPriority(.defaultLow, for: .horizontal)
label.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
label.textColor = defaultTintColor
label.numberOfLines = 0
let fontSize: CGFloat = Constants.shared.IPHONE ? 14 : 20
label.font = UIFont(name: Utility.getFontName(), size: fontSize)
return label
}()
lazy private(set) var separatorLineView: UIView = {
let separator = UIView()
separator.translatesAutoresizingMaskIntoConstraints = false
let heightConstraint = separator.heightAnchor.constraint(equalToConstant: 1)
heightConstraint.priority = UILayoutPriority(999)
heightConstraint.isActive = true
separator.backgroundColor = defaultTintColor.withAlphaComponent(0.5)
return separator
}()
lazy private(set) var separatorContainerStackView: UIStackView = {
let verticalStackView = UIStackView()
verticalStackView.translatesAutoresizingMaskIntoConstraints = false
verticalStackView.axis = .vertical
verticalStackView.spacing = 10
verticalStackView.distribution = .fillProportionally
verticalStackView.alignment = .fill
return verticalStackView
}()
lazy private(set) var iconContainerStackView: UIStackView = {
let horizontalStackView = UIStackView()
horizontalStackView.translatesAutoresizingMaskIntoConstraints = false
horizontalStackView.axis = .horizontal
horizontalStackView.spacing = 16
horizontalStackView.distribution = .fill
horizontalStackView.alignment = .center
return horizontalStackView
}()
// MARK: - Init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Life Cycle
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
super.setHighlighted(highlighted, animated: animated)
let color = highlighted
? Utility.hexStringToUIColor(hex: AppConfiguration.sharedAppConfiguration.primaryHoverColor ?? "ffffff")
: defaultTintColor
optionImageView.tintColor = color
optionNameLabel.textColor = color
}
// MARK: - Setup
private func setupView() {
selectionStyle = .none
backgroundColor = .clear
contentView.backgroundColor = .clear
setupIconStackView()
setupSeparatorStackView()
}
private func setupSeparatorStackView() {
contentView.addSubview(separatorContainerStackView)
NSLayoutConstraint.activate([
separatorContainerStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
separatorContainerStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
separatorContainerStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
separatorContainerStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16)
])
separatorContainerStackView.addArrangedSubview(iconContainerStackView)
separatorContainerStackView.addArrangedSubview(separatorLineView)
}
private func setupIconStackView() {
iconContainerStackView.addArrangedSubview(optionImageView)
iconContainerStackView.addArrangedSubview(optionNameLabel)
let labelWidth = optionNameLabel.widthAnchor.constraint(greaterThanOrEqualTo: iconContainerStackView.widthAnchor, constant: -40)
labelWidth.isActive = true
}
// MARK: - Configure
}
Ниже приведена реализация TableView для cell:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return CGFloat(cellHeight)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard
let cell = tableView.dequeueReusableCell(withIdentifier: OptionTVCell.id) as? OptionTVCell,
indexPath.row <= optionsArray.count-1,
let navItem = optionsArray[indexPath.row] as? OptionItem
else {
return UITableViewCell()
}
cell.separatorLineView.isHidden = navItem.hasSeparator == true ? false : true
if let imageName = navItem.pageIcon, !imageName.isEmpty {
if let optionImage = UIImage(named: imageName)?.withRenderingMode(.alwaysTemplate) {
cell.optionImageView.image = optionImage
cell.optionImageView.isHidden = false
} else {
let listImageStringURL = imageName.appending("?w=(Utility.sharedUtility.getImageSizeAsPerScreenResolution(size: cell.optionImageView.frame.size.width)))amp;h=(Utility.sharedUtility.getImageSizeAsPerScreenResolution(size: cell.optionImageView.frame.size.height))")
if let imageURL = URL(string: listImageStringURL) {
cell.optionImageView.af.setImage(
withURL: imageURL,
placeholderImage: nil,
filter: nil,
imageTransition: .crossDissolve(0.2)
)
}
}
} else {
cell.optionImageView.isHidden = true
}
cell.optionNameLabel.text = navItem.title?.uppercased()
return cell
}
Как вы можете видеть, разделитель и значок могут быть скрыты или показаны и соответствующим образом изменяют высоту ячейки. Проблема в том, что саморазмер не работает при первоначальном удалении из очереди, но после прокрутки он исправляется сам.
Вот ошибка времени выполнения, вызванная из автозапуска: 1.
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x600002525810 'fittingSizeHTarget' UIStackView:0x7fafd1e8c330.width == 0 (active)>",
"<NSLayoutConstraint:0x600002524f50 'UISV-canvas-connection' UIStackView:0x7fafd1e8c330.leading == UIImageView:0x7fafd1e8c4c0.leading (active)>",
"<NSLayoutConstraint:0x600002524fa0 'UISV-canvas-connection' H:[UILabel:0x7fafd1e8aa30]-(0)-| (active, names: '|':UIStackView:0x7fafd1e8c330 )>",
"<NSLayoutConstraint:0x600002524ff0 'UISV-spacing' H:[UIImageView:0x7fafd1e8c4c0]-(16)-[UILabel:0x7fafd1e8aa30] (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600002524ff0 'UISV-spacing' H:[UIImageView:0x7fafd1e8c4c0]-(16)-[UILabel:0x7fafd1e8aa30] (active)>
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x600002500410 V:|-(8)-[UIStackView:0x7fafd1f47c90] (active, names: '|':UITableViewCellContentView:0x7fafd1f0cf90 )>",
"<NSLayoutConstraint:0x600002500820 UIStackView:0x7fafd1f47c90.bottom == UITableViewCellContentView:0x7fafd1f0cf90.bottom - 8 (active)>",
"<NSLayoutConstraint:0x600002500550 UIImageView:0x7fafd1f569f0.height == 24 (active)>",
"<NSLayoutConstraint:0x600002502120 'UISV-canvas-connection' V:[_UILayoutSpacer:0x60000399e2b0'UISV-alignment-spanner']-(0)-| (active, names: '|':UIStackView:0x7fafd1f5a1e0 )>",
"<NSLayoutConstraint:0x600002500370 'UISV-canvas-connection' UIStackView:0x7fafd1f5a1e0.centerY == UIImageView:0x7fafd1f569f0.centerY (active)>",
"<NSLayoutConstraint:0x600002500000 'UISV-canvas-connection' UIStackView:0x7fafd1f47c90.top == UIStackView:0x7fafd1f5a1e0.top (active)>",
"<NSLayoutConstraint:0x6000025d2260 'UISV-canvas-connection' V:[UIView:0x7fafd1f48db0]-(0)-| (active, names: '|':UIStackView:0x7fafd1f47c90 )>",
"<NSLayoutConstraint:0x6000025b2490 'UISV-spacing' V:[UIStackView:0x7fafd1f5a1e0]-(10)-[UIView:0x7fafd1f48db0] (active)>",
"<NSLayoutConstraint:0x600002502210 'UISV-spanning-boundary' _UILayoutSpacer:0x60000399e2b0'UISV-alignment-spanner'.bottom >= UIImageView:0x7fafd1f569f0.bottom (active)>",
"<NSLayoutConstraint:0x6000025f2cb0 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fafd1f0cf90.height == 43.5 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600002500550 UIImageView:0x7fafd1f569f0.height == 24 (active)>
Что я сделал:
Я добавил несколько ограничений и уменьшил приоритет, поскольку они выдавали некоторые ошибки, когда были скрыты, но имели фиксированную высоту / ширину. Но ошибки по-прежнему возникают, и самонастройка по-прежнему не работает должным образом.
Ответ №1:
Вы должны быть в состоянии избавиться от ошибок / предупреждений вашего макета одним изменением:
lazy private(set) var separatorContainerStackView: UIStackView = {
let verticalStackView = UIStackView()
verticalStackView.translatesAutoresizingMaskIntoConstraints = false
verticalStackView.axis = .vertical
verticalStackView.spacing = 10
// use .fill NOT .fillProportionally
verticalStackView.distribution = .fill // .fillProportionally
verticalStackView.alignment = .fill
return verticalStackView
}()
Совет: при работе с UIStackView
забудьте о .fillProportionally
настройке распространения. Существуют очень специфические макеты, где это уместно, но вы вряд ли столкнетесь с ними. И, как вы видите, это может вызвать проблемы при неправильном использовании.
В качестве дополнительных примечаний:
lazy private(set) var optionImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.heightAnchor.constraint(equalToConstant: 24).isActive = true
// you can use this
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true
// none of this is needed
//imageView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
//imageView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
//let widthConstraint = imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor)
//widthConstraint.priority = UILayoutPriority(998)
//widthConstraint.isActive = true
imageView.contentMode = .scaleAspectFit
imageView.tintColor = defaultTintColor
return imageView
}()
и:
lazy private(set) var optionNameLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
// these two are not needed
//label.setContentHuggingPriority(.defaultLow, for: .horizontal)
//label.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
label.textColor = defaultTintColor
label.numberOfLines = 0
let fontSize: CGFloat = Constants.shared.IPHONE ? 14 : 20
label.font = UIFont(name: Utility.getFontName(), size: fontSize)
return label
}()