Home > Mobile >  Collectionview Showing Different Cell with Different iPad Devices for Custom Calendar
Collectionview Showing Different Cell with Different iPad Devices for Custom Calendar

Time:03-07

I’m trying to create a custom calendar in my app and I added the following code.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout:
                        UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        return CGSize(width: collectionView.frame.size.width/7, height: 36)

    }

But in different iPad devices, the weekdays show more than 7 & and some iPad show 5 days of the week. It's changing the cell size depending on the screen size. But I want to add & days for all iPad screen widths. Then I tried the following

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    if UIDevice().userInterfaceIdiom == .pad
    {
    if (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad &&
        (UIScreen.main.bounds.size.height == 1366 || UIScreen.main.bounds.size.width == 1366))
    {
    print("iPad Pro : 12.9 inch")
    return CGSize(width: collectionView.frame.width/7   20, height: 36)
    }
    else if (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad &&
             (UIScreen.main.bounds.size.height == 1024 || UIScreen.main.bounds.size.width == 1024))
        {
    print("iPad 2 || iPad Pro : 9.7 inch || iPad Air/iPad Air 2 || iPad Retina ")
    return CGSize(width: collectionView.frame.width/7 - 5, height: 36)
    }
    else if (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad &&
             (UIScreen.main.bounds.size.height == 1180 || UIScreen.main.bounds.size.width == 1180))
        {
    print("iPad Air 4th gen")
    return CGSize(width: collectionView.frame.width/7   5, height: 45)
    }
    else if (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad &&
             (UIScreen.main.bounds.size.height == 1194 || UIScreen.main.bounds.size.width == 1194))
        {
    print("iPad Pro 11")
    return CGSize(width: collectionView.frame.width/7   5, height: 45)
    }
    else if (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad &&
             (UIScreen.main.bounds.size.height == 1112 || UIScreen.main.bounds.size.width == 1112))
        {
    print("iPad Air 3")
    return CGSize(width: collectionView.frame.width/7, height: 45)
    }
    else
        {
        print("iPad 3")
        return CGSize(width: collectionView.frame.width/7, height: 45)
        }
    }
    return CGSize(width: collectionView.frame.width/7, height: 45)
}

For using this most of the iPad devices are getting exactly 7 days cell. But some devices (iPad 5th, 6th, 7th generation) with 1024 screen width still showing 6 days a week. I tried with the simulator.

CodePudding user response:

Your answer might work for the time being, however I would recommend against using hard-coded values like 173, 20, 7 etc which might not work if screen dimensions change or even the orientation.

The main thing to figure out is how much available width you actually have to work with before getting the cell dimensions.

Here is what I would do:

extension CalendarVC: UICollectionViewDelegateFlowLayout
{
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize
    {
        var availableWidth = collectionView.bounds.size.width
        
        // Remove horizontal insets if any
        if let collectionViewLayout
            = collectionViewLayout as? UICollectionViewFlowLayout
        {
            let totalSectionInsets
                = collectionViewLayout.sectionInset.left   collectionViewLayout.sectionInset.right
            
            availableWidth -= totalSectionInsets
            
            // Remove the horizontal spacing between cells, which is a bit tricky
            // The horizontal spacing between cells is the interItemSpacing
            // There will always be numberOfColumns - 1 in spaces between cells
            // For 7 cells, there will be 6 spaces. For 5, there will be 4 etc
            let spaces = numberOfColumns - 1
            let totalSpacing = CGFloat(spaces) * collectionViewLayout.minimumInteritemSpacing
            
            availableWidth -= totalSpacing
        }
        
        // Remove the vertical content insets if any
        let horizontalContentInsets
            = collectionView.contentInset.left   collectionView.contentInset.right
        
        availableWidth -= horizontalContentInsets
        
        // Now we have the actual available width after all the insets and spacing
        // So calculate the cell width
        let cellDimension = availableWidth / CGFloat(numberOfColumns)
        
        // Keep the height same as width to get a square or set it as you wish
        return CGSize(width: cellDimension, height: cellDimension)
    }
}

This will give you the following in phones portrait:

CollectionView Calendar Cells Spacing Flow Layout Swift iOS

Landscape phones

CollectionView Calendar Cells Spacing Flow Layout Swift iOS orientation autolayout

iPad

iPad UICollectionView Cell calendar columns UICollectionViewFlowLayout

In all cases you get 7 columns without using random numbers

Here is the full solution:

// Cell class, not so important for you
fileprivate class DateCell: UICollectionViewCell
{
    static let reuseIdentifier = "DateCell"
    
    let date = UILabel()
    
    override init(frame: CGRect)
    {
        super.init(frame: frame)
        contentView.backgroundColor = .yellow
        configureLabel()
        layoutIfNeeded()
    }
    
    required init?(coder: NSCoder)
    {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func configureLabel()
    {
        contentView.addSubview(date)
        
        date.backgroundColor = .lightGray
        date.textColor = .black
        date.textAlignment = .center
        date.translatesAutoresizingMaskIntoConstraints = false
        
        addConstraints([
        
            date.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            date.topAnchor.constraint(equalTo: contentView.topAnchor),
            date.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
            date.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
        
        ])
    }
}
// View Controller
class CalendarVC: UIViewController
{
    var calendarCollectionView: UICollectionView!
    
    // Number of columns per row
    let numberOfColumns = 7
    
    // Padding between the cells horizontally
    // and vertically
    let padding: CGFloat = 10
    
    let sectionInset: CGFloat = 10
    
    var daysInMonth = 0
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        title = "CV Calendar"
        view.backgroundColor = .white
        
        configureDaysInMonth()
        configureCollectionView()
    }
    
    // I am just getting days of the month, not so important for you
    private func configureDaysInMonth()
    {
        let cal = Calendar(identifier: .gregorian)
        
        // Calculate start and end of the current year (or month with `.month`):
        if let range = cal.range(of: .day, in: .month, for: Date())
        {
            daysInMonth = range.count
        }
    }
    
    private func configureCollectionView()
    {
        calendarCollectionView = UICollectionView(frame: CGRect.zero,
                                                  collectionViewLayout: createLayout())
        
        calendarCollectionView.register(DateCell.self,
                                        forCellWithReuseIdentifier: DateCell.reuseIdentifier)
        
        calendarCollectionView.dataSource = self
        calendarCollectionView.delegate = self
        calendarCollectionView.backgroundColor = .white
        calendarCollectionView.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(calendarCollectionView)
        
        // Collection view auto layout
        view.addConstraints([
        
            calendarCollectionView.leadingAnchor
                .constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor,
                            constant: 0),
            
            calendarCollectionView.topAnchor
                .constraint(equalTo: view.topAnchor,
                            constant: 0),
            
            calendarCollectionView.trailingAnchor
                .constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor,
                            constant: 0),
            
            calendarCollectionView.bottomAnchor
                .constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
                            constant: 0)
            
        ])
    }
    
    private func createLayout() -> UICollectionViewFlowLayout
    {
        let flowLayout = UICollectionViewFlowLayout()
        flowLayout.minimumLineSpacing = padding
        flowLayout.minimumInteritemSpacing = padding
        flowLayout.scrollDirection = .vertical
        flowLayout.sectionInset = UIEdgeInsets(top: sectionInset,
                                               left: 0,
                                               bottom: 0,
                                               right: 0)
        
        return flowLayout
    }
}
// UICollectionView DataSource, not too important for you
extension CalendarVC: UICollectionViewDataSource
{
    func collectionView(_ collectionView: UICollectionView,
                        numberOfItemsInSection section: Int) -> Int
    {
        daysInMonth
    }
    
    func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
    {
        let cell
            = collectionView.dequeueReusableCell(withReuseIdentifier: DateCell.reuseIdentifier,
                                                 for: indexPath) as! DateCell
        
        cell.date.text = "\(indexPath.row   1)"
        
        return cell
    }
}
// UICollectionViewDelegateFlowLayout, same as above
extension CalendarVC: UICollectionViewDelegateFlowLayout
{
    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize
    {
        var availableWidth = collectionView.bounds.size.width
        
        // Remove horizontal insets if any
        if let collectionViewLayout
            = collectionViewLayout as? UICollectionViewFlowLayout
        {
            let totalSectionInsets
                = collectionViewLayout.sectionInset.left   collectionViewLayout.sectionInset.right
            
            availableWidth -= totalSectionInsets
            
            // Remove the horizontal spacing between cells, which is a bit tricky
            // The horizontal spacing between cells is the interItemSpacing
            // There will always be numberOfColumns - 1 in spaces between cells
            // For 7 cells, there will be 6 spaces. For 5, there will be 4 etc
            let spaces = numberOfColumns - 1
            let totalSpacing = CGFloat(spaces) * collectionViewLayout.minimumInteritemSpacing
            
            availableWidth -= totalSpacing
        }
        
        // Remove the vertical content insets if any
        let horizontalContentInsets
            = collectionView.contentInset.left   collectionView.contentInset.right
        
        availableWidth -= horizontalContentInsets
        
        // Now we have the actual available width after all the insets and spacing
        // So calculate the cell width
        let cellDimension = availableWidth / CGFloat(numberOfColumns)
        
        // Keep the height same as width to get a square or set it as you wish
        return CGSize(width: cellDimension, height: cellDimension)
    }
}

CodePudding user response:

I got the perfect solution with the following code.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: (self.contentView.frame.width - 173)/7, height: 45) }

  • Related