UILabel по центру UICollectionVIewCell с отступами по краям
Как разместить UILabel внутри UICollectionViewCell по центру с отступами по бокам, как на картинке ниже? 
Пытался делать так, но такой вариант не работает:
import UIKit
import SnapKit
class ViewController: UIViewController {
@IBOutlet weak var myCollectionView: UICollectionView!
var data: [String] = ["Здесь будет какой-то очень длинный текст", "Какой-нибудь текст", "Здесь будет какой-то очень длинный текст", "Ячейка среднего размера"]
override func viewDidLoad() {
super.viewDidLoad()
myCollectionView.delegate = self
myCollectionView.dataSource = self
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.myCollectionView.dequeueReusableCell(withReusableIdentifier: "cell", for: indexPath) as! CustomCollectionViewCell
cell.layer.borderWidth = 1
cell.layer.borderColor = UIColor.black.cgColor
cell.layer.cornerRadius = 5
cell.myLabel.text = self.data[indexPath.row]
cell.myLabel.snp.makeConstraints { make in
make.left.equalTo(cell.snp.left).offset(10)
make.right.equalTo(cell.snp.right).offset(10)
}
return cell
}
}
UPD: Добавил скриншот для наглядности того, что именно хочу получить в итоге.
Попробовал посчитать количество букв в слове / фразе, чтобы автоматически задавать разную ширину для разных ячеек UICollectionViewCell, но, допустим, если слово из 4-х букв, то отступы слева и справа будут небольшими, а если фраза состоит, например, из 20-ти букв, то отступы по краям получаются очень существенными. Вот код:
import UIKit
class ViewController: UIViewController {
var data: [String] = ["Все", "Одежда и обувь", "Товары для дома", "Электроника", "Мебель"] // Список тэгов
var width: Int = 0 // Ширина текущего тега
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.backgroundColor = .black
/**
* Получаем количество букв в текущем слове (или фразе)
*/
var len = self.data[indexPath.row].count
/**
* Умножаем количество букв на 25 поинтов (т.к. оптимальная ширина на одну букву составляет 25 поинтов)
*/
var width = ceil(len * 15)
let label = UILabel(frame: CGRect(x: 0, y: 0, width: Int(width), height: 40))
label.text = self.data[indexPath.row]
label.backgroundColor = UIColor.red
label.textColor = .black
label.textAlignment = .center
label.layoutMargins = UIEdgeInsets(top: 0, left: 80, bottom: 0, right: 10)
cell.contentView.addSubview(label)
return cell
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
/**
* Получаем количество букв в текущем слове (или фразе)
*/
var len = self.data[indexPath.row].count
/**
* Умножаем количество букв на 25 поинтов (т.к. оптимальная ширина на одну букву составляет 25 поинтов)
*/
var width = ceil(len * 15)
return CGSize(width: Int(width), height: 40)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
}
Что я упускаю из вида?
UPDATE: С помощью UIScrollView. Данный способ имелся ввиду?
import UIKit
class ViewController: UIViewController {
let list: [String] = ["Первый", "Второй элемент", "Какой-нибудь новый элемент"]
let stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = NSLayoutConstraint.Axis.horizontal
stackView.distribution = UIStackView.Distribution.equalSpacing
stackView.spacing = 16.0
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
let scrollView: UIScrollView = {
let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 80))
scrollView.backgroundColor = .red
scrollView.contentSize = CGSize(width: 1000, height: 200)
return scrollView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView)
DispatchQueue.main.async {
self.list.forEach( { $0
let label = self.createLabel(text: $0)
label.frame = CGRect(x: label.frame.origin.x, label.frame.origin.y, width: label.frame.size.width, height: 40)
self.stackView.addArrangedSubview(label)
})
}
scrollView.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 80),
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10),
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10)
])
}
private func createLabel(text: String) -> UILabel {
let label = UILabel()
label.font = UIFont(name: "Halvetica", size: 17)
label.numberOfLines = 1
label.text = text
label.sizeToFit()
label.backgroundColor = .white
label.frame = CGRect(x: 0, y: 0, width: label.frame.width, height: 40)
return label
}
}
Ответы (3 шт):
- Вы выровнили текст по горизонтали, но не по вертикали. Нужно добавить центрирование, то есть добавить недостающие контрейты для вертикали. Что-то вроде этого
make.centerY.equalTo(superview)
- установка контрейнтов для ячейки в cellForItemAt неправильный подход, они должны быть в ксибе/стироборде или там где ячейка создается.
class ViewController: UIViewController {
@IBOutlet weak var myCollectionView: UICollectionView!
var data: [String] = ["Здесь будет какой-то очень длинный текст", "Какой-нибудь текст", "Здесь будет какой-то очень длинный текст", "Ячейка среднего размера"]
override func viewDidLoad() {
super.viewDidLoad()
myCollectionView.delegate = self
myCollectionView.dataSource = self
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
myCollectionView.collectionViewLayout = layout
myCollectionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = myCollectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CustomCollectionViewCell
let text = data[indexPath.row]
cell.updateText(text: text)
return cell
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
// рассчитывает размер ячейки коллекции
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// расчитываем сколько места (ширины) занимает текст с заданным шрифтом
let text = data[indexPath.row]
let size = CGSize(width: .greatestFiniteMagnitude, height: 100.0) // высоту берем как высота колекции
let font = UIFont.systemFont(ofSize: 20) // нужно брать фонт лейбла в ячейке
let rect = (text as NSString).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
let result = CGSize(width: rect.size.width, height: 100)
return result
}
}
class CustomCollectionViewCell: UICollectionViewCell {
private var label: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
contentView.layer.borderWidth = 1
contentView.layer.borderColor = UIColor.black.cgColor
contentView.layer.cornerRadius = 5
contentView.layer.masksToBounds = true
contentView.backgroundColor = .yellow
label = UILabel()
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(label)
label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func updateText(text: String) {
label.text = text
}
}
Более продвинутое и как мне кажется менее геморойное решение (iOS 13+)
private func createLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(
widthDimension: .estimated(100.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .estimated(100.0),
heightDimension: .absolute(44))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .continuous
section.interGroupSpacing = 20
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
Надо может поиграться с параметрами айтема и группы, но в целом подход такой что мы ставим расчет ширины автоматически (.estimated), тут еще важно чтобы констрейнты в ячейке были верно расставлены.
И в viewDidLoad присваиваем новый лайаут.
...
myCollectionView.collectionViewLayout = createLayout()

