2013-04-10 58 views
13

我想在我的應用程序中實現「卡片」,如Safari選項卡或App Store搜索。UICollectionView:像Safari選項卡或App Store搜索一樣分頁

我會在屏幕的中心顯示用戶一張牌,並在左右兩側顯示前一張和下一張牌的一部分。 (請參閱App Store搜索或Safari標籤爲例)

我決定使用UICollectionView,我需要更改頁面大小(沒有找到如何)或實現自己的佈局子類(不知道如何)?

請幫忙嗎?

回答

26

下面是我發現得到這種效果的最簡單的方法。它涉及你的收藏視圖和一個額外的祕密滾動視圖。

設置您收集意見

  • 設置您的收藏視圖及其所有數據源的方法。
  • 構圖集合視圖;它應該跨越你想要看到的全部寬度。
  • 將集合視圖的contentInset

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

這有助於抵消正常細胞。

設置你的祕密滾動型

  • 創建滾動視圖,將其放置你喜歡的地方。如果您願意,您可以將其設置爲hidden
  • 將滾動視圖邊界的大小設置爲所需的頁面大小。
  • 將自己設置爲滾動視圖的代表。
  • 將其contentSize設置爲您的收藏視圖的預期內容大小。

移動你的手勢識別

  • 祕密滾動型的手勢識別添加到集合視圖,並禁用集合視圖的手勢識別:

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

代表

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

隨着scrollview的移動,獲取其偏移量並將其設置爲集合視圖的偏移量。

我的博客上講述這個在這裏,所以檢查此鏈接更新:http://khanlou.com/2013/04/paging-a-overflowing-collection-view/

+0

然而,這並不正確傳通過對底層集合視圖iOS7的選擇並點擊事件。有任何想法嗎? – eanticev

+1

在iOS7上(至少 - 在此之前可能是真的),你不需要滾動視圖。只需檢測左右滑動手勢,並使用它們將集合視圖的偏移設置爲前後1頁。 – buildsucceeded

+3

我不能爲了我的生活讓它在iOS7上工作。 「祕密」滾動視圖的「scrollViewDidScroll」永遠不會被傳送。 –

-6

以前是相當複雜的,UICollectionView是的UIScrollView的子類,所以只是這樣做:

[self.collectionView setPagingEnabled:YES]; 

你們都是設置去。

看到這個detailed tutorial

+7

這並沒有解決分頁時可以看到左側和右側內容的問題,如Safari瀏覽器標籤或應用商店搜索。 –

1

Soroush答案的一個小編輯,這對我來說伎倆。 我做的,而不是禁用手勢唯一編輯:

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

我上的CollectionView禁用滾動:

_collectionView.scrollEnabled = NO; 

由於禁用禁用祕密滾動型手勢以及手勢。

6

你也可以繼承UICollectionViewFlowLayout並重寫,像這樣:

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

這將讓根據車速下一個或前一個單元格...它不會對當前單元格對齊但是。

+0

通過此委託方法爲偏移返回不同的'CGPoint'效果很好。停在單元格上的過渡感覺非常平穩。 – Andrei

+0

此外,轉換時將滾動視圖設置爲* snap *的技巧是將集合視圖的「decelerationRate」設置爲「UIScrollViewDecelerationRateFast」。爲了更快的捕捉,如果你將'decelerationRate'設置爲小於0.990000(這似乎是當前的'UIScrollViewDecelerationRateFast'),它看起來還不錯。 – Andrei

0

我會添加另一個解決方案。捕捉到的位置並不完美(不如啓用分頁功能時效果不錯,但效果不錯)。

我嘗試過實施Soroush的解決方案,但它不適用於我。

由於UICollectionViewUIScrollView一個子類它將一個重要UIScrollViewDelegate方法,其是響應:

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

targetContentOffset(一個INOUT指針),可以重新定義爲一個集合視圖停止點(在這種情況下,如果您水平滑動,則爲x)。

約上幾個變量的快速說明如下發現:

  • self.cellWidth - 這是您的收藏觀察室的寬度(如果你願意,你甚至可以硬編碼有)

  • self.minimumLineSpacing - 這是您在單元格之間設置的最小行間距

  • self.scrollingObjects是集合視圖中包含的對象數組(我需要此主要用於算,知道什麼時候該停止滾動)

所以,這個想法是落實在集合視圖的委託這種方法,像這樣:

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

期待你的任何反饋,使捕捉更好。我還會繼續尋找更好的解決方案......在斯威夫特

+0

這似乎工作...但不能精確複製「分頁啓用」,感覺有點尷尬。 –

2

@Mike M's answer ...

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) 
    } 

}