Reputation: 547
I'm trying to create a horizontal collection view inside a tableviewcell, with a dynamic size uiimageview inside, which will be the mandatory view.
I created a UIView :
public class CardImage: UIView {
private var imageView: UIImageView?
private var titleLabel: UILabel?
private var descriptionLabel: UILabel?
private var subtitleLabel: UILabel?
private var icon: UIImage?
private var imageURL: String?
private var screenPercentage: CGFloat = 1.0
override public func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = 4
}
public override func awakeFromNib() {
super.awakeFromNib()
setup()
layoutSubviews()
}
public init() {
super.init(frame: .zero)
setup()
layoutSubviews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
layoutSubviews()
}
public func fill(dto: SomeDTO) {
setupImage(icon: dto.image, imageUrl: dto.imageURL)
descriptionLabel?.text = dto.description
titleLabel?.text = dto.title
subtitleLabel?.text = dto.subtitle
screenPercentage = dto.screenPercentage
}
private func setup() {
isUserInteractionEnabled = true
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .red
titleLabel = UILabel()
titleLabel?.textColor = .white
titleLabel?.numberOfLines = 1
addSubview(titleLabel!)
descriptionLabel = UILabel()
descriptionLabel?.textColor = .white
descriptionLabel?.numberOfLines = 2
addSubview(descriptionLabel!)
subtitleLabel = UILabel()
subtitleLabel?.textColor = .white
subtitleLabel?.numberOfLines = 1
addSubview(subtitleLabel!)
imageView = UIImageView(frame: .zero)
imageView?.backgroundColor = .red
addSubview(imageView!)
setupConstraints()
}
private func setupImage(icon: UIImage?, imageUrl: String?) {
if let url = imageURL {
return
}
guard let image = icon else {
return
}
imageView?.image = image
imageView?.contentMode = .scaleAspectFit
setNeedsDisplay()
setNeedsLayout()
}
private func setupConstraints() {
imageView?.translatesAutoresizingMaskIntoConstraints = false
imageView?.topAnchor.constraint(equalTo: topAnchor).isActive = true
imageView?.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
imageView?.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
let screenSize = UIScreen.main.bounds
let computedWidth = screenSize.width * screenPercentage
imageView?.widthAnchor.constraint(equalToConstant: computedWidth).isActive = true
//the image should be 16:9
imageView?.heightAnchor.constraint(equalTo: widthAnchor, multiplier: 9.0/16.0).isActive = true
titleLabel?.translatesAutoresizingMaskIntoConstraints = false
titleLabel?.topAnchor.constraint(equalTo: imageView!.bottomAnchor, constant: 16).isActive = true
titleLabel?.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16).isActive = true
titleLabel?.heightAnchor.constraint(equalToConstant: 18).isActive = true
subtitleLabel?.translatesAutoresizingMaskIntoConstraints = false
subtitleLabel?.topAnchor.constraint(equalTo: titleLabel!.topAnchor).isActive = true
subtitleLabel?.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16).isActive = true
titleLabel?.widthAnchor.constraint(equalTo: subtitleLabel!.widthAnchor).isActive = true
titleLabel?.trailingAnchor.constraint(equalTo: subtitleLabel!.leadingAnchor, constant: 6).isActive = true
descriptionLabel?.translatesAutoresizingMaskIntoConstraints = false
descriptionLabel?.topAnchor.constraint(equalTo: titleLabel!.bottomAnchor, constant: 16).isActive = true
descriptionLabel?.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16).isActive = true
descriptionLabel?.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16).isActive = true
descriptionLabel?.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16).isActive = true
}
}
Which will be inside a CollectionViewCell:
public class CardImageCollectionViewCell: UICollectionViewCell {
private let view: CardImage = CardImage()
override public func awakeFromNib() {
super.awakeFromNib()
translatesAutoresizingMaskIntoConstraints = false
//this only pin the view to the four anchors of the uicollectionview
view.pinToBounds(of: self.contentView)
backgroundColor = .clear
AccessibilityManager.setup(self, log: true)
}
public func fill(dto: SomeDTO) {
view.fill(dto: dto)
}
}
And then the CollectionViewCell, inside a tableviewcell, which has a collectionview:
public class CardImageCollectionView: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
@IBOutlet private weak var collectionView: UICollectionView!
private var dto: [SomeDTO] = []
public override func awakeFromNib() {
super.awakeFromNib()
setup()
}
private func setup() {
backgroundColor = .blue
collectionView.backgroundColor = .clear
collectionView.delegate = self
collectionView.dataSource = self
if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
}
registerCells()
}
func fill(dto: [SomaImageCardDTO]) {
self.dto = dto
DispatchQueue.main.async {
self.collectionView.reloadData()
self.collectionView.layoutIfNeeded()
}
}
private func registerCells() {
collectionView.register(UINib(nibName: String(describing: CardImageCollectionViewCell.self), bundle: nil), forCellWithReuseIdentifier: String(describing: CardImageCollectionViewCell.self))
}
public func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dto.count
}
//this should not be setted since the sizing is automatic
// public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// return CGSize(width: 304, height: 238)
// }
//
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: CardImageCollectionViewCell.self), for: indexPath) as! CardImageCollectionViewCell
cell.fill(dto: dto[indexPath.row])
return cell
}
}
The two problems I'm facing is, the image cannot assume any size, since the collection view doesn't have any size until it's filled with some info.
And then, even if I set the image size, I cannot pass the info to the UITableViewcell size.
Upvotes: 4
Views: 332
Reputation: 887
If I understand your problem correctly, you have more than one rows in an UITableView which have horizontal UICollectionViews. Those collection views have cells which have dynamic size based on the images inside them.
So the width of the UITableView is fixed, but the height for the row depends on the height of the UICollectionView, correct?
I would recommend using self-sizing cells in the UITableView. See the reference here: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSelf-SizingTableViewCells.html
After that, you need to find a way to calculate the height of the UICollectionView correctly, so the UITableView cell can determine the correct height. There are several ways to do it, for example by overriding the intrinsicContentSize
property of the UICollectionView and returning the largest height of the images.
Upvotes: 3