เปลี่ยนขนาด UICollectionViewCell ในการวางแนวอุปกรณ์ต่างๆ


100

ฉันใช้UICollectionViewกับUICollectionViewFlowLayout.

ฉันกำหนดขนาดของแต่ละเซลล์ผ่านไฟล์

collectionView:layout:sizeForItemAtIndexPath:

เมื่อเปลี่ยนจากแนวตั้งเป็นแนวนอนฉันต้องการปรับขนาดของแต่ละเซลล์ให้พอดีกับขนาดของ CollectionView โดยไม่ต้องเว้นช่องว่างระหว่างเซลล์

คำถาม:

1) จะเปลี่ยนขนาดของเซลล์หลังจากเหตุการณ์การหมุนเวียนได้อย่างไร?

2) และที่ดีไปกว่านั้นคือจะสร้างเค้าโครงที่เซลล์พอดีกับขนาดทั้งหมดของหน้าจอได้อย่างไร?


ปัจจุบันคำตอบของ followben ได้รับการยอมรับ แต่มีปัญหาบางประการ: จะทำให้เกิดข้อผิดพลาดของคอนโซลและภาพเคลื่อนไหวไปยังขนาดใหม่จะไม่เกิดขึ้นอย่างถูกต้อง ดูคำตอบของฉันสำหรับการใช้งานที่ถูกต้อง
memmons

@ MichaelG.Emmons: คำตอบของคุณจะทำงานได้ดีขึ้นเมื่อมุมมองคอลเลคชันแสดงเซลล์ขนาดเต็มเพียงเซลล์เดียวในแต่ละครั้ง ในกรณีที่มันทำให้รู้สึก UIKit บ่นในการหมุนในขณะที่มันจะได้รับการสอบถามก่อนที่จะเรียกsizeForItemAtIndexPath didRotateFromInterfaceOrientationอย่างไรก็ตามสำหรับมุมมองคอลเลกชันที่มีเซลล์หลายเซลล์บนหน้าจอการทำให้เค้าโครงไม่ถูกต้องก่อนการหมุนจะทำให้มีขนาดกระโดดที่น่ารังเกียจและมองเห็นได้ก่อนที่มุมมองจะหมุน ในกรณีนั้นฉันเชื่อว่าคำตอบของฉันยังคงเป็นการใช้งานที่ถูกต้องที่สุด
followben

@followben ยอมรับว่าแต่ละคนมีปัญหาในกรณีการใช้งานที่แตกต่างกัน ในกรณีนี้ OP ระบุI would like to adjust the size of each cell to **completely fit the size of the CollectionView**ว่าคำตอบใดที่เสนอในคำตอบของฉัน หากคุณสามารถรวมคำตอบของฉันไว้ในคำตอบของคุณและจดบันทึกว่าแนวทางใดได้ผลดีที่สุดภายใต้เงื่อนไขใดที่ดีพอสำหรับฉัน
memmons

คำตอบ:


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:จะทำให้เค้าโครงไม่ถูกต้องและปรับขนาดเซลล์ด้วยภาพเคลื่อนไหว (คุณสามารถส่งค่าศูนย์ไปยังพารามิเตอร์บล็อกทั้งสองได้หากคุณไม่มีการปรับเปลี่ยนเพิ่มเติมเพื่อดำเนินการ)

2) แทนที่จะเข้ารหัสขนาดรายการในฮาร์ดโค้ด-collectionView:layout:sizeForItemAtIndexPathเพียงแค่แบ่งความสูงหรือความกว้างของขอบเขตของคอลเลกชันด้วยจำนวนเซลล์ที่คุณต้องการให้พอดีกับหน้าจอ ใช้ความสูงหาก collectionView ของคุณเลื่อนในแนวนอนหรือความกว้างหากเลื่อนในแนวตั้ง


3
ทำไมไม่โทรหา[self.collectionView.collectionViewLayout invalidateLayout];?
user102008

7
@ user102008 การโทรinvalidateLayoutจะวาดเซลล์อีกครั้งด้วยขนาดที่ถูกต้อง แต่-performBatchUpdates:completion:จะทำให้การเปลี่ยนแปลงเคลื่อนไหวซึ่งดูดีกว่า IMHO มาก
followben

4
ในกรณีของฉันรหัสจะไม่ปรับขนาดคอลเลกชันวิวเซลล์จนกว่าฉันจะเลื่อนหน้าจอ
newguy

9
didRotateFromInterfaceOrientationเลิกใช้งานใน iOS 8
János

8
ใน iOS8 ฉันกำลังโทรcoordinator.animateAlongsideTransitionเข้าviewWillTransitionToSizeและโทรperformBatchUpdatesภายในanimationบล็อก สิ่งนี้ให้เอฟเฟกต์ภาพเคลื่อนไหวที่ดี
Mike Taverne

33

หากคุณไม่ต้องการให้มีการนำภาพเคลื่อนไหวเพิ่มเติมมาperformBatchUpdates:completion:ให้เพียงแค่ใช้invalidateLayoutเพื่อบังคับให้ collectionViewLayout โหลดซ้ำ

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

13

ฉันแก้ไขโดยใช้ UICollectionViewFlowLayout สองตัว หนึ่งสำหรับแนวตั้งและสำหรับแนวนอน ฉันกำหนดให้กับ 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];
    }
}

8

มีวิธีง่ายๆในการตั้งค่าขนาดเซลล์มุมมองคอลเลกชัน:

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

ไม่แน่ใจว่ามันเคลื่อนไหวได้หรือไม่


ฉันต้องใช้สิ่งนี้ภายในมุมมอง WillLayoutSubviews กับคำตอบของ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.