UICollectionViewCell & Multitasking on iPad

Hi All,


I am using UICollectionViewCell in my app extensively to layout things.


But when on iPad Pro I go into Multitasking mode Collection View Cell Layout breaks.


Can anyone let me know how to fix this & relayout things in Multitasking?


Thanks in Adavance.

Replies

What do you mean by "breaks"? How is the cell layout different than what you expect?

I have designed horizontal row with 4 cells count but when Multitasking activated horizontal cells count goes in to 2 cells and sticking cell to edges.

There's no easy way to fix this, but here's what I would do:


1. Respond to the change of app size by implementing the viewWillTransition(to:with:) method in your view controller.

2. Then, adjust your cell layout object (UICollectionView.collectionViewLayout) by changing the layout properties (minimumInteritemSpacing, itemSize, etc.).

3. Finally, invalidate the layout with UICollectionViewLayout.invalidateLayout.


You will still need to decide how to layout the cells in different screen sizes. My recommendation is to size your cells/spacing based on a ratio with the overall size of the collection view.

Can you explain in detail with example code?

This is just an example (in Swift). I wrote it outside Xcode so it may not work or compile. Since I don't know how you do your custom cell layout, you will need to write your own code that is similar.


// In your view controller:
// This method is called when the app screen size changes.
func viewWillTransition(to size: CGSize,
                   with coordinator: UIViewControllerTransitionCoordinator) {
    if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
        // Adjust the layout. This is a flow layout example.
        flowLayout.minimumInteritemSpacing = size.width / 64.0
        flowLayout.itemSize = CGSize(width: size.width / 10.0, height: 50.0)
        // Invalidate the layout.
        flowLayout.invalidateLayout()
    }
}

I have used your code like this...


    // In your view controller:
    // This method is called when the app screen size changes.
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
            // Adjust the layout. This is a flow layout example.
            // flowLayout.minimumInteritemSpacing = size.width / 0.0
            // flowLayout.itemSize = CGSize(width: size.width / 0.0, height: 0.0)
            // Invalidate the layout.
            flowLayout.invalidateLayout()
        }
        collectionView?.reloadData()
    }


It seems when I get into multitasking this function gets called for 'sizeForItemAt indexPath: IndexPath'


& specifically for iPad this function gets called when I go into multitasking... UIDeviceOrientation.unknown

if UIDevice.current.orientation == UIDeviceOrientation.unknown {
                print("It's iPad UIDeviceOrientation.unknown")
                /
                let spacing = self.view.frame.size.width - 15
                let itemWidth = spacing / 2
                let itemHeight = itemWidth
                sizeArea = CGSize(width: itemWidth, height: itemHeight)
            }


But when I change column size of App view screen size It sticks to two column view in every orientation but it doesn't reload data when I get out of Multitasking....


I want to reload data when I enter into multitasking or get out of multitasking....


Thanks Again.

I made a simple test app with the following code in my view controller:

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        print("view will transition to \(size)")
    }


Here is the output from using split view on an iPad:

view will transition to (694.0, 768.0)
view will transition to (507.0, 768.0)
view will transition to (1024.0, 768.0)

1. Enabled split view to 2/3 of screen

2. Changed split view to 1/2 of screen

3. Disabled split view (to full screen)


From this test, I showed that viewWillTransition(to:with:) is called every time split view is enabled, changed, or disabled.


The first code snippet that you posted seems OK. (Though I did forget to mention that super should be called — line 4.)

    // In your view controller:
    // This method is called when the app screen size changes.
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
            // Adjust the layout. This is a flow layout example.
            // flowLayout.minimumInteritemSpacing = size.width / 0.0
            // flowLayout.itemSize = CGSize(width: size.width / 0.0, height: 0.0)
            // Invalidate the layout.
            flowLayout.invalidateLayout()
        }
        collectionView?.reloadData()
    }


So, if you are still having problems, there might be a problem with your layout code. For example, I would use UITraitCollection instead of UIDevice.current.orientation. Put some print statements in different places so you can check to see what is/what isn't being called when the collection view is reloaded.

HI, Thanks for info.


As you mentioned above.


I am using UITrait Collection to identify if it's iPad iPhone or TV etc.

It look like this:


if(UIScreen.main.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiom.pad)


& I am using UI Device Orientation to identify if device is in Landscape, Portrait or Face Down etc.

It look Like this:


ifUIDevice.current.orientation == UIDeviceOrientation.landscapeLeft



What I am doing wrong here? Do let me know!

Use your print statements to see if your sizeForItemAt: method is being called every time split view is activated. What is the console output?


Regardless, you should be using trait collections to determine the relative screen sizes. Like this:


if traitCollection.verticalSizeClass == .regular && traitCollection.horizontalSizeClass == .compact {

}


See these guides: https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/AdoptingMultitaskingOniPad/QuickStartForSlideOverAndSplitView.html#//apple_ref/doc/uid/TP40015145-CH13-SW1 and https://developer.apple.com/documentation/uikit/uitraitcollection


Also:

Figure 1

Figure 2

Hi,


I have used your code like this...


if traitCollection.verticalSizeClass == .regular && traitCollection.horizontalSizeClass == .regular {


} else if  traitCollection.verticalSizeClass == .regular && traitCollection.horizontalSizeClass == .compact {


}

But is there any way to find out if user on multitasking in split view with regular & regular... than without multitasking with regular and regular...

How to differentiate regular regular properties in normal view & in multitasking view?

I think you can check the main window size to see if the app is in split screen mode. But why does it matter if the app is in split view or not?

In full screen landscape mode without Multitasking UICollection View Cell Columns are 5, When in landscape mode app goes in to multitasking with regular and regular traitcollection columns should be 3.


How to do that?

Again, I don't know your app's layout, so I can't tell you exactly how to fix this.


Here is the size of your app:

UIApplication.shared.keyWindow.bounds.size


Here is the size of the entire screen:

UIScreen.main.bounds.size


Compare the two. If the app is in split view, the two will not be the same. Use the sizes to make a ratio for the collection view sizes and spacing.