2015-09-04 12 views
14

Tôi đang làm việc trên phiên bản iOS của ứng dụng tôi đã phát triển trên Android. Ứng dụng này có lưới 2 cột sau tự kích thước (chiều rộng cố định nhưng chiều cao biến) tế bào:cách phát triển UICollectionViewLayout tùy chỉnh có các cột so le với các ô tự định kích thước?

Staggered 2 column layout on Android

Đạt được điều này trong phiên bản Android là dễ dàng bởi vì Google cung cấp một StaggeredGridLayoutManager cho RecyclerView của nó. Bạn chỉ định số cột và hướng cuộn và bạn đã hoàn tất.

Bố cục mặc định UICollectionViewUICollectionViewFlowLayout không cho phép bố cục so le tôi đang tìm kiếm, vì vậy tôi phải triển khai bố cục tùy chỉnh. Tôi đã xem 2 video về WWDC nói về chủ đề này (What's New in Table and Collection ViewsAdvanced User Interfaces with Collection Views) và tôi ít nhiều có ý tưởng về cách triển khai video đó.

Bước 1. Trước tiên, tính toán bố cục xấp xỉ.

enter image description here

Bước 2. Sau đó, các tế bào được tạo ra và kích thước với autolayout.

enter image description here

Bước 3. Sau đó, bộ điều khiển sẽ thông báo cho các kích thước tế bào nên bố trí được cập nhật.

enter image description here

Nghi ngờ của tôi xảy ra khi cố gắng mã hóa các bước này. Tôi đã tìm thấy một tutorial giải thích việc tạo bố cục tùy chỉnh với các cột so le, nhưng nó không sử dụng tính năng tự động trả lời để thu được kích thước của các ô. Mà để lại cho tôi những câu hỏi sau:

Ở bước 2, cách và khi nào tôi có thể lấy kích thước ô?

Ở bước 3, làm cách nào và khi nào tôi có thể thông báo bố cục thay đổi?

+0

Xin chào theo câu hỏi của bạn, bạn có thể tạo [UICollectionView] (http://www.appcoda.com/ios-programming-uicollectionview-tutorial/). Nhưng để tạo [UICollectionViewLayout] tùy chỉnh (http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12) với [ô tự định kích thước] (http://corsarus.com)/2015/collection-view-with-self-sizing-cells /). –

Trả lời

2

Tôi nhận ra đây không phải là câu trả lời hoàn chỉnh, nhưng một số gợi ý liên quan đến các bước 2 và 3 của bạn có thể được tìm thấy trong ghi chú lớp con cho UICollectionViewLayout.

Tôi đoán bạn đã phân lớp UICollectionViewFlowLayout kể từ đầu đầu của tôi Tôi tin rằng đây là điểm khởi đầu tốt để thực hiện điều chỉnh bố cục để có được giao diện so le bạn muốn.

Đối với bước 2 layoutAttributesForElementsInRect(_:) nên cung cấp thuộc tính bố cục cho các ô có kích thước tự.

Đối với bước 3, bố cục của bạn sẽ có shouldInvalidateLayoutForPreferredLayoutAttributes(_:withOriginalAttributes:) được gọi với kích thước ô đã thay đổi.

4

Tôi muốn chỉ ra rằng, như bạn đã đề cập, RayWenderlich PinInterest Layout chính xác là hướng dẫn sẽ giúp bạn đạt được bố cục này.

Để trả lời câu hỏi của bạn - liên quan đến hướng dẫn:

Trong bước 2, làm thế nào và khi nào tôi có thể có được kích thước tế bào?

Để có chiều cao ô, phương thức ủy nhiệm đã được triển khai được gọi trong phương thức prepareLayout của tùy chỉnh UICollectionViewLayout. Phương pháp này được gọi là sau khi (hoặc hai lần, tôi chỉ cố gắng chạy nó với tuyên bố print và tôi nhận được hai cuộc gọi). Điểm của prepareLayout là để khởi tạo thuộc tính frame của ô, nói cách khác, cung cấp kích thước chính xác của mỗi ô. Chúng ta biết rằng chiều rộng là hằng số, và chỉ có height đang thay đổi, vì vậy trong dòng này của prepareLayout:

let cellHeight = delegate.collectionView(collectionView!, 
     heightForItemAtIndexPath: indexPath, withWidth: width) 

Chúng tôi có được chiều cao của tế bào từ các phương pháp đại biểu đã được thực hiện trong UICollectionViewController. Điều này xảy ra cho tất cả các ô chúng tôi muốn hiển thị trong số collectionView. Sau khi có được và sửa đổi chiều cao cho mỗi ô, chúng tôi lưu trữ kết quả để chúng tôi có thể kiểm tra sau này.

Sau đó, đối với collectionView để lấy kích thước của mỗi ô trên màn hình, tất cả những gì cần làm là truy vấn bộ nhớ cache để biết thông tin. Điều này được thực hiện theo phương thức layoutAttributesForElementsInRect của lớp tùy chỉnh UICollectionViewLayout của bạn.

Phương pháp này được gọi tự động theo số UICollectionViewController. Khi UICollectionViewController cần thông tin bố cục cho các ô sắp tới trên màn hình (chẳng hạn như di chuyển, hoặc khi tải lần đầu tiên), bạn trả lại các thuộc tính từ bộ nhớ cache mà bạn đã điền trong prepareLayout.

Khi kết thúc câu hỏi của bạn: Ở bước 2, tôi có thể lấy kích thước ô bằng cách nào và khi nào?

Trả lời: Mỗi kích thước tế bào thu được trong phương pháp prepareLayout tùy chỉnh của bạn UICollectionViewFlowLayout, và được tính sớm trong vòng đời của UICollectionView của bạn.

Ở bước 3, làm cách nào và khi nào tôi có thể thông báo bố cục thay đổi?

Lưu ý rằng các hướng dẫn không tính đến các tế bào mới được bổ sung trong thời gian chạy:

Lưu ý: Như prepareLayout() được gọi bất cứ khi nào bố trí các điểm thu được không còn giá trị, có rất nhiều tình huống trong một điển hình triển khai nơi bạn có thể cần phải tính toán lại các thuộc tính tại đây. Ví dụ: các giới hạn của UICollectionView có thể thay đổi - chẳng hạn như khi định hướng thay đổi - hoặc các mục có thể được thêm vào hoặc xóa khỏi bộ sưu tập. Những trường hợp này nằm ngoài phạm vi của hướng dẫn này, nhưng điều quan trọng là phải nhận thức được chúng trong một triển khai không tầm thường.

Như ông đã viết, đây là một triển khai không nhỏ nhặt mà bạn có thể cần. Tuy nhiên, có một triển khai nhỏ (rất không hiệu quả) mà bạn có thể áp dụng nếu tập dữ liệu của bạn nhỏ (hoặc cho mục đích thử nghiệm). Khi bạn cần vô hiệu hóa bố cục vì xoay màn hình hoặc thêm/xóa ô, bạn có thể dọn bộ nhớ cache trong tùy chỉnh UICollectionViewFlowLayout để buộc prepareLayout khởi động lại thuộc tính bố cục.

Ví dụ, khi bạn phải gọi reloadData trên collectionView, cũng thực hiện cuộc gọi đến lớp bố trí tùy chỉnh của bạn, để xóa bộ nhớ cache:

cache.removeAll() 
+0

Và chúng theo những gì tôi đã viết và theo hướng dẫn. Tất cả các phép tính kích thước được thực hiện trong 'PrepareLayout'. –

0

Các "kích thước tế bào" được định nghĩa bởi UICollectionViewLayoutAttribute trong lớp con bố cục có nghĩa là bạn có thể sửa đổi nó mỗi lần bạn có cơ hội chạm vào chúng. Bạn có thể đặt kích thước của mọi thuộc tính cho những gì bạn mong muốn.

Ví dụ bạn có thể làm điều đó trong layoutAttributesOfElementsInRect (:), tính toán kích thước phù hợp và cấu hình tất cả các thuộc tính trước khi chuyển chúng vào collectionView. Bạn cũng có thể làm điều đó trong layoutAttributeOfItemAtIndexPath (:), thực hiện phép tính khi mọi thuộc tính được tạo.

Ngoài ra, hãy xem xét cung cấp kích thước mong muốn theo nguồn dữ liệu để mọi thuộc tính có thể dễ dàng nhận được kích thước của chúng với chỉ mục của chúng.

Vì nếu bạn muốn có các kích thước tế bào để bố trí các subviews trong một tế bào, làm điều đó trong phương pháp đại biểu collectionView: collectionView: ItemAtIndexPath:

Hope trợ giúp này.

2

First example of custom layout

Trong bước 2, làm thế nào và khi nào tôi có thể có được kích thước tế bào? Bạn cần tính chiều cao của mỗi ô trong phương thức PrepareLayout(). Kết quả tính toán cho mỗi ô nên được gán cho UICollectionViewLayoutAttributes biến và đặt nó vào bộ sưu tập NSDictionary, trong đó khóa sẽ là NSIndexPath (của mỗi ô) và giá trị sẽ là biến UICollectionViewLayoutAttributes.

Ví dụ:

- (void)prepareLayout { 
    [_layoutMap removeAllObjects]; 
    _totalItemsInSection = [self.collectionView numberOfItemsInSection:0]; 
    _columnsYoffset = [self initialDataForColumnsOffsetY]; 

    if (_totalItemsInSection > 0 && self.totalColumns > 0) { 
     [self calculateItemsSize]; 

     NSInteger itemIndex = 0; 
     CGFloat contentSizeHeight = 0; 

     while (itemIndex < _totalItemsInSection) { 
      NSIndexPath *targetIndexPath = [NSIndexPath indexPathForItem:itemIndex inSection:0]; 
      NSInteger columnIndex = [self columnIndexForItemAtIndexPath:targetIndexPath]; 

      // you need to implement this method and perform your calculations 
      CGRect attributeRect = [self calculateItemFrameAtIndexPath:targetIndexPath]; 
      UICollectionViewLayoutAttributes *targetLayoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:targetIndexPath]; 
      targetLayoutAttributes.frame = attributeRect; 

      contentSizeHeight = MAX(CGRectGetMaxY(attributeRect), contentSizeHeight); 
      _columnsYoffset[columnIndex] = @(CGRectGetMaxY(attributeRect) + self.interItemsSpacing); 
      _layoutMap[targetIndexPath] = targetLayoutAttributes; 

      itemIndex += 1; 
     } 

     _contentSize = CGSizeMake(self.collectionView.bounds.size.width - self.contentInsets.left - self.contentInsets.right, 
            contentSizeHeight); 
    } 
} 

Đừng quên để thực hiện phương pháp sau đây:

- (NSArray <UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect { 
    NSMutableArray<UICollectionViewLayoutAttributes *> *layoutAttributesArray = [NSMutableArray new]; 

    for (UICollectionViewLayoutAttributes *layoutAttributes in _layoutMap.allValues) { 
     if (CGRectIntersectsRect(layoutAttributes.frame, rect)) { 
      [layoutAttributesArray addObject:layoutAttributes]; 
     } 
    } 

    return layoutAttributesArray; 
} 

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { 
    return _layoutMap[indexPath]; 
} 

Những phương pháp này sẽ được kích hoạt khi bạn gọi reloadData() mehtod hoặc invalidateLayout().

Ở bước 3, làm cách nào và khi nào tôi có thể thông báo bố cục thay đổi? Chỉ cần gọi phương thức self.collectionView.collectionViewLayout.invalidateLayout() và prepareLayout() sẽ được gọi lại một lần nữa, vì vậy bạn có thể tính toán lại tất cả các tham số bạn cần.

Bạn có thể tìm hướng dẫn của tôi đầy đủ về UICollectionViewLayout tùy chỉnh ở đây: https://octodev.net/custom-collectionviewlayout/

Tutorial chứa thực hiện trong cả hai ngôn ngữ: Swift và Objective-C. Sẽ rất vui khi trả lời tất cả các câu hỏi của bạn.

Các vấn đề liên quan