how to adjust layout of tableview cells having collectionView on screen rotation on all screen sizes?

304 Views Asked by At

I have CollectionView embedded in TableView cells to show images posted by a user. Im targeting this app on every device from iPhone 8 to Mac and I do have screen rotation on for iPhones and iPads. This is my first time developing app for all platforms & dealing with screen rotation & after searching online I came up with many solutions for handling layout on screen rotation but none of them worked for TableView cells that contain CollectionView and are repeated.

Here I would like to know the best solutions among these for different situations and how to use them to handle CollectionView inside TableView cells.

The first solution I implemented is to handle everything by overriding viewWillLayoutSubviews() in my main View Controller and wrote something like this:

  override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        let index = IndexPath(row: 0, section: 0)

        guard let cell = tblHome.cellForRow(at: index) as? PriorityTaskTableViewCell else {
            return
        }

        guard let flowLayout = cell.cvPriorityTask.collectionViewLayout as? UICollectionViewFlowLayout else {
            return
        }

        flowLayout.invalidateLayout()
        cell.layoutIfNeeded()
        cell.heightCvPriorityTask.constant = cell.height + 40
    }

I had to add cell.layoutIfNeeded() & cell.heightCvPriorityTask.constant = cell.height + 40 to update cell height after rotation. The above approach worked fine for me on every device but only for one TableView cell as you can see I can access CollectionView present in 1st row of TableView only. This approach did not help me deal with situation where I have multiple rows with CollectionView like in case of a social media feed.

Then I decided to deal with rotation in TableView Cell Subclass and came up with following code inside PriorityTaskTableViewCell:

 weak var weakParent: HomeViewController?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        cvPriorityTask.contentInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
        cvPriorityTask.delegate = self
        cvPriorityTask.dataSource = self
    }
    
    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)

        guard
            let previousTraitCollection = previousTraitCollection,
            self.traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass ||
            self.traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass
        else {
            return
        }

        self.cvPriorityTask?.collectionViewLayout.invalidateLayout()
        weakParent?.tblHome.updateConstraints()
        DispatchQueue.main.async {
            self.cvPriorityTask?.reloadData()
            self.weakParent?.tblHome.updateConstraints()
        }
    }

    func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        viewWillTransition(to: size, with: coordinator)

        self.cvPriorityTask?.collectionViewLayout.invalidateLayout()
        weakParent?.tblHome.updateConstraints()

        coordinator.animate(alongsideTransition: { context in

        }, completion: { context in
            self.cvPriorityTask?.collectionViewLayout.invalidateLayout()
            self.weakParent?.tblHome.updateConstraints()
        })
    }

And this is how I'm setting up CollectionView Layout:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        var width = 0.0
        if traitCollection.horizontalSizeClass == .compact {
            width = (weakParent?.view.frame.size.width ?? 200) * 0.88
        }
        else {
            width = (weakParent?.view.frame.size.width ?? 200) * 0.425
        }
        if traitCollection.userInterfaceIdiom == .mac {
            height = width * 0.75
        }
        else {
            height = width * 1.13
        }
        return CGSize(width: width, height: height)
    }

This is again another modified code and it seems to work perfectly for small devices like iPhone 8. But does not have any impact on larger displays on rotation. Maybe it works only for traitCollection.horizontalSizeClass == .compact If I could get this working I would have solved my issue for dealing my repeating CollectionView inside multiple rows but I have no idea why it doesn't work on iPhone 13 Pro Max or iPads.

After trying for hours I removed code and just called cvPriorityTask.collectionViewLayout.invalidateLayout() in layoutSubviews() function and it worked too for all cells so I removed all above code from Cell subclass and left with this only:

override func layoutSubviews() {
        super.layoutSubviews()
        
        cvPriorityTask.collectionViewLayout.invalidateLayout()
    }

This alone made sure collectionView embedded in TableView cells were changing layout as expected but they started cutting TableView cell as main TableView wasn't being updated so I modified my code to somehow refresh TableView too and came up with something like this:

 override func layoutSubviews() {
        super.layoutSubviews()
        
        cvPriorityTask.collectionViewLayout.invalidateLayout()
        layoutIfNeeded()
        heightCvPriorityTask.constant = height + 40
        weakParent?.tblHome.rowHeight = UITableView.automaticDimension
        weakParent?.tblHome.updateConstraints()
    }

It did not work at all. If somehow I could update layout of the main table too from the cell subclass.

So im confused which should be right approach. The first one seemed to be most common one I find on internet but I don't know how to handle multiple cells containing CollectionViews with that approach.

Second one works only for iPhone 8 and I have no idea why. And I don't think reloading collectionView is the right approach but it works for small screens so I wouldn't mind if somehow works for large screens.

The third one seems totally wrong to me but actually works on perfectly for every device. However only cell layout is adjusted and I tried to update layout of whole tableView from Cell Subclass but no luck.

So how to deal with layout issues for CollectionView which is embedded in TableView cells? Am I on right track or there is another better way to deal with this situtaion?

0

There are 0 best solutions below