Swift: How to refresh UICollectionView layout after rotation of the device
Solution 1
Add this function:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myCollection.collectionViewLayout.invalidateLayout()
}
When you change the orientation, this function would be called.
Solution 2
The better option is to call invalidateLayout()
instead of reloadData()
because it will not force recreation of the cells, so performance will be slightly better:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
myCollection.collectionViewLayout.invalidateLayout()
}
Solution 3
Also you can invalidate it in this way.
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[self.collectionView.collectionViewLayout invalidateLayout];
}
Solution 4
The viewWillLayoutSubviews() did not work for me. Neither did viewDidLayoutSubviews(). Both made the app go into an infinite loop which I checked using a print command.
One of the ways that do work is
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
// Reload here
}
Solution 5
When UICollectionLayout
detects a bounds change, it asks if it needs to reroute the Invalidate layout. You can rewrite the method directly.UICollectionLayout
can call invalidateLayout
method at the right time
class CollectionViewFlowLayout: UICollectionViewFlowLayout{
/// The default implementation of this method returns false.
/// Subclasses can override it and return an appropriate value
/// based on whether changes in the bounds of the collection
/// view require changes to the layout of cells and supplementary views.
/// If the bounds of the collection view change and this method returns true,
/// the collection view invalidates the layout by calling the invalidateLayout(with:) method.
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return (self.collectionView?.bounds ?? newBounds) != newBounds
}
}
Related videos on Youtube
Talha Ahmad Khan
Updated on November 14, 2021Comments
-
Talha Ahmad Khan over 2 years
I used UICollectionView (flowlayout) to build a simple layout. the width for each cell is set to the width of screen using
self.view.frame.width
but when I rotate the device, the cells don't get updated.
I have found a function, which is called upon orientation change :
override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) { //code }
but I am unable to find a way to update the UICollectionView layout
The main code is here:
class ViewController: UIViewController , UICollectionViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout{ @IBOutlet weak var myCollection: UICollectionView! var numOfItemsInSecOne: Int! override func viewDidLoad() { super.viewDidLoad() numOfItemsInSecOne = 8 // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) { //print("orientation Changed") } func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { return 1 } func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return numOfItemsInSecOne } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellO", forIndexPath: indexPath) return cell } func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize{ let itemSize = CGSize(width: self.view.frame.width, height: 100) return itemSize }}
-
Talha Ahmad Khan about 8 yearsthanks @khuong291 what if I have no object of my collectionview, and I have UICollectionViewController ? how will I suppose to call reloadData?
-
Khuong about 8 yearsUICollectionViewController already has
collectionView
property, so you just call them as I said. Just edit them intocollectionView.reloadData()
. It will work. -
Rivera about 8 yearsSo you need to reload the view, you can't just trigger a layout refresh?
-
thesummersign about 7 yearsThis is the correct way. This should be the accepted answer.
-
thesummersign about 7 yearscheck @kelin's answer
.invalidateLayout()
makes a lot lot of sense. -
Abdul Waheed about 7 yearsthis is the correct way to do it.. not the reload data again
-
nyxee almost 7 yearsthat caused a crush for me. and an infinite loop recognized when i put a print statement in the viewDidLayoutSubviews function.
-
kelin almost 7 years@nyxee, try
viewWillLayoutSubviews
then. I bet, your Collection View is the view of the View Controller? If so, I recommend to wrap it into another view. -
nyxee almost 7 yearsthanks.. I'll get back to that at some point. I've switched to tableViews for now. I was using the UICollectionViewController.
-
Alexandre Cassagne over 6 years@kelin May I suggest you change your answer to
viewWillLayoutSubviews
? As it is, the layout doesn't update correctly in current iOS versions. -
arionik about 6 yearsThis might cause endless loops. @birdy's answer works better
-
kelin about 6 yearsThe endless loop occurs if you use
UICollectionViewController
, because in this case thecollectionView
is also the view of the controller. So if you changecollectionView
layoutviewWillLayoutSubviews
and related methods will be called. -
d4Rk almost 6 yearsDon't forget to call
super.viewWillTransition...
-
Rocket Garden over 5 yearsThis worked best for me, the approach required is really dependent of your application of CollectionView, in my instance this was more compatible with the other behaviours of my custom FlowLayout than the other solutions because my items all have the same size that is dependent on the traitCollection
-
jfgrang over 5 years
viewDidLayoutSubviews
makes my app crash when going back to portrait because old layout cells are too big.viewWillLayoutSubviews
works perfectly -
Nikunj Kumbhani over 5 years@Khuong it's work for me but some time cells are too big while going from landscape view to portrait. why happen like this?
-
Khuong over 5 years@NikunjKumbhani If so, I think you set layout wrong, please check it again.
-
Yuvraj Bashyal over 5 yearsThanks for your code but it didn't work in my case too but solved it using notification. Here is the way how it work on my case stackoverflow.com/questions/18653171/…
-
Hons over 5 yearsI had to useAs @jfgrang proposed, i had to use
viewWillLayoutSubviews
to really get the height and width of the cells re-calculated properly... I'd however did not crash in either case. -
DrWhat about 5 yearsBrilliant - it also works when instantiating the UICollectionView within a custom UITableViewCell.
-
rv7284 almost 5 yearsapp hangs on app launch after using this.
-
Imanou Petit over 4 yearsThis solution is not efficient as
viewDidLayoutSubviews()
is called many times when user scrolls the cells of theUICollectionView
. -
Dan Rosenstark over 4 yearsThis should be viewWillLayout
-
titusmagnus about 4 yearsI agree with @jfgrang: overriding
viewWillLayoutSubviews
is the only way I could get it to work (Xcode 11.4.1 + iOS 13.3). -
Aidy almost 4 years
viewWillTransitionToSize
worked,viewDidLayoutSubviews
hanged the app. -
zumzum about 3 yearsI currently use
viewWillTransitionToSize
, but, does not seem as bullet proof asviewDidLayoutSubviews
. viewDidLayoutSubviews kills performance though. -
Lance Samaria about 3 yearsI tried every other highly voted answer in this thread and this is the only one that worked. Btw my collectionView is a subView of the viewController(
view.addSubview(myCollectionView)
-added in viewDidLoad). I put a print statement insideviewWillLayoutSubviews
and it only printed on rotations. I didn't have any endless loops. -
Lance Samaria about 3 yearsthis is the same answer as @kelin's answer stackoverflow.com/a/42174492/4833705
-
Hamed Ghadirian almost 3 yearsTanX!!! You saved me hours! I change
UIScreen.main.bounds.width
tocollectionView.frame.width
and boom! Fixed! -
Mehul almost 3 yearsYour Welcome @HamedGhadirian Enjoy 😉👍
-
tphduy over 2 yearsThis solution doesn't work on iPad, because the horizontal trait and vertical trait are unchanged.
-
Lance Samaria almost 2 yearsThis answer worked for me last year and now it's not working in a new project. Very strange.