Collectionview in collectionview cell fitting to content - swift

I have a collectionview with two sections, each sections have one item. The cell in section contains collectionview and I need to fit height cell to content collectionview.
My first section has a fixed height to 120 because it's horizontal collectionview and height not changed. But for second section number of elements can be changed and I need to display all items (collectionview have scrollEnabled to false and scrolldirecton to vertical).
My problem is I need to display a collectionview with two scrolldirection depends sections and display all items for vertical collectionview.
When I do collectionview.collectionViewLayout.collectionViewContentSize.height I have the good height but I don't know how passed this height to parent viewcontroller. I search a dynamic solution to fixed this.
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
// MARK: - UICollectionViewDataSource
func numberOfSections(in collectionView: UICollectionView) -> Int {
return presenter.numberOfSections // == 2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return presenter.numberOfItemsInSection // == 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueCell(InlineDisplayCollectionViewCell.self, for: indexPath)
configureCell(cell, at: indexPath)
return cell
}
// MARK: - UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let type = presenter.typeAtIndexPath(indexPath)
switch type {
case .city:
return CGSize(width: collectionView.frame.width, height: 120)
case .suggestion:
return CGSize(width: collectionView.frame.width, height: 820)
default:
return CGSize.zero
}
}
}
class InlineDisplayCollectionViewCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView: UICollectionView!
var itemType: ItemModelCollectionType! = .city {
didSet {
updateCell()
}
}
var cities: [City]? {
didSet {
collectionView.reloadData()
}
}
var suggestions: [Suggestion]? {
didSet {
collectionView.reloadData()
}
}
override func awakeFromNib() {
super.awakeFromNib()
configureView()
}
// MARK: - UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemType == .city ? cities?.count ?? 0 : suggestions?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: UICollectionViewCell
if itemType == .city {
let cityCell = collectionView.dequeueCell(CityCollectionViewCell.self, for: indexPath)
configure(cell: cityCell, at: indexPath)
cell = cityCell
} else {
let suggestionCell = collectionView.dequeueCell(SquareCollectionViewCell.self, for: indexPath)
configure(cell: suggestionCell, at: indexPath)
cell = suggestionCell
}
return cell
}
// MARK: - UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return itemType == .city ? 10 : 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return itemType == .city ? 0 : 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let size: CGSize
if itemType == .city {
size = CGSize(width: 100, height: 120)
} else {
let width = (collectionView.frame.width - (collectionView.contentInset.left + collectionView.contentInset.right) - 10) * 0.5
let height = width
size = CGSize(width: width, height: height)
}
return size
}
// MARK: - Private functions
private func configureView() {
collectionView.delegate = self
collectionView.dataSource = self
collectionView.contentInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
collectionView.registerNib(CityCollectionViewCell.self, bundle: nil)
collectionView.registerNib(SquareCollectionViewCell.self, bundle: nil)
}
private func updateCell() {
if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.scrollDirection = itemType == .city ? .horizontal : .vertical
}
collectionView.isScrollEnabled = itemType == .city
}
private func configure(cell: CityCollectionViewCell, at indexPath: IndexPath) {
guard let city = cities?[indexPath.row] else { return }
cell.configure(with: city)
}
private func configure(cell: SquareCollectionViewCell, at indexPath: IndexPath) {
guard let suggestion = suggestions?[indexPath.row] else { return }
cell.configure(with: suggestion)
}
}

You can't do that. CollectionView cells are dequeued and reused dynamically. The best way to go about it is to use different types of cells within the same CollectionView.
Please check the questions I have posted on this topic: Get the sum of all collectionViewCells' heights inside a CollectionView and Multiple Cells for Comments and Reply in a CollectionView

Unfortunately you can't do something like that. But you can have different types of cells inside your CV. Also you can modify that in the function called "cellForItemAt".

Related

Why is my uiCollection view cell not changing based on different screen size? I'm using storyboards and Swift

just as to make it clear all other similar questions are asking for objective C or either not in storyboard hence my question is valid..
So I want to do is create a full screen image slider. Im using collection view to do it. but the outcome is not as expected especially the scrolling and the way the cell is displayed.
here is my code for collection view:
import UIKit
import Firebase
import FirebaseDatabase
import SDWebImage
class DoceFullImageVIew: UIViewController {
#IBOutlet weak var colltionView: UICollectionView!
var images = [ChudoInsta]()
// var imgArr = [ChudoInsta]()
var dbRef: DatabaseReference!
let viewImageSegueIdentifier = "doceImageSegueIdentifier"
override func viewDidLoad() {
super.viewDidLoad()
dbRef = Database.database().reference().child("wedding/doce_pics")
loadDB()
}
func loadDB(){
dbRef.observe(DataEventType.value, with: { (snapshot) in
var newImages = [ChudoInsta]()
for chudoInstaSnapshot in snapshot.children {
let chudoInstaObject = ChudoInsta(snapshot: chudoInstaSnapshot as! DataSnapshot)
newImages.append(chudoInstaObject)
}
self.images = newImages
self.colltionView.reloadData()
})
}
func printvalue() {
print("This is the doce full image firebase \(images)")
// print("This is the doce full image firebase \(images)")
}
}
extension DoceFullImageVIew: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? DataCollectionViewCell
let image = images[indexPath.row]
cell?.img.sd_setImage(with: URL(string: image.imageUrl), placeholderImage: UIImage(named: "image1"))
return cell!
}
}
extension DoceFullImageVIew: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let size = UIScreen.main.bounds
return CGSize(width: size.width, height: size.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
//return UIEdgeInsets(0, 5, 0, 5);
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
Can some one point out what is wrong and why the output comes like this images chopped :
https://drive.google.com/file/d/1JzbX-B0dhuInJ_ydwFWyuYTNq6d_lZS_/view?usp=sharing
https://drive.google.com/file/d/1KdPKkcvdAR4lMEY-0nwthsFlS2OxC_Te/view?usp=sharing
and in landscape the images are chopped as well:
https://drive.google.com/file/d/1RgqUCtUL_L2Xystg1eDDm7ZMBBhqaZvj/view?usp=sharing
here are some of my story board screenshots:
https://drive.google.com/file/d/1NMzvSHfEHWil_HsMtELmU4kd2Awe4nXt/view?usp=sharing
https://drive.google.com/file/d/1Q6DiBWWXGCzZ6bVKn9dkprMeluXuuonW/view?usp=sharing
https://drive.google.com/file/d/1-CB_XtGSI_rXpqbckytGgNrdaH1upaUy/view?usp=sharing
https://drive.google.com/file/d/1r4gAV2p_VS4Ix5ht_HPQVe7Rs8LeKtpX/view?usp=sharing
my flow delegate:
https://drive.google.com/file/d/1BjIBwvTVe0Ew_MAKBpeorkowQlDdD3eT/view?usp=sharing
Im trying to achieve a fullscreen image slider with collection view(images are retrieved from firebase)

How to center the collectionview cells of 1 row in the center, What am i doing worng

I have in my StoryBoard a Collection view with leading and trailing to superview 20 from each side.
Inside the collection view i want to show 7 Circular cells ( days of week ) centered in the collection view.
I want the cells to be with Height = 30 and width = 30 ( given in storyBoard )
class DailyTimeLimitVC: BaseViewController {
#IBOutlet weak var collectionView: UICollectionView!
var identifier = "daysCell"
private var spacing:CGFloat = 20
override func viewDidLoad() {
super.viewDidLoad()
self.setupUI()
}
private func setupUI(){
collectionView.delegate = self
collectionView.dataSource = self
collectionView.allowsMultipleSelection = true
}
extension DailyTimeLimitVC: UICollectionViewDelegate {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
}
extension DailyTimeLimitVC: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! DaysCollectionViewCell
switch indexPath.row {
case 0:
cell.label.text = Week.Monday.rawValue
case 1:
cell.label.text = Week.Tuesday.rawValue
case 2:
cell.label.text = Week.Wednessday.rawValue
case 3:
cell.label.text = Week.Thursday.rawValue
case 4:
cell.label.text = Week.Friday.rawValue
case 5:
cell.label.text = Week.Saturday.rawValue
case 6:
cell.label.text = Week.Sunday.rawValue
default:
return cell
}
return cell
}
}
extension DailyTimeLimitVC: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let numberOfItemsPerRow:CGFloat = 7
let spacingBetweenCells:CGFloat = 5
let totalSpacing = (2 * self.spacing) + ((numberOfItemsPerRow - 1) * spacingBetweenCells) //Amount of total spacing in a row
if let collection = self.collectionView{
let width = (collection.bounds.width - totalSpacing)/numberOfItemsPerRow
return CGSize(width: width, height: width)
}else{
return CGSize(width: 0, height: 0)
}
}
}
And in my collectionViewCell Class
class DaysCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var holderView: UIView!
#IBOutlet weak var label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.holderView.layer.masksToBounds = true
}
override func layoutSubviews() {
super.layoutSubviews()
self.setCircularView()
}
func setCircularView() {
self.holderView.layer.cornerRadius = CGFloat(roundf(Float(self.holderView.frame.size.width / 2.0)))
}
}
Try setting the minimumInteritemSpacingForSectionAt and minimumLineSpacingForSectionAt to the appropriate spacing to fix your issue.
extension DailyTimeLimitVC: UICollectionViewDelegateFlowLayout {
//...
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { 0 }
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { spacing }
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: spacing)
}
}
Since you're calculating the sizeForItemAt by substracting the spacings from all sides this should work. But if you're going for a constant height/width it would be best to calculate the spacing that needs to set instead of setting it as a constant as well.

UICollectionView not loading new cells when scrolling horizontally

I am placing a horizontal scroll UICollectionViewController inside of another UICollectionViewController cell. I am doing this because I want to create an image slider like effect.
Code for horizontal scroll UICollectionViewController:
class BusinessPhotosViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout{
var photos = [String]()
let cellId = "photos"
override func viewDidLoad() {
super.viewDidLoad()
if let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
}
collectionView?.backgroundColor = .red
collectionView?.showsVerticalScrollIndicator = false
collectionView?.showsHorizontalScrollIndicator = false
collectionView?.alwaysBounceVertical = false
collectionView?.isPagingEnabled = true
collectionView?.isScrollEnabled = true
collectionView?.register(BusinessPhotoCells.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! BusinessPhotoCells
if photos.count != 0 {
let imageUrl = photos[indexPath.item]
let url = URL(string: imageUrl)
cell.logoImage.kf.setImage(with: url)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.height-10, height: view.frame.height-10)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
}
In another class I add this horizontal scroll UICollectionViewController as such:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//ADDING SCROLL CELL HERE
if indexPath.section == 1 {
let scrollCell = collectionView.dequeueReusableCell(withReuseIdentifier: pictureCellId, for: indexPath)
scrollCell.backgroundColor = .blue
let bizVC = BusinessPhotosViewController(collectionViewLayout: UICollectionViewFlowLayout())
scrollCell.addSubview(bizVC.view)
bizVC.view.anchor(top: scrollCell.topAnchor, left: scrollCell.leftAnchor, bottom: scrollCell.bottomAnchor, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: scrollCell.frame.width, height: scrollCell.frame.height)
return scrollCell
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: listCellId, for: indexPath) as! CouponCell
return cell
}
Edit: Adding numberOfItemsInSection per comments
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return 0
}
if section == 1 {
return 1
}
return coupons.count
}
I return 1 for this section because I only want 1 horizontal scroll cell...Below that horizontal scroll cell (in section 2) I then have a list of vertical scroll cells that work as expected.
This results in something like this where only 3 squares are loaded up not the 10 as expected:
EDIT: ADDING IMAGE OF WHAT MY VIEW LOOKS LIKE NOW

Changing size of selected UICollectionView Cell

I have a simple UICollectionViewController that returns X amount of cells. I want to have it so when a cell is selected, that specific selected cell will change it's size and become larger in height as all of the other cells stay the same. How do I achieve this? Here's my code:
class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor(white: 0.90, alpha: 1.0)
collectionView?.register(PostCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.showsVerticalScrollIndicator = false
collectionView?.alwaysBounceVertical = true
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height = (view.frame.width) * 9 / 16
return CGSize(width: view.frame.width, height: height + 50 + 50)
}
}
You can check to see if the indexPath from sizeForItemAt is selected. If it is, you return a different height otherwise return the standard height.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.performBatchUpdates(nil, completion: nil)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch collectionView.indexPathsForSelectedItems?.first {
case .some(indexPath):
return CGSize() // your selected height
default:
let height = (view.frame.width) * 9 / 16
return CGSize(width: view.frame.width, height: height + 50 + 50)
}
}

CollectionView Cell clicked expand new view

I'm trying to create a demo app that has a TabBarViewController as well as 4 tabs ( 4 Different ViewControllers on the storyboard)
On the first Tab which is the first ViewController, I have a collectionView
with Cells that have ImageView and few Labels.
What I'm trying to do is, when you click on a Cell, It expands to the whole screen, and the view still stays in the first Tab from the Tab Bar,
just now that the view inside the ViewController has changed to a new view that is an expanded view of the Cell that was selected, and when you scroll down the collection view continues, and you can click another cell and it expands and so on...while you can at any time press back and it shows the last cell that was expanded in the view
I hope I was clear enough about what I'm trying to do. now ill add the code of what I've got already
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var imagesArray = []
var imageSizes = [CGSize]()
var labelsArray = [""]
var descArray = [""]
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.delegate = self
self.collectionView.dataSource = self
for i in 0..<imagesArray.count {
let currImg = imagesArray[i]
let currHeight = currImg.size.height
let currSize = CGSize(width: 150, height: currHeight + 125)
imageSizes.append(currSize)
}
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imagesArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionVCe
cell.cellImg.image = imagesArray[indexPath.row]
cell.descLabel.text = descArray[1]
cell.itemLabel.text = labelsArray[0]
cell.cellImg.contentMode = .scaleAspectFill
cell.layer.cornerRadius = 9
cell.backgroundColor = UIColor.red
cell.cellImg.backgroundColor = UIColor.blue
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return imageSizes[indexPath.row]
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(0, 25, 0, 25)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.frame = collectionView.frame
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You have reload the collection view and also call size delegate of collection view