Collectionview 显示不同单元格与不同 iPad 设备的自定义日历

发布于 2025-01-12 00:21:50 字数 2522 浏览 2 评论 0原文

我正在尝试在我的应用程序中创建自定义日历,并添加了以下代码。

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

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

    }

但在不同的 iPad 设备中,工作日显示的时间多于 7 和 7。有些 iPad 显示一周中的 5 天。它根据屏幕尺寸更改单元格尺寸。但我想添加 &所有 iPad 屏幕宽度的天数。然后我尝试了以下方法

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)
}

,使用此方法后,大多数 iPad 设备的电池续航时间正好为 7 天。但某些屏幕宽度为 1024 的设备(iPad 5、6、7 代)仍每周显示 6 天。我用模拟器尝试过。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

剑心龙吟 2025-01-19 00:21:50

您的答案可能暂时有效,但我建议不要使用硬编码值,例如 173、20、7 等,如果屏幕尺寸甚至方向发生变化,这些值可能不起作用。

主要要弄清楚的是在获取单元格尺寸之前您实际上需要使用多少可用宽度

这就是我要做的:

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)
    }
}

这将为您提供手机肖像中的以下内容:

CollectionView 日历单元格间距流布局 Swift iOS

横向手机

CollectionView 日历单元格间距流布局 Swift iOS 方向自动布局

iPad

iPad UICollectionView Cell calendar columns UICollectionViewFlowLayout

在所有情况下,您都可以在不使用随机数的情况下获得 7 列

这是完整的解决方案:

// 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)
    }
}

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)
    }
}
泛泛之交 2025-01-19 00:21:50

我通过以下代码得到了完美的解决方案。

func collectionView(_ collectionView: UICollectionView, 布局 collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGS尺寸{
返回 CGSize(宽度: (self.contentView.frame.width - 173)/7, 高度: 45)
}

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)
}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文