AutoLayout: многострочная метка и кнопка фиксированного размера

Я пытаюсь воспроизвести следующий макет из приложения Kayak: приложение для каяка

Макет состоит из UICollectionViewCell с UILabel и INUIAddVoiceShortcutButton.

Однако в моей реализации метка не заставляет ячейку растягиваться дальше, если текст не помещается: Обрезка текста

Как сделать так, чтобы UICollectionViewCell увеличивался вместе с меткой, а не урезать метку до размера ячейки?

Весь код ячейки:

final class AddToSiriCell: CornerMaskCellBase {
  lazy var button: INUIAddVoiceShortcutButton = {
    let b = INUIAddVoiceShortcutButton(style: .whiteOutline)
    return b
  }()

  lazy var textLabel: UILabel = {
    let label = UILabel()
    label.numberOfLines = 0
    return label
  }()

  override init(frame: CGRect) {
    super.init(frame: frame)
    configureViews()
  }

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

  private func configureViews() {
    textLabel.text = "View balance with your pre-recorded Siri Command .View balance with your pre-recorded Siri Command  View balance with your pre-recorded Siri Command View balance with your pre-recorded Siri Command "
    contentView.backgroundColor = .white
    [button, textLabel].forEach(contentView.addSubview)
    button.snp.makeConstraints { (make) in
      make.top.bottom.trailing.equalTo(contentView.layoutMarginsGuide)
    }

    textLabel.snp.makeConstraints { (make) in
      make.top.bottom.leading.equalTo(contentView.layoutMarginsGuide).priority(.required)
      make.trailing.equalTo(button.snp.leading).priority(.required)
    }
  }
}

Обновление 1: добавлен «базовый класс» с фиксированной шириной. Вот базовый класс, который я использую для всех ячеек в UICollectionView:

import UIKit
import SnapKit

class AutoSizingCellBase: UICollectionViewCell {
  override class var requiresConstraintBasedLayout: Bool {
    return true
  }

  private final var widthConstraint: Constraint?

  override init(frame: CGRect) {
    super.init(frame: frame)
    contentView.layoutMargins = UIEdgeInsets(padding: 14)
  }

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

  override func updateConstraints() {
    if widthConstraint == nil {
      if let window = window {
        let width = window.bounds.width - 16
        contentView.snp.makeConstraints { (make) in
          widthConstraint = make.width.equalTo(width).priority(.required).constraint
        }
      }
      contentView.translatesAutoresizingMaskIntoConstraints = true
    }
    super.updateConstraints()
  }
}

person Richard Topchii    schedule 17.10.2018    source источник


Ответы (2)


Установите верхнее ограничение как для метки, так и для кнопки на greaterThanOrEqual

Установите нижнее ограничение как для метки, так и для кнопки на lessThanOrEqual


Изменить:

Оба должны также иметь ограничения centerY.

Вот полный пример (у меня не iOS 12, поэтому я использовал стандартную UIButton вместо INUIAddVoiceShortcutButton). Я также установил голубой фон метки, чтобы было легко увидеть полученную рамку:

//
//  SnapTableViewController.swift
//
//  Created by Don Mag on 10/19/18.
//

import UIKit

class SnapCell: UITableViewCell {

    lazy var theButton: UIButton = {
        let b = UIButton()
        b.backgroundColor = .yellow
        b.setTitle("Add to Siri", for: .normal)
        b.setTitleColor(.black, for: .normal)
        b.layer.cornerRadius = 8
        b.layer.borderColor = UIColor.black.cgColor
        b.layer.borderWidth = 1
        return b
    }()

    lazy var theLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 0
        label.backgroundColor = .cyan
        return label
    }()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        configureViews()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configureViews()
    }

    func configureViews() -> Void {

        contentView.backgroundColor = .white
        [theButton, theLabel].forEach(contentView.addSubview)

        // constrain button size to 120 x 40
        theButton.snp.makeConstraints { (make) in
            make.width.equalTo(120)
            make.height.equalTo(40)
        }

        // constrain button to trailing margin
        theButton.snp.makeConstraints { (make) in
            make.trailing.equalTo(contentView.layoutMarginsGuide)
        }

        // constrain button top to greaterThanOrEqualTo margin
        theButton.snp.makeConstraints { (make) in
            make.top.greaterThanOrEqualTo(contentView.layoutMarginsGuide)
        }

        // constrain button bottom to lessThanOrEqualTo margin
        theButton.snp.makeConstraints { (make) in
            make.bottom.lessThanOrEqualTo(contentView.layoutMarginsGuide)
        }

        // also constrain button to centerY
        theButton.snp.makeConstraints { (make) in
            make.centerY.equalTo(contentView.snp.centerY)
        }

        // constrain label to leading margin
        theLabel.snp.makeConstraints { (make) in
            make.leading.equalTo(contentView.layoutMarginsGuide)
        }

        // constrain label top to greaterThanOrEqualTo margin
        theLabel.snp.makeConstraints { (make) in
            make.top.greaterThanOrEqualTo(contentView.layoutMarginsGuide)
        }

        // constrain label bottom to lessThanOrEqualTo margin
        theLabel.snp.makeConstraints { (make) in
            make.bottom.lessThanOrEqualTo(contentView.layoutMarginsGuide)
        }

        // also constrain label to centerY
        theLabel.snp.makeConstraints { (make) in
            make.centerY.equalTo(contentView.snp.centerY)
        }

        // constrain label trailing to 8-pts from button leading
        theLabel.snp.makeConstraints { (make) in
            make.trailing.equalTo(theButton.snp.leading).offset(-8)
        }

    }

}

class SnapTableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.rowHeight = UITableViewAutomaticDimension
        tableView.estimatedRowHeight = 100

    }

    // MARK: - Table view data source
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "SnapCell", for: indexPath) as! SnapCell

        switch indexPath.row % 4 {

        case 0:
            cell.theLabel.text = "One line label."

        case 1:
            cell.theLabel.text = "This label has\nTwo Lines."

        case 2:
            cell.theLabel.text = "This label has enough text that is will wrap to Three Lines (on an iPhone 7)."

        default:
            cell.theLabel.text = "View balance with your pre-recorded Siri Command .View balance with your pre-recorded Siri Command  View balance with your pre-recorded Siri Command View balance with your pre-recorded Siri Command "

        }

        return cell
    }

}

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

person DonMag    schedule 17.10.2018
comment
Я обновил свой ответ примером кода и скриншотом - person Richard Topchii; 19.10.2018
comment
@RichardTopchiy - полный пример см. В моем отредактированном ответе. - person DonMag; 19.10.2018
comment
Демонстрационный проект, который вы представили, выглядит великолепно! Это именно то поведение, которое я хочу. Тем не менее, я использую UICollectionView, я тоже посмотрю, как он себя ведет... - person Richard Topchii; 19.10.2018
comment
Ваш пример работает хорошо, большое спасибо! К сожалению, проблема, похоже, заключается в том, как размер обрабатывается в моем приложении. - person Richard Topchii; 19.10.2018
comment
Интересно, я запустил ваш пример, а затем воссоздал ту же ячейку с UICollectionView и получил совершенно другой результат (с включенными ячейками с автоматическим размером): user-images.githubusercontent.com/8013017/ - person Richard Topchii; 22.10.2018
comment
Похоже, что в ваших ячейках отсутствуют ограничения/размеры ширины. Вам нужно будет опубликовать остальную часть кода, который устанавливает размеры элементов. - person DonMag; 22.10.2018
comment
абсолютно, мне просто интересно, как я мог установить требование сделать ячейки шириной UICollectionView. Игра с preferredMaxLayoutWidth метки помогла решить проблему, но, по сути, мне нужно угадать правильную ширину. - person Richard Topchii; 22.10.2018
comment
Я добавил базовый класс, который я использую для всех ячеек с фиксированной шириной. - person Richard Topchii; 22.10.2018
comment
Существует много дискуссий о том, как автоматически изменять размер ячеек представления коллекции. Многие из них работают только кое-как. Лично, если бы я выполнял представление набора из одного столбца (что, похоже, делаете и вы)... если только вы не используете некоторые другие функции представления набора, которые не доступны в табличных представлениях, я вместо этого будет использовать табличное представление. Кажется, намного проще получить макет, который вам нужен. - person DonMag; 22.10.2018
comment
Я абсолютно согласен, однако в этом случае невозможно использовать TableView. Рассматриваемый макет имеет карточный интерфейс, который возможен только с UICollectionView. Кроме того, я не использую AutoSizing в стиле Apple, я использую подход с определением размера ячейки, где я создаю специальную ячейку, используемую только для определения размера для каждого класса ячеек, и кэширую результаты. - person Richard Topchii; 22.10.2018
comment
хм... карточный интерфейс? Как это возможно только с просмотром коллекции? - person DonMag; 22.10.2018
comment
Карточки могут быть бесконечного размера (т. е. должны быть прокручиваемыми), а тень должна покрывать всю карточку. Я не смог добиться этого эффекта, используя только UITableView, так как не смог сделать тень единой. Между ячейками было некоторое расстояние, из-за чего тень выглядела странно. - person Richard Topchii; 22.10.2018
comment
Судя по всему, проблема в том, что я использую UICollectionView. Та же самая реализация отлично работает в UITableView. - person Richard Topchii; 22.10.2018
comment
Ну, мы довольно далеко за рамками этого вопроса. Однако, основываясь на изображениях, которые вы включили в свой исходный вопрос, я не вижу ничего, что нельзя было бы сделать с табличным представлением. - person DonMag; 22.10.2018
comment
Да, но это помогает сформулировать вопрос так, как я ДОЛЖЕН сделать это с UICollectionView. Конечно, я бы использовал UITableView, если бы мог, но у этого программного обеспечения есть множество требований и требований к дизайну;) - person Richard Topchii; 22.10.2018
comment
проблема была связана с неправильной конфигурацией ограничения ширины на моей стороне. - person Richard Topchii; 23.10.2018
comment
Используя ваш подход, то есть greaterThanOrEqualTo ограничения, я смог добиться желаемого результата. - person Richard Topchii; 23.10.2018

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

person vivekDas    schedule 17.10.2018