在不同设备方向上更改UICollectionViewCell大小

问题描述:

我正在使用UICollectionViewUICollectionViewFlowLayout在不同设备方向上更改UICollectionViewCell大小

我设置每个小区的大小通过

collectionView:layout:sizeForItemAtIndexPath: 

当从纵向到横向的切换,我想调整每个单元的尺寸,以完全贴合的CollectionView的大小,而不留下填充空间在细胞之间。

问题:

1)旋转事件之后如何更改单元格的大小?

2)更好的是,如何使单元格始终适合屏幕的整个大小的布局?

+0

followben的答案是目前公认的一个,但它有一些问题:它会抛出一个控制台错误和动画,新的大小将无法正常发生。看到我的答案正确的实施。 – memmons

+0

@ MichaelG.Emmons:如果集合视图一次只显示一个全尺寸的单元格,您的答案将会更好。在这种情况下,UIKit抱怨旋转是有道理的,因为在调用'didRotateFromInterfaceOrientation'之前它将查询'sizeForItemAtIndexPath'。 但是,对于屏幕上具有多个单元格的集合视图,在旋转之前使布局无效将在视图旋转之前导致大小令人讨厌的可见跳转。在这种情况下,我相信我的答案仍然是最正确的实施。 – followben

+0

@followben同意每个人在不同的使用案例中都有自己的问题。在这种情况下,OP表示“我想调整每个单元格的大小,使其完全符合CollectionView **的大小,这正是我答案中提出的解决方案。如果你可以在你的答案中包括我的解决方案,并记下哪种方法最适合我的条件。 – memmons

1)你可以维护和换出多个布局对象,但有一个更简单的方法。只需以下添加到您的UICollectionViewController子类,并根据需要调整大小:

- (CGSize)collectionView:(UICollectionView *)collectionView 
        layout:(UICollectionViewLayout *)collectionViewLayout 
    sizeForItemAtIndexPath:(NSIndexPath *)indexPath 
{  
    // Adjust cell size for orientation 
    if (UIDeviceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) { 
     return CGSizeMake(170.f, 170.f); 
    } 
    return CGSizeMake(192.f, 192.f); 
} 

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation 
{ 
    [self.collectionView performBatchUpdates:nil completion:nil]; 
} 

调用-performBatchUpdates:completion:会作废的布局和调整与动画细胞(你可以通过零到两个块PARAMS如果你已经没有多余的调整来执行)。

2)不要对-collectionView:layout:sizeForItemAtIndexPath中的项目大小进行硬编码,只需将collectionView的边界的高度或宽度除以要放在屏幕上的单元格的数量即可。如果您的collectionView水平滚动,则使用高度;如果垂直滚动,则使用宽度。

+0

Fiveagle提出的解决方案也在工作,但这是我首选的解决方案。 – Fmessina

+0

如何在焦点位于另一个视图控制器上时处理方向改变,然后焦点切换回原始视图控制器。目前,我的细胞仍然保留由它们保留的方向确定的大小,但取决于新的方向。 –

+0

nm,我刚刚调用[self.collectionView performBatchUpdates:nil completion:nil]; viewDidAppear –

我解决了使用两个UICollectionViewFlowLayout。一个用于肖像,另一个用于景观。我把它们dinamically分配给我的CollectionView在-viewDidLoad

self.portraitLayout = [[UICollectionViewFlowLayout alloc] init]; 
self.landscapeLayout = [[UICollectionViewFlowLayout alloc] init]; 

UIInterfaceOrientation orientationOnLunch = [[UIApplication sharedApplication] statusBarOrientation]; 

if (UIInterfaceOrientationIsPortrait(orientationOnLunch)) { 
    [self.menuCollectionView setCollectionViewLayout:self.portraitLayout]; 
} else { 
    [self.menuCollectionView setCollectionViewLayout:self.landscapeLayout]; 
} 

然后,我只是修改了collectionViewFlowLayoutDelgate方法这样

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{ 
    CGSize returnSize = CGSizeZero; 

    if (collectionViewLayout == self.portraitLayout) { 
     returnSize = CGSizeMake(230.0, 120.0); 
    } else { 
     returnSize = CGSizeMake(315.0, 120.0); 
    } 

    return returnSize; 
} 

最后我从布局到另一个开启旋转

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{ 
    if (UIInterfaceOrientationIsPortrait(fromInterfaceOrientation)) { 
     [self.menuCollectionView setCollectionViewLayout:self.landscapeLayout animated:YES]; 
    } else { 
     [self.menuCollectionView setCollectionViewLayout:self.portraitLayout animated:YES]; 
    } 
} 
+0

谢谢!我会稍后尝试您的解决方案,我会让你知道。 – Fmessina

如果您不希望performBatchUpdates:completion:带来额外的动画,只需使用invalidateLayout即可强制collectionViewLayout重新加载。

- (void)viewWillLayoutSubviews 
{ 
    [super viewWillLayoutSubviews]; 
    [self.collectionView.collectionViewLayout invalidateLayout]; 
} 

有一个简单的方法来设置集合视图单元尺寸:

UICollectionViewFlowLayout *layout = (id) self.collectionView.collectionViewLayout; 
layout.itemSize = CGSizeMake(cellSize, cellSize); 

不知道它的动画虽然。

+0

我不得不在viewWillLayout里面用Hai Feng Kao的回答:http://*.com/a/14776915/2298002 – greenhouse

如果您使用的是autolayout。旋转时需要在视图控制器中输入invalidate layout,并在流布局子类中调整offset

所以,在您的视图控制器 -

override func willRotateToInterfaceOrientation(toInterfaceOrientation:  UIInterfaceOrientation, duration: NSTimeInterval) { 
    self.galleryCollectionView.collectionViewLayout.invalidateLayout() 
} 

而在你FlowLayout Subclass

override func prepareLayout() { 
    if self.collectionView!.indexPathsForVisibleItems().count > 0{ 
    var currentIndex : CGFloat! 
    currentIndex = CGFloat((self.collectionView!.indexPathsForVisibleItems()[0] as! NSIndexPath).row) 
    if let currentVal = currentIndex{ 
     self.collectionView!.contentOffset = CGPointMake(currentIndex * self.collectionView!.frame.width, self.collectionView!.contentOffset.y); 
    } 
    }  
} 
+1

这个很好用!在iOS 8中不推荐使用'willRotateToInterfaceOrientation',但'viewWillTransitionToSize'对此也适用。 – keegan3d

斯威夫特:集合视图细胞是

我需要屏幕高度的N%是一个具有特定百分比视图高度的单元格,并且会随着设备旋转调整大小。

从上面的帮助下,这里是解决方案

override func viewDidLoad() { 
    super.viewDidLoad() 
    automaticallyAdjustsScrollViewInsets = false 
    // if inside nav controller set to true 
} 

override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) { 
    collectionViewLayout.invalidateLayout() 
} 

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { 
    return CGSize(width: 100, height: collectionView.frame.height * 0.9) 
}