Why doesn't go CollectionViewCell? - ios

I noticed that Topics CollectionViewCell was not gone when I debugged. Why is that?
the cells are all correct but the collectionview is not available.
The link of the project is below. you can also look from there.
here I define cell.
extension ArticlesVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return models.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CollectionTableViewCell.identifier, for: indexPath) as! CollectionTableViewCell
cell.configure(with: models)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 200
}
}
struct Model {
let text: String
let imageName: String
init(text: String, imageName: String) {
self.text = text
self.imageName = imageName
}
}
Here I define CollectionTableViewCell
class CollectionTableViewCell: UITableViewCell {
static let identifier = "CollectionTableViewCell"
var collectionView: UICollectionView?
var models = [Model]()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
collectionView?.register(TopicsCollectionViewCell.self, forCellWithReuseIdentifier: TopicsCollectionViewCell.identifier)
collectionView?.delegate = self
collectionView?.dataSource = self
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
contentView.addSubview(collectionView!)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
collectionView?.frame = contentView.bounds
}
func configure(with models: [Model]) {
self.models = models
collectionView?.reloadData()
}
}
extension CollectionTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return models.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TopicsCollectionViewCell.identifier, for: indexPath) as! TopicsCollectionViewCell
cell.configure(with: models[indexPath.row])
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 250, height: 250)
}
}
**This TopicsCollectionViewCell **
class TopicsCollectionViewCell: UICollectionViewCell {
static let identifier = "TopicsCollectionViewCell"
let titleLabel: UILabel = {
let label = UILabel()
label.text = "label"
return label
}()
let photoImage: UIImageView = {
let imageView = UIImageView()
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(titleLabel)
contentView.addSubview(photoImage)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
photoImage.frame = CGRect(x: 3,
y: 3,
width: contentView.width - 6,
height: 150)
titleLabel.frame = CGRect(x: 3,
y: photoImage.bottom + 5,
width: contentView.width - 6,
height: contentView.height - photoImage.height - 6)
}
public func configure(with model: Model) {
print(model)
self.titleLabel.text = model.text
self.photoImage.image = UIImage(named: model.imageName)
}
}
This link is project with github

During execution those lines are useless as collectionView? is nil
collectionView?.register(TopicsCollectionViewCell.self, forCellWithReuseIdentifier: TopicsCollectionViewCell.identifier)
collectionView?.delegate = self
collectionView?.dataSource = self
You need to reorder the code by initing the collectionView first
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
contentView.addSubview(collectionView!)
collectionView?.register(TopicsCollectionViewCell.self, forCellWithReuseIdentifier: TopicsCollectionViewCell.identifier)
collectionView?.delegate = self
collectionView?.dataSource = self

Related

How to add a collectionView inside a uiTableViewCell

I'm trying to add a uicollectionView with 5 cells inside a uitableView cell. I'm running into issues where the collection view is not displaying at all. Here's my code:
// TableViewController
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "book", for: indexPath) as! BookTableViewCell
cell.cellIndex = indexPath
cell.dataSource = self
cell.delegate = self
cell.backgroundColor = UIColor.white
// if let books = all_books[indexPath.section], books.count > 0 {
// cell.collectionView.reloadData()
// }
return cell
}
// BookTableViewCell
import UIKit
import Firebase
import FirebaseDatabase
import SDWebImage
class BookTableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource {
var delegate: BookTableViewCellDelegate?
var dataSource: BookTableViewCellDelegate?
let leftAndRightPaddings: CGFloat = 10.0
let numberOfItemsPerRow: CGFloat = 5
let screenSize: CGRect = UIScreen.main.bounds
var collectionView: UICollectionView!
var tableRowData: Book!
var books : [Book]?
var ref: DatabaseReference!
var cache:NSCache<AnyObject, AnyObject>!
var cellIndex: IndexPath!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
flowLayout.itemSize = CGSize(width: 100, height: self.frame.height)
//You can also provide estimated Height and Width
flowLayout.estimatedItemSize = CGSize(width: 100, height: self.frame.height)
//For Setting the Spacing between cells
flowLayout.minimumInteritemSpacing = 25
flowLayout.minimumLineSpacing = 20
collectionView = UICollectionView(frame: self.bounds, collectionViewLayout: flowLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "collectionCell")
collectionView.showsHorizontalScrollIndicator = false
collectionView.backgroundColor = UIColor.clear
self.addSubview(collectionView)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath as IndexPath) as! BookCollectionViewCell
self.dataSource?.getData(cell: self)
let books = self.books
let book = books?[indexPath.row]
if book != nil {
cell.imageView.sd_setImage(with: URL(string: book?.image as! String), placeholderImage: nil)
cell.label.text = book?.title
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: 100, height: self.frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
{
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.dataSource?.getData(cell: self)
let books = self.books
let book = books?[indexPath.row]
if let book = book {
self.delegate?.didSelectCollection(book: book)
}
}
}
protocol BookTableViewCellDelegate {
func getData(cell: BookTableViewCell)
func didSelectCollection(book: Book)
}
Can someone please help? I am initializing the collection view in the init function. Setting up all the delegates correctly. Not sure what I'm doing wrong.
Problem is
collectionView = UICollectionView(frame: self.bounds, collectionViewLayout: flowLayout)
you need to set the frame at correct time not inside init here
override func layoutSubviews() {
super.layoutSubviews()
collectionView.frame = self.bounds
}
or better to use constraints inside init , also add it to
self.contentView.addSubview(collectionview)

MyUICollectionViewCell is not visible

import UIKit
private let reuseIdentifier = "NewCell"
class SeondViewController: UIViewController, UICollectionViewDelegateFlowLayout {
#IBOutlet var myNavBar: UINavigationBar!
var frutta: [String]!
override func viewDidLoad() {
super.viewDidLoad()
let margins = self.view.layoutMarginsGuide
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 30, left: 10, bottom: 10, right: 10)
layout.scrollDirection = .vertical
let newCollectionView: UICollectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
newCollectionView.dataSource = self
newCollectionView.delegate = self
newCollectionView.register(SecondTabCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
newCollectionView.reloadData()
newCollectionView.backgroundColor = UIColor.clear
newCollectionView.isHidden = false
newCollectionView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(newCollectionView)
frutta = ["Mele", "Olive", "Pere", "Noci", "Banane", "Kiwi", "Ananas"]
myNavBar.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
myNavBar.topAnchor.constraint(equalTo: margins.topAnchor).isActive = true
myNavBar.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
newCollectionView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
newCollectionView.topAnchor.constraint(equalTo: myNavBar.bottomAnchor).isActive = true
newCollectionView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
◦ newCollectionView.bottomAnchor.constraint(equalTo: margins.bottomAnchor).isActive = true
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.bounds.size.width / 2, height: collectionView.bounds.size.height / 3)
}
}
extension SecondViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return frutta.count
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! SecondTabCollectionViewCell
myCell.backgroundColor = UIColor.clear
myCell.secondCellLabel = UILabel()
myCell.secondCellLabel.frame.size = CGSize(width: myCell.bounds.width / 2, height: myCell.secondCellLabel.bounds.height / 2)
myCell.secondCellLabel.adjustsFontSizeToFitWidth = true
myCell.secondCellLabel.backgroundColor = UIColor.clear
myCell.secondCellLabel.textColor = UIColor.black
myCell.secondCellLabel.text = frutta[indexPath.item]
myCell.translatesAutoresizingMaskIntoConstraints = false
myCell.contentView.addSubview(myCell.secondCellLabel)
return myCell
}
}
extension SecondViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Hai premuto \(indexPath.row).")
}
}
import UIKit
class SecondTabCollectionViewCell: UICollectionViewCell {
var secondCellLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) non e stato implementato.")
}
}
myCell.secondCellLabel.frame.size = CGSize(width: myCell.bounds.width / 2, height: myCell.secondCellLabel.bounds.height / 2)
The above line is wrong in cellForItemAt, you put the height of label is myCell.secondCellLabel.bounds.height instead of myCell.bounds.height.
correct one is
myCell.secondCellLabel.frame.size = CGSize(width: myCell.bounds.width / 2, height: myCell.bounds.height / 2)
It is a little strange to be laying out a cell's internal views in cellForRowAtIndexPath, this should be done in the cell class itself. The basic problem is that you your UILabel is appearing in the top left corner of the cell and has a height of zero, so you cannot see it. I moved the code you had in cellForRowAtIndexPath into the cell itself.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! SecondTabCollectionViewCell
myCell.backgroundColor = UIColor.clear
myCell.secondCellLabel.text = frutta[indexPath.item]
return myCell
}
import UIKit
class SecondTabCollectionViewCell: UICollectionViewCell {
var secondCellLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .clear
label.textColor = .black
label.adjustsFontSizeToFitWidth = true
label.textAlignment = .center
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureViews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func configureViews() {
addSubview(secondCellLabel)
}
override func layoutSubviews() {
super.layoutSubviews()
configureLayout()
}
private func configureLayout() {
let width = bounds.width / 2
let height = frame.height / 2
let size = CGSize(width: width, height: height)
let origin = CGPoint(x: width/2, y: height/2)
secondCellLabel.frame = CGRect(origin: origin, size: size)
}
}

How to properly add an UICollectionView inside UITableViewCell using IOS 12

For some reasons I cannot properly display some items from a collection inside an tableview cell when using Xcode 10 beta. I tried all I know for the last 4 days.
I made a small project sample to see what my issue is.
Full code is here if someone wants to run it locally: https://github.com/adrianstanciu24/CollectionViewInsideUITableViewCell
I first add an table view with 2 different resizable cells, first cell has the collection view and a label:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 44
tableView.rowHeight = UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "collCell") as! CollectionTableViewCell
cell.collectionView.collectionViewLayout.invalidateLayout()
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "normalCell")!
return cell
}
}
}
Here is the cell with collection view defined. This also has a height constraint which I updated in layoutSubviews:
class CollectionTableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint!
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
}
}
override func layoutSubviews() {
super.layoutSubviews()
collectionViewHeightConstraint.constant = collectionView.collectionViewLayout.collectionViewContentSize.height
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "taskCell", for: indexPath as IndexPath) as! TaskCollectionViewCell
cell.name.text = "Task_" + String(indexPath.row)
return cell
}
}
Below is a screenshot with the collection view constraint and how the storyboard looks:
And this is how it looks when running:
The cells are squashed and label on top disappears. What I want is the collection view should grow to show all elements and also making table view cell to resize, but that is not happening at the moment and I can't figure out where the issue is.
If you want the following output:
Code
ViewController:
class ViewController: UIViewController {
let list = [String](repeating: "Label", count: 10)
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
tableView.reloadData()
}
public lazy var tableView: UITableView = { [unowned self] in
let v = UITableView.init(frame: .zero)
v.delegate = self
v.dataSource = self
v.estimatedRowHeight = 44
v.rowHeight = UITableViewAutomaticDimension
v.register(TableCell.self, forCellReuseIdentifier: "TableCell")
return v
}()
}
extension ViewController: UITableViewDelegate{
}
extension ViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableCell", for: indexPath) as! TableCell
cell.label.text = "\(list[indexPath.row]) \(indexPath.row)"
return cell
}
}
TableCell:
class TableCell: UITableViewCell{
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
func commonInit(){
contentView.addSubview(label)
contentView.addSubview(collectionView)
updateConstraints()
}
override func updateConstraints() {
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: topAnchor),
label.leadingAnchor.constraint(equalTo: leadingAnchor),
label.trailingAnchor.constraint(equalTo: trailingAnchor)
])
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: label.bottomAnchor),
collectionView.leadingAnchor.constraint(equalTo: leadingAnchor),
collectionView.bottomAnchor.constraint(equalTo: bottomAnchor),
collectionView.trailingAnchor.constraint(equalTo: trailingAnchor),
])
super.updateConstraints()
}
let list = [String](repeating: "Task_", count: 10)
public let label: UILabel = {
let v = UILabel()
v.textAlignment = .center
return v
}()
public lazy var collectionView: UICollectionView = { [unowned self] in
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
let v = UICollectionView(frame: .zero, collectionViewLayout: layout)
v.register(CollectionCell.self, forCellWithReuseIdentifier: "CollectionCell")
v.delegate = self
v.dataSource = self
v.isScrollEnabled = false
return v
}()
override func sizeThatFits(_ size: CGSize) -> CGSize {
let collectionCellHeight = 50
let rows = list.count / 5 // 5: items per row
let labelHeight = 20 // you can calculate String height
let height = CGFloat((collectionCellHeight * rows) + labelHeight)
let width = contentView.frame.size.width
return CGSize(width: width, height: height)
}
}
extension TableCell: UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return list.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CollectionCell
cell.label.text = "\(list[indexPath.item])\(indexPath.item)"
return cell
}
}
extension TableCell: UICollectionViewDelegate{
}
extension TableCell: UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width/5, height: 50)
}
}
CollectionCell
class CollectionCell: UICollectionViewCell{
let padding: CGFloat = 5
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit(){
backgroundColor = .yellow
contentView.addSubview(label)
updateConstraints()
}
override func updateConstraints() {
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: topAnchor, constant: padding),
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding),
label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -padding),
label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -padding)
])
super.updateConstraints()
}
public let label: UILabel = {
let v = UILabel()
v.textColor = .darkText
v.minimumScaleFactor = 0.5
v.numberOfLines = 1
return v
}()
}
Made with Swift 5.3 and tested on iOS 14, the follow it should work just copy and paste 😉:
TableViewController.swift
import UIKit
import SnapKit
class TableViewController: UIViewController {
private let tableView: UITableView = UITableView()
private let tableViewCellId = "TableViewCell"
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
}
// MARK: - Setup UIs
extension TableViewController {
private func setupUI() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(TableViewCell.self, forCellReuseIdentifier: tableViewCellId)
view.addSubview(tableView)
tableView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
}
// MARK: - Delegate & DataSource
extension TableViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCellId) as! TableViewCell
cell.backgroundColor = .white
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}
TableViewCell.swift
import UIKit
import SnapKit
class TableViewCell: UITableViewCell {
private let list = [String](repeating: "Row ", count: 10)
private let collectionViewLayout = UICollectionViewFlowLayout()
lazy private var collectionView: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
override func layoutSubviews() {
setupUI()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
// MARK: - UI Setup
extension TableViewCell {
private func setupUI() {
// layout.minimumLineSpacing = 5
// layout.minimumInteritemSpacing = 5
collectionViewLayout.scrollDirection = .horizontal
collectionView.backgroundColor = .clear
collectionView.showsHorizontalScrollIndicator = false
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionCell")
collectionView.delegate = self
collectionView.dataSource = self
addSubview(collectionView)
collectionView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
}
extension TableViewCell: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return list.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CollectionViewCell
cell.titleLabel.text = "\(list[indexPath.item])\(indexPath.item)"
return cell
}
}
extension TableViewCell: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width / 5, height: 100)
}
}
CollectionViewCell.swift
import UIKit
class CollectionViewCell: UICollectionViewCell {
var titleLabel: UILabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
setupUI()
}
}
// MARK: - UI Setup
extension CollectionViewCell {
private func setupUI() {
titleLabel.font = UIFont(name: "HelveticaNeue", size: 20)
titleLabel.textColor = .white
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.backgroundColor = .systemGreen
titleLabel.textAlignment = .center
addSubview(titleLabel)
titleLabel.snp.makeConstraints {
$0.centerX.centerY.equalToSuperview()
$0.height.equalTo(60)
$0.width.equalTo(150)
}
}
}
If your desired output is following
Then replace your code of collectionview with this
class CollectionTableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var collectionViewHeightConstraint: NSLayoutConstraint!
var arrForString:[String] = ["Task_0","Task_1","Task_3","Task_4","Task_5","Task_6","Task_7","Task_8","Task_9","Task_10"]
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrForString.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "taskCell", for: indexPath as IndexPath) as! TaskCollectionViewCell
cell.name.text = arrForString[indexPath.row]
cell.layoutIfNeeded()
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
//we are just measuring height so we add a padding constant to give the label some room to breathe!
var padding: CGFloat = 10.0
var height = 10.0
height = Double(estimateFrameForText(text: arrForString[indexPath.row]).height + padding)
return CGSize(width: 50, height: height)
}
private func estimateFrameForText(text: String) -> CGRect {
//we make the height arbitrarily large so we don't undershoot height in calculation
let height: CGFloat = 120
let size = CGSize(width: 50, height: height)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18, weight: UIFont.Weight.light)]
return NSString(string: text).boundingRect(with: size, options: options, attributes: attributes, context: nil)
}
}
feel free to ask.

Change Color and text of items in cell in collectionView

I want change color of circleView and text in label in cell in CollectionView using function.
Class of my Cell:
class CustomCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
var timerLabel:UILabel = {
let label = UILabel()
label.text = "5"
label.frame = CGRect(x: 200, y: 10, width: 60, height: 60)
label.backgroundColor = .white
label.textAlignment = NSTextAlignment.center
return label
}()
var circleView: UIView = {
let circleView = UIView()
circleView.frame = CGRect(x: 100, y: 10, width: 60, height: 60)
circleView.backgroundColor = .red
circleView.layer.cornerRadius = 30
circleView.layer.masksToBounds = true
return circleView
}()
func setupViews() {
backgroundColor = .white
addSubview(timerLabel)
addSubview(circleView)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupViews()
}
}
Create CollectionView in VC:
func collectionViewSet() {
collectionView?.frame.size.height = (view.frame.height / 100) * 70
collectionView?.backgroundColor = .white
collectionView?.register(CustomCell.self, forCellWithReuseIdentifier: cellId)
}
//-------------- (CollectionView Configure) -------------------
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:view.frame.width ,height: collectionView.frame.height / 6)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId , for: indexPath)
customCell.tag = indexPath.row + 1
return cell
}
And in my created func for example setColor() i want generate ranodm color (by UIColor) for CircleView and generate random number for TimerLable but only in one cell. For next cell I want generate another color and number. Is there any way to do that, i was trying use indexPath.row and func didselectedItemForindexPath but it didn't works at all. Maybe I did something wrong.
Ok I have found an answer. I post my code if someone will have similar problem :)
for x in 0...4{
let indexPath = IndexPath(row: x, section: 0)
guard let cell = collectionView?.cellForItem(at: indexPath) as? CustomCell else {
return
}
cell.circleView.backgroundColor = .blue
}
There are many, many examples of generating random colors and numbers out there - a quick search and you'll have plenty to choose from.
Once you've picked a couple, change your cellForItemAt function to:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId , for: indexPath)
customCell.tag = indexPath.row + 1
let randColor = myRandomColorFunc()
let randNumber = myRandomNumberFunc()
customCell.circleView.backgroundColor = randColor
customCell.timerLabel.text = "\(randNumber)"
return cell
}

Swift - set height for UICollectionViewCell inside UITableViewCell

I have created UITableView with multiple sections. Each section has one line (UITableViewCell) and inside this line, I have UICollectionView and inside it UICollectionViewCell. Scrolling is working correctly. However, when I change the table row height, UICollectionView height remain unchaged. How to fix this?
class ViewController: UIViewController {
var tab: HorizontalSelector? = nil
#IBOutlet weak var tabView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
tab = HorizontalSelector(frame: self.tabView.frame)
tab?.addSection("Jedna", withHeight: 100)
tab?.addSection("Middle", withHeight: 100)
tab?.addSection("Dva", withHeight: 100)
self.tabView.addSubview(tab!)
}
}
Class for horizontal selector
class HorizontalSelector: UITableView, UITableViewDataSource, UITableViewDelegate {
let CELL_REUSE_ID = "cellSectionRow"
let DEFAULT_ROW_HEIGHT: CGFloat = 44
var sectionHeights: [CGFloat] = []
var sections: [String] = []
init(frame: CGRect){
var f: CGRect = frame
f.origin.x = 0
f.origin.y = 0
super.init(frame: f, style: UITableViewStyle.plain)
self.initialize()
}
override init(frame: CGRect, style: UITableViewStyle) {
super.init(frame: frame, style: style)
self.initialize()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func initialize(){
self.register(SectionCell.self, forCellReuseIdentifier: CELL_REUSE_ID)
self.dataSource = self
self.delegate = self
self.backgroundColor = UIColor.blue
}
func addSection(_ section: String){
sections.append(section)
sectionHeights.append(DEFAULT_ROW_HEIGHT)
}
func addSection(_ section: String, withHeight: CGFloat){
sections.append(section)
sectionHeights.append(withHeight)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return sectionHeights[indexPath.section]
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let sectionCell = tableView.dequeueReusableCell(withIdentifier: CELL_REUSE_ID) as! SectionCell
sectionCell.backgroundColor = UIColor.brown
return sectionCell
}
}
Class for single table section
class SectionCell: UITableViewCell {
required init?(coder aDecoder: NSCoder){
fatalError("init(coder:) has not been implemented")
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.initialize()
}
override func awakeFromNib() {
super.awakeFromNib()
self.initialize()
}
func initialize(){
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
self.contentView.addSubview(SectionCellRowCollection(frame: self.contentView.bounds, collectionViewLayout: layout))
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Class for collection within single table section
class SectionCellRowCollection: UICollectionView, UICollectionViewDataSource, UICollectionViewDelegate {
private let COLLECTION_CELL_ID = "collectionCell"
required init?(coder aDecoder: NSCoder){
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout){
super.init(frame: frame, collectionViewLayout: layout)
self.initialize()
}
private func initialize(){
self.register(SectionCellRowCollectionCell.self, forCellWithReuseIdentifier: COLLECTION_CELL_ID)
self.dataSource = self
self.delegate = self
self.backgroundColor = UIColor.yellow
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: COLLECTION_CELL_ID, for: indexPath as IndexPath) as! SectionCellRowCollectionCell
cell.backgroundColor = UIColor.gray
return cell
}
}
Class for single cell inside colection
class SectionCellRowCollectionCell: UICollectionViewCell{
var imageView: UIImageView!
required init?(coder aDecoder: NSCoder){
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height*2/3))
imageView.contentMode = UIViewContentMode.scaleAspectFit
imageView.image = UIImage(named: "Swift-cover")
contentView.addSubview(imageView)
self.backgroundColor = UIColor.green
}
}
Setup constraints for your collectionView and it will change its frame base on these constraints:
class SectionCell: UITableViewCell {
private var collectionView: SectionCellRowCollection!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
initialize()
}
override func awakeFromNib() {
super.awakeFromNib()
initialize()
}
private func initialize() {
translatesAutoresizingMaskIntoConstraints = false
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
collectionView = SectionCellRowCollection(frame: contentView.bounds, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(collectionView)
// constraints
collectionView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
collectionView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
collectionView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
collectionView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
}
}
You have collectionView inside the tableviewCell so there is some static height of collectionView in storyboard so whatever the height of tableview the collectionView takes its height as its not clip to bounds.So to resolve that problem you must give height of tableView
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 100.0;//Choose your custom row height
}
Then set the collectionView cell height that must be cell or equal to tableview height
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSize(width: screenWidth, height:90 );
}

Resources