ios Swift:如何在设备旋转后刷新 UICollectionView 布局
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36884376/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Swift: How to refresh UICollectionView layout after rotation of the device
提问by Talha Ahmad Khan
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
我使用 UICollectionView (flowlayout) 来构建一个简单的布局。每个单元格的宽度设置为屏幕的宽度使用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
但我找不到更新 UICollectionView 布局的方法
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
}}
回答by Khuong
Add this function:
添加此功能:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myCollection.collectionViewLayout.invalidateLayout()
}
When you change the orientation, this function would be called.
当您更改方向时,将调用此函数。
回答by kelin
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:
更好的选择是调用invalidateLayout()
而不是调用,reloadData()
因为它不会强制重新创建单元格,因此性能会稍微好一点:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
myCollection.collectionViewLayout.invalidateLayout()
}
回答by awuu
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];
}
回答by The Doctor
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.
viewWillLayoutSubviews() 对我不起作用。viewDidLayoutSubviews() 也没有。两者都使应用程序进入无限循环,我使用打印命令进行了检查。
One of the ways that do work is
工作的方法之一是
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
// Reload here
}
回答by birdy
To update UICollectionViewLayout
, traitCollectionDidChange
method might be used as well:
要更新UICollectionViewLayout
,traitCollectionDidChange
也可以使用方法:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
guard previousTraitCollection != nil else { return }
collectionView?.collectionViewLayout.invalidateLayout()
}
回答by Abhinandan Pratap
you can update your UICollectionView Layout by using
您可以使用更新您的 UICollectionView 布局
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if isLandscape {
return CGSizeMake(yourLandscapeWidth, yourLandscapeHeight)
}
else {
return CGSizeMake(yourNonLandscapeWidth, yourNonLandscapeHeight)
}
}
回答by zheng
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
当UICollectionLayout
检测到边界变化时,它会询问是否需要重新路由 Invalidate 布局。您可以直接重写该方法。UICollectionLayout
可以invalidateLayout
在合适的时间调用方法
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
}
}
回答by congpc87
It works for me. And this is code in Objective-C:
这个对我有用。这是 Objective-C 中的代码:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[collectionView.collectionViewLayout invalidateLayout];
}
回答by user2698544
I was also having some problem but then it got solved using :
我也遇到了一些问题,但后来使用以下方法解决了:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
collectionViewFlowLayoutSetup(with: view.bounds.size.width)
collectionView?.collectionViewLayout.invalidateLayout()
collectionViewFlowLayoutSetup(with: size.width)
}
fileprivate func collectionViewFlowLayoutSetup(with Width: CGFloat){
if let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = CGSize(width: Width, height: 300)
}
}
回答by Yubraj Basyal
I solve this by setting notification when screen orientation changes and reloading cell which set itemsize according to screen orientation and setting indexpath to previous cell. This does work with flowlayout too. Here is the code i wrote:
我通过在屏幕方向更改时设置通知并重新加载根据屏幕方向设置 itemsize 并将 indexpath 设置为前一个单元格来解决此问题。这也适用于 flowlayout。这是我写的代码:
var cellWidthInLandscape: CGFloat = 0 {
didSet {
self.collectionView.reloadData()
}
}
var lastIndex: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
cellWidthInLandscape = UIScreen.main.bounds.size.width
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc func rotated() {
// Setting new width on screen orientation change
cellWidthInLandscape = UIScreen.main.bounds.size.width
// Setting collectionView to previous indexpath
collectionView.scrollToItem(at: IndexPath(item: lastIndex, section: 0), at: .right, animated: false)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
NotificationCenter.default.addObserver(self, selector: #selector(rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
// Getting last contentOffset to calculate last index of collectionViewCell
lastIndex = Int(scrollView.contentOffset.x / collectionView.bounds.width)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// Setting new width of collectionView Cell
return CGSize(width: cellWidthInLandscape, height: collectionView.bounds.size.height)
}