2013-04-10 24 views
13

Tôi muốn triển khai "thẻ" trong ứng dụng của mình như tab Safari hoặc tìm kiếm trên App Store.UICollectionView: phân trang như tab Safari hoặc tìm kiếm trên App Store

Tôi sẽ hiển thị cho người dùng một thẻ ở giữa màn hình và một phần của thẻ trước đó và tiếp theo ở bên trái và bên phải. (Ví dụ: xem tab Tìm kiếm trên App Store hoặc tab Safari)

Tôi quyết định sử dụng UICollectionView và tôi cần thay đổi cỡ trang (không tìm thấy cách) hoặc triển khai phân lớp bố cục riêng (không biết cách)?

Bất kỳ trợ giúp nào, vui lòng?

Trả lời

26

Dưới đây là cách đơn giản nhất mà tôi đã tìm thấy để có được hiệu ứng này. Nó liên quan đến chế độ xem bộ sưu tập của bạn và chế độ xem cuộn bí mật bổ sung.

Thiết lập quan sưu tập của bạn

  • Thiết lập xem bộ sưu tập của bạn và tất cả các phương pháp nguồn dữ liệu của nó.
  • Khung chế độ xem bộ sưu tập; nó sẽ kéo dài toàn bộ chiều rộng mà bạn muốn hiển thị.
  • Đặt xem bộ sưu tập của contentInset:

    _collectionView.contentInset = UIEdgeInsetsMake(0, (self.view.frame.size.width-pageSize)/2, 0, (self.view.frame.size.width-pageSize)/2); 
    

này giúp bù đắp các tế bào đúng cách.

Thiết lập scrollview bí mật của bạn

  • Tạo một scrollview, đặt nó bất cứ nơi nào bạn muốn. Bạn có thể đặt nó thành hidden nếu bạn muốn.
  • Đặt kích thước giới hạn của chế độ xem cuộn với kích thước mong muốn của trang của bạn.
  • Đặt mình là đại biểu của chế độ xem cuộn.
  • Đặt contentSize thành kích thước nội dung dự kiến ​​của chế độ xem bộ sưu tập của bạn.

Chuyển cử chỉ nhận dạng của bạn

  • Thêm nhận dạng cử chỉ các scrollview bí mật của đến xem bộ sưu tập, và tắt chế độ xem bộ sưu tập của cử chỉ recognizer:

    [_collectionView addGestureRecognizer:_secretScrollView.panGestureRecognizer]; 
    _collectionView.panGestureRecognizer.enabled = NO; 
    

Đại biểu

- (void) scrollViewDidScroll:(UIScrollView *)scrollView { 
    CGPoint contentOffset = scrollView.contentOffset; 
    contentOffset.x = contentOffset.x - _collectionView.contentInset.left; 
    _collectionView.contentOffset = contentOffset; 
} 

Khi scrollview di chuyển, hãy lấy bù trừ và đặt giá trị đó thành độ lệch của chế độ xem bộ sưu tập.

Tôi viết blog về vấn đề này ở đây, vì vậy kiểm tra liên kết này để cập nhật: http://khanlou.com/2013/04/paging-a-overflowing-collection-view/

+0

này, tuy nhiên, không vượt qua lựa chọn và sự kiện tap một cách chính xác thông qua các bộ sưu tập xem cơ bản như của iOS7. Bất kỳ ý tưởng? – eanticev

+1

Trên iOS7 (ít nhất— có thể đã đúng trước điều này), bạn không cần scrollview. Chỉ cần phát hiện cử chỉ vuốt trái và phải và sử dụng chúng để đặt độ lệch của giao diện bộ sưu tập của bạn 1 trang về phía trước/sau. – buildsucceeded

+3

Tôi không thể cho cuộc sống của tôi làm cho nó hoạt động trên iOS7. 'scrollViewDidScroll' của chế độ xem cuộn" bí mật "không bao giờ được phân phối. –

-6

Các trước là khá phức tạp, UICollectionView là một lớp con của UIScrollView, vì vậy chỉ cần làm điều này:

[self.collectionView setPagingEnabled:YES]; 

Bạn là tất cả thiết lập để đi.

Xem điều này detailed tutorial.

+7

Điều này không giải quyết được vấn đề với nội dung ở bên trái và bên phải hiển thị khi phân trang, như các tab safari hoặc tìm kiếm trong cửa hàng ứng dụng. –

1

Một chút chỉnh sửa về câu trả lời Soroush, điều này đã làm cho tôi mẹo. Tóm lược sửa đổi duy nhất tôi làm thay cho việc vô hiệu hóa các cử chỉ:

[_collectionView addGestureRecognizer:_secretScrollView.panGestureRecognizer]; 
_collectionView.panGestureRecognizer.enabled = NO; 

tôi vô hiệu hóa di chuyển trên collectionview:

_collectionView.scrollEnabled = NO; 

Như vô hiệu hóa các cử chỉ vô hiệu hóa các cử chỉ scrollview bí mật là tốt.

6

Bạn có thể phân lớp UICollectionViewFlowLayout và ghi đè như vậy:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)contentOffset 
           withScrollingVelocity:(CGPoint)velocity 
{ 
    NSArray* layoutAttributesArray = 
    [self layoutAttributesForElementsInRect:self.collectionView.bounds]; 

    if(layoutAttributesArray.count == 0) 
     return contentOffset; 


    UICollectionViewLayoutAttributes* candidate = 
    layoutAttributesArray.firstObject; 

    for (UICollectionViewLayoutAttributes* layoutAttributes in layoutAttributesArray) 
    { 
     if (layoutAttributes.representedElementCategory != UICollectionElementCategoryCell) 
      continue; 


     if((velocity.x > 0.0 && layoutAttributes.center.x > candidate.center.x) || 
      (velocity.x <= 0.0 && layoutAttributes.center.x < candidate.center.x)) 
      candidate = layoutAttributes; 


    } 

    return CGPointMake(candidate.center.x - self.collectionView.bounds.size.width * 0.5f, contentOffset.y); 
} 

này sẽ nhận được các tế bào tiếp theo hoặc trước đó tùy thuộc vào vận tốc ... nó sẽ không chụp trên ô hiện tại tuy nhiên.

+0

Trả về một 'CGPoint' khác để bù đắp thông qua phương thức ủy nhiệm này hoạt động khá tốt. Việc chuyển đổi để dừng lại trên các tế bào cảm thấy khá trơn tru. – Andrei

+0

Ngoài ra, có vẻ như mẹo để chuyển chế độ xem cuộn sang * snap * khi chuyển đổi là đặt 'decelerationRate' của chế độ xem bộ sưu tập thành' UIScrollViewDecelerationRateFast'. Để chụp nhanh hơn, nó cũng có vẻ như nó hoạt động nếu bạn đặt 'decelerationRate' thành một cái gì đó nhỏ hơn 0.990000 (mà có vẻ là' UIScrollViewDecelerationRateFast' hiện tại). – Andrei

0

Tôi sẽ thêm giải pháp khác. Việc chụp tại chỗ không phải là hoàn hảo (không tốt như khi kích hoạt phân trang được thiết lập, nhưng hoạt động đủ tốt).

Tôi đã thử triển khai giải pháp của Soroush, nhưng nó không hiệu quả đối với tôi.

UICollectionView là một lớp con của UIScrollView nó sẽ phản ứng với một UIScrollViewDelegate phương pháp quan trọng đó là:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView 
        withVelocity:(CGPoint)velocity 
       targetContentOffset:(inout CGPoint *)targetContentOffset 

Các targetContentOffset (một inout con trỏ) cho phép bạn xác định lại điểm dừng cho một cái nhìn bộ sưu tập (x trong trường hợp này nếu bạn vuốt theo chiều ngang).

Một lưu ý nhanh chóng về một cặp vợ chồng của các biến tìm thấy dưới đây:

  • self.cellWidth - đây là chiều rộng tế bào xem bộ sưu tập của bạn (thậm chí bạn có thể hardcode nó ở đó nếu bạn muốn)

  • self.minimumLineSpacing - đây là khoảng cách dòng tối thiểu bạn đặt giữa các ô

  • self.scrollingObjects là mảng đối tượng chứa trong chế độ xem bộ sưu tập (tôi cần phần lớn này cho đếm, để biết khi nào phải ngừng di chuyển)

Vì vậy, ý tưởng là thực hiện phương pháp này trong đại biểu quan điểm bộ sưu tập, như vậy:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView 
        withVelocity:(CGPoint)velocity 
       targetContentOffset:(inout CGPoint *)targetContentOffset 
{  
    if (self.currentIndex == 0 && velocity.x < 0) { 
     // we have reached the first user and we're trying to go back 
     return; 
    } 

    if (self.currentIndex == (self.scrollingObjects.count - 1) && velocity.x > 0) { 
     // we have reached the last user and we're trying to go forward 
     return; 
    } 

    if (velocity.x < 0) { 
     // swiping from left to right (going left; going back) 
     self.currentIndex--; 
    } else { 
     // swiping from right to left (going right; going forward) 
     self.currentIndex++; 
    } 

    float xPositionToStop = 0; 

    if (self.currentIndex == 0) { 
     // first row 
     xPositionToStop = 0; 
    } else { 
     // one of the next ones 
     xPositionToStop = self.currentIndex * self.cellWidth + (self.currentIndex + 1) * self.minimumLineSpacing - ((scrollView.bounds.size.width - 2*self.minimumLineSpacing - self.cellWidth)/2); 
    } 

    targetContentOffset->x = xPositionToStop; 

    NSLog(@"Point of stopping: %@", NSStringFromCGPoint(*targetContentOffset)); 
} 

Mong bất kỳ phản hồi nào bạn có thể có mà làm cho chụp ở vị trí tốt hơn.Tôi cũng sẽ tiếp tục tìm kiếm một giải pháp tốt hơn ...

+0

Điều này dường như làm việc ... nhưng không sao chép "phân trang được kích hoạt" chính xác và cảm thấy một chút khó xử. –

2

@Mike M's answer trong Swift ...

class CenteringFlowLayout: UICollectionViewFlowLayout { 

    override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { 

     guard let collectionView = collectionView, 
      let layoutAttributesArray = layoutAttributesForElementsInRect(collectionView.bounds), 
      var candidate = layoutAttributesArray.first else { return proposedContentOffset } 

     layoutAttributesArray.filter({$0.representedElementCategory == .Cell }).forEach { layoutAttributes in 

      if (velocity.x > 0 && layoutAttributes.center.x > candidate.center.x) || 
       (velocity.x <= 0 && layoutAttributes.center.x < candidate.center.x) { 
       candidate = layoutAttributes 
      } 
     } 

     return CGPoint(x: candidate.center.x - collectionView.bounds.width/2, y: proposedContentOffset.y) 
    } 

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