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


100

我使用的是UICollectionViewUICollectionViewFlowLayout

我通过设置每个单元的大小

collectionView:layout:sizeForItemAtIndexPath:

从纵向切换为横向时,我想调整每个单元的大小以完全适合CollectionView的大小,而又不留单元之间的填充空间。

问题:

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

2)而且,更好的是,如何在单元格始终适合屏幕整个尺寸的情况下进行布局?


followben的答案目前被接受,但是它存在一些问题:它将引发控制台错误,并且动画无法正确播放到新的大小。请参阅我的答案以获取正确的实现。
记忆

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

@followben同意每个人在不同的用例中都有自己的问题。但是在这种情况下,OP会指出I would like to adjust the size of each cell to **completely fit the size of the CollectionView**哪一个正是我的答案中提出的解决方案。如果您可以在答案中包括我的解决方案,并记下哪种方法在哪种条件下对我来说最合适就最好。
记忆

Answers:


200

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:将使布局无效并使用动画调整单元格的大小(如果您无需执行其他调整,则可以将nil传递给两个块参数)。

2)而不是用硬编码项目大小-collectionView:layout:sizeForItemAtIndexPath,只需将collectionView边界的高度或宽度除以您要在屏幕上容纳的像元数即可。如果collectionView水平滚动,请使用高度;如果垂直滚动,请使用width。


3
为什么不打电话[self.collectionView.collectionViewLayout invalidateLayout];呢?
user102008 2013年

7
@ user102008调用invalidateLayout将以正确的大小重新绘制单元格,但-performBatchUpdates:completion:将为更改添加动画效果,恕我直言。
2013年

4
在我的情况下,直到滚动屏幕时,代码才会调整collectionviewcell的大小。
newguy

9
didRotateFromInterfaceOrientation弃用在IOS 8
亚诺什

8
在iOS8上,我叫coordinator.animateAlongsideTransitionin viewWillTransitionToSize,并performBatchUpdates在其代码animation块内调用。这样可以产生很好的动画效果。
Mike Taverne

33

如果您不希望带来其他动画performBatchUpdates:completion:,只需使用invalidateLayout强制重新加载collectionViewLayout。

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

13

我解决了使用两个UICollectionViewFlowLayout的问题。一种用于肖像,另一种用于风景。我在-viewDidLoad中将它们分配给我的collectionView

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];
    }
}

8

有一种简单的方法来设置集合视图单元格大小:

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

不确定是否可以动画。


我必须在viewWillLayoutSubviews内部使用此方法,并使用Hai Feng Kao的答案:stackoverflow.com/a/14776915/2298002
温室

7

Swift:集合视图单元格为屏幕高度的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)
}

2

如果您正在使用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
这很棒!willRotateToInterfaceOrientation在iOS 8中已弃用,但viewWillTransitionToSize在此方面同样适用。
keegan3d 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.