Thay đổi kích thước UICollectionViewCell trên các hướng thiết bị khác nhau


100

Tôi đang sử dụng UICollectionViewvới UICollectionViewFlowLayout.

Tôi đặt kích thước của mỗi ô thông qua

collectionView:layout:sizeForItemAtIndexPath:

Khi chuyển từ dọc sang ngang, tôi muốn điều chỉnh kích thước của từng ô để hoàn toàn vừa với kích thước của CollectionView mà không để lại khoảng đệm giữa các ô.

Câu hỏi:

1) Làm thế nào để thay đổi kích thước của một ô sau một sự kiện xoay?

2) Và, tốt hơn nữa, làm thế nào để bố trí nơi các ô luôn phù hợp với toàn bộ kích thước của màn hình?


Câu trả lời của followben hiện đang được chấp nhận, nhưng nó có một số vấn đề: nó sẽ gây ra lỗi bảng điều khiển và hoạt ảnh ở kích thước mới sẽ không diễn ra chính xác. Xem câu trả lời của tôi để thực hiện chính xác.
memmons

@ MichaelG.Emmons: câu trả lời của bạn sẽ hoạt động tốt hơn khi chế độ xem bộ sưu tập chỉ hiển thị một ô có kích thước đầy đủ tại một thời điểm. Trong trường hợp đó, UIKit phàn nàn về việc xoay vòng vì nó sẽ truy vấn sizeForItemAtIndexPathtrước khi gọi didRotateFromInterfaceOrientation. Tuy nhiên, đối với chế độ xem bộ sưu tập có nhiều ô trên màn hình, việc vô hiệu hóa bố cục trước khi xoay sẽ gây ra một bước nhảy khó chịu, có thể nhìn thấy trước khi chế độ xem xoay. Trong trường hợp đó, tôi tin rằng câu trả lời của tôi vẫn là cách triển khai đúng nhất.
followben

@followben Đồng ý rằng mỗi loại có vấn đề trong các trường hợp sử dụng khác nhau. Trong trường hợp này, OP cho biết I would like to adjust the size of each cell to **completely fit the size of the CollectionView**đó chính xác là giải pháp được đề xuất trong câu trả lời của tôi. Nếu bạn có thể đưa giải pháp của tôi vào câu trả lời của bạn và ghi chú lại cách tiếp cận nào hoạt động tốt nhất trong những điều kiện đủ tốt cho tôi.
memmons

Câu trả lời:


200

1) Bạn có thể duy trì và hoán đổi nhiều đối tượng bố cục, nhưng có một cách đơn giản hơn. Chỉ cần thêm phần sau vào UICollectionViewControllerlớp con của bạn và điều chỉnh kích thước theo yêu cầu:

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

Việc gọi -performBatchUpdates:completion:sẽ làm mất hiệu lực của bố cục và thay đổi kích thước các ô có hoạt ảnh (bạn chỉ có thể chuyển nil cho cả hai tham số khối nếu bạn không có thêm điều chỉnh để thực hiện).

2) Thay vì mã hóa cứng kích thước mục -collectionView:layout:sizeForItemAtIndexPath, chỉ cần chia chiều cao hoặc chiều rộng của giới hạn của collectionView cho số ô bạn muốn vừa với màn hình. Sử dụng chiều cao nếu bộ sưu tập của bạn cuộn theo chiều ngang hoặc chiều rộng nếu cuộn theo chiều dọc.


3
tại sao không chỉ cần gọi [self.collectionView.collectionViewLayout invalidateLayout];?
user102008

7
@ user102008 Việc gọi invalidateLayoutsẽ vẽ lại các ô với kích thước chính xác, nhưng -performBatchUpdates:completion:sẽ tạo hoạt ảnh cho các thay đổi, trông IMHO đẹp hơn rất nhiều.
followben

4
Trong trường hợp của tôi, mã không thay đổi kích thước ô sưu tập cho đến khi tôi cuộn màn hình.
newguy

9
didRotateFromInterfaceOrientationkhông được dùng trong iOS 8
János

8
Trên iOS8, tôi đang gọi coordinator.animateAlongsideTransitionvào viewWillTransitionToSizevà gọi performBatchUpdatestrong animationkhối của nó . Điều này mang lại hiệu ứng hoạt hình đẹp mắt.
Mike Taverne

33

Nếu bạn không muốn có thêm hoạt ảnh performBatchUpdates:completion:, chỉ cần sử dụng invalidateLayoutđể buộc collectionViewLayout tải lại.

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

13

Tôi đã giải quyết bằng cách sử dụng hai UICollectionViewFlowLayout. Một cho chân dung và một cho phong cảnh. Tôi chỉ định chúng vào bộ sưu tập của mìnhXem di động trong -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];
}

Sau đó, tôi chỉ cần sửa đổi Phương thức collectionViewFlowLayoutDelgate của mình như thế này

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

Lần cuối cùng tôi chuyển từ một bố cục sang một bố cục khác khi xoay

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

8

Có một cách đơn giản để đặt kích thước ô trong chế độ xem bộ sưu tập:

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

Tuy nhiên, không chắc liệu nó có hoạt hình hay không.


tôi đã phải sử dụng bên trong này viewWillLayoutSubviews với Hải Phong Kao của câu trả lời: stackoverflow.com/a/14776915/2298002
nhà kính

7

Swift: Ô Chế độ xem Bộ sưu tập có chiều cao n% của màn hình

Tôi cần một ô là một tỷ lệ phần trăm chiều cao nhất định của chế độ xem và sẽ thay đổi kích thước khi xoay thiết bị.

Với sự giúp đỡ từ phía trên, đây là giải pháp

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

Nếu bạn đang sử dụng autolayout. Bạn chỉ cần invalidate layoutvào bộ điều khiển chế độ xem khi xoay và điều chỉnh offsettrong lớp con bố cục luồng của bạn.

Vì vậy, trong bộ điều khiển chế độ xem của bạn -

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

Và trong của bạn 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
Điều này hoạt động tuyệt vời! willRotateToInterfaceOrientationkhông được dùng nữa trong iOS 8 nhưng viewWillTransitionToSizehoạt động tốt cho điều này.
keegan3d
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.