簡體   English   中英

水平分頁scrollview中缺少UICollectionView對齊邏輯

[英]UICollectionView align logic missing in horizontal paging scrollview

我有一個UICollectionView ,它工作正常,直到我開始滾動。 這里有一些照片: 在此輸入圖像描述

你可以看到它很棒。 當我開始滾動(啟用分頁)時,第一個在屏幕外顯示: 在此輸入圖像描述

這就是問題。 原始我的視圖有3個視圖,我想滾動並僅顯示3個視圖。 但是當它滾動(啟用分頁)時,它隱藏了第一個視圖的一點點,並顯示下一頁的下一個第一個視圖的一點點。

這是一個視頻,因為它有點難以解釋: 問題的視頻(Dropbox)

這是我的UICollectionView設置的圖片: 在此輸入圖像描述

如果有人可以提供幫助,那將會很棒!

根本問題是Flow Layout不支持分頁。 要實現分頁效果,您必須犧牲單元格之間的空間。 並仔細計算單元格框架,並使其可以通過集合視圖框架進行划分而不會留下余地。 我會解釋原因。

說出以下布局就是你想要的。

在此輸入圖像描述

請注意,最左邊距(綠色)不是單元格間距的一部分。 它由流動布局部分插入確定。 由於流布局不支持異構間距值。 這不是一項微不足道的任務。

因此,設置間距和插入后。 您將獲得以下布局。

在此輸入圖像描述

滾動到下一頁后。 你的細胞顯然不符合你的預期。

在此輸入圖像描述

使單元格間隔為0可以解決此問題。 但是,如果您希望頁面上有額外的邊距,則會限制您的設計,特別是如果邊距不同於單元格間距。 它還要求視圖幀必須可被單元格框整除。 有時,如果您的視圖框架未固定(考慮旋轉情況),則會很痛苦。

真正的解決方案是繼承UICollectionViewFlowLayout並覆蓋以下方法

- (CGSize)collectionViewContentSize
{
    // Only support single section for now.
    // Only support Horizontal scroll 
    NSUInteger count = [self.collectionView.dataSource collectionView:self.collectionView
                                               numberOfItemsInSection:0];

    CGSize canvasSize = self.collectionView.frame.size;
    CGSize contentSize = canvasSize;
    if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal)
    {
        NSUInteger rowCount = (canvasSize.height - self.itemSize.height) / (self.itemSize.height + self.minimumInteritemSpacing) + 1;
        NSUInteger columnCount = (canvasSize.width - self.itemSize.width) / (self.itemSize.width + self.minimumLineSpacing) + 1;
        NSUInteger page = ceilf((CGFloat)count / (CGFloat)(rowCount * columnCount));
        contentSize.width = page * canvasSize.width;
    }

    return contentSize;
}


- (CGRect)frameForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CGSize canvasSize = self.collectionView.frame.size;

    NSUInteger rowCount = (canvasSize.height - self.itemSize.height) / (self.itemSize.height + self.minimumInteritemSpacing) + 1;
    NSUInteger columnCount = (canvasSize.width - self.itemSize.width) / (self.itemSize.width + self.minimumLineSpacing) + 1;

    CGFloat pageMarginX = (canvasSize.width - columnCount * self.itemSize.width - (columnCount > 1 ? (columnCount - 1) * self.minimumLineSpacing : 0)) / 2.0f;
    CGFloat pageMarginY = (canvasSize.height - rowCount * self.itemSize.height - (rowCount > 1 ? (rowCount - 1) * self.minimumInteritemSpacing : 0)) / 2.0f;

    NSUInteger page = indexPath.row / (rowCount * columnCount);
    NSUInteger remainder = indexPath.row - page * (rowCount * columnCount);
    NSUInteger row = remainder / columnCount;
    NSUInteger column = remainder - row * columnCount;

    CGRect cellFrame = CGRectZero;
    cellFrame.origin.x = pageMarginX + column * (self.itemSize.width + self.minimumLineSpacing);
    cellFrame.origin.y = pageMarginY + row * (self.itemSize.height + self.minimumInteritemSpacing);
    cellFrame.size.width = self.itemSize.width;
    cellFrame.size.height = self.itemSize.height;

    if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal)
    {
        cellFrame.origin.x += page * canvasSize.width;
    }

    return cellFrame;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes * attr = [super layoutAttributesForItemAtIndexPath:indexPath];
    attr.frame = [self frameForItemAtIndexPath:indexPath];
    return attr;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray * originAttrs = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray * attrs = [NSMutableArray array];

    [originAttrs enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * attr, NSUInteger idx, BOOL *stop) {
        NSIndexPath * idxPath = attr.indexPath;
        CGRect itemFrame = [self frameForItemAtIndexPath:idxPath];
        if (CGRectIntersectsRect(itemFrame, rect))
        {
            attr = [self layoutAttributesForItemAtIndexPath:idxPath];
            [attrs addObject:attr];
        }
    }];

    return attrs;
}

注意,上面的代碼片段只支持單節和水平滾動方向。 但它不難擴展。

此外,如果你沒有數百萬的細胞。 緩存那些UICollectionViewLayoutAttributes可能是一個好主意。

您可以在UICollectionView上禁用分頁,並使用自定義頁面寬度/偏移量實現自定義水平滾動/分頁機制,如下所示:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    float pageWidth = 210;

    float currentOffset = scrollView.contentOffset.x;
    float targetOffset = targetContentOffset->x;
    float newTargetOffset = 0;

    if (targetOffset > currentOffset)
        newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth;
    else
        newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth;

    if (newTargetOffset < 0)
        newTargetOffset = 0;
    else if (newTargetOffset > scrollView.contentSize.width)
        newTargetOffset = scrollView.contentSize.width;

    targetContentOffset->x = currentOffset;
    [scrollView setContentOffset:CGPointMake(newTargetOffset, 0) animated:YES];
}

這個答案是遲到的,但我剛剛一直在玩這個問題,發現漂移的原因是行間距 如果希望UICollectionView / FlowLayout以單元格寬度的精確倍數進行分頁,則必須設置:

UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)collectionView.collectionViewLayout;
flowLayout.minimumLineSpacing = 0.0;

您不會認為行間距在水平滾動中起作用,但顯然它確實如此。

在我的情況下,我正在嘗試從左到右分頁,一次一個單元格,單元格之間沒有空格。 頁面的每一個轉彎都會從所需的位置引入一點點漂移,並且它似乎線性地累積。 每回合~10分 我意識到10.0是流布局中minimumLineSpacing的默認值。 當我將其設置為0.0時 ,沒有漂移,當我將其設置為邊界寬度的一半時,每個頁面都會移動額外的一半邊界。

更改minimumInteritemSpacing 無效

編輯 - 從UICollectionViewFlowLayout的文檔:

@property(nonatomic)CGFloat minimumLineSpacing;

討論

...

對於垂直滾動網格,此值表示連續行之間的最小間距。 對於水平滾動網格,此值表示連續列之間的最小間距。 此間距不應用於標題和第一行之間或最后一行和頁腳之間的空格。

此屬性的默認值為10.0。

在此輸入圖像描述

以下文章的解決方案優雅而簡單。 主要思想是在collectionView的頂部創建scrollView並傳遞所有contentOffset值。

http://b2cloud.com.au/tutorial/uiscrollview-paging-size/

應該通過實現這個方法來說:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity;

我沒有像使用pagingEnabled = YES那樣實現平滑動畫。

我知道這個問題已經過時了,但對於碰巧偶然發現這個問題的人來說。

要糾正此問題,您需要將MinimumInterItemSpacing設置為0並減少內容的幀。

我想我明白了這個問題。 我會嘗試讓你理解它。

如果你仔細觀察,那么你會看到這個問題只是逐漸發生,而不僅僅是在第一頁滑動。

在此輸入圖像描述

如果我理解正確,在您的應用程序中,目前,每個UICollectionView項目都是我們看到的那些圓角框,並且您在所有這些項目之間有一些不變的偏移/邊距。 這就是造成這個問題的原因。

相反,你應該做的是創建一個UICollectionView項,它是整個視圖寬度的1/3,然后在其中添加該圓形圖像視圖。 要引用圖像,綠色應該是您的UICollectionViewItem而不是黑色。

@devdavid在flowLayout.minimumLineSpacing上點亮為零。

它也可以在布局編輯器中完成,將行的最小間距設置為0:

更輕松的方式

你推出自己的UICollectionViewFlowLayout嗎?

如果是這樣,添加-(CGPoint) targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity將幫助您計算scrollview應停止的位置。

這可能有用(NB:UNTESTED!):

-(CGPoint) targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
                             withScrollingVelocity:(CGPoint)velocity
{
  CGFloat offsetAdjustment = MAXFLOAT;
  CGFloat targetX = proposedContentOffset.x + self.minimumInteritemSpacing + self.sectionInset.left;

  CGRect targetRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);

  NSArray *array = [super layoutAttributesForElementsInRect:targetRect];
  for(UICollectionViewLayoutAttributes *layoutAttributes in array) {

    if(layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) {
      CGFloat itemX = layoutAttributes.frame.origin.x;

      if (ABS(itemX - targetX) < ABS(offsetAdjustment)) {
        offsetAdjustment = itemX - targetX;
      }
    }
  }

  return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}

我的答案基於答案https://stackoverflow.com/a/27242179/440168,但更簡單。

在此輸入圖像描述

您應該將UIScrollView放在UICollectionView上方並給它們相同的大小:

@property (nonatomic, weak) IBOutlet UICollectionView *collectionView;
@property (nonatomic, weak) IBOutlet UIScrollView *scrollView;

然后配置集合視圖的contentInset ,例如:

CGFloat inset = self.view.bounds.size.width*2/9;
self.collectionView.contentInset = UIEdgeInsetsMake(0, inset, 0, inset);

和滾動視圖的contentSize

self.scrollView.contentSize = CGSizeMake(self.placesCollectionView.bounds.size.width*[self.collectionView numberOfItemsInSection:0],0);

不要忘記設置滾動視圖的委托:

self.scrollView.delegate = self;

並實現主要魔力:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == self.scrollView) {
        CGFloat inset = self.view.bounds.size.width*2/9;
        CGFloat scale = (self.placesCollectionView.bounds.size.width-2*inset)/scrollView.bounds.size.width;
        self.collectionView.contentOffset = CGPointMake(scrollView.contentOffset.x*scale - inset, 0);
    }
}

你的UICollectionView的寬度應該是單元格大小+左右插圖的精確乘法。 在您的示例中,如果單元格寬度為96,則UICollectionView的寬度應為(96 + 5 + 5) * 3 = 318 或者,如果您希望保持UICollectionView的320寬度,則單元格大小寬度應為320 / 3 - 5 - 5 = 96.666

如果這沒有幫助,那么當應用程序運行時,您的UICollectionView的寬度可能與xib文件中設置的寬度不同。 要檢查這一點 - 添加NSLog語句以在運行時打印視圖的大小:

NSLog(@"%@", NSStringFromCGRect(uiContentViewController.view.frame));

這是我遇到的同樣問題,我在另一篇文章上發布了我的解決方案,所以我會在這里再次發布。

我找到了一個解決方案,它涉及子類化UICollectionViewFlowLayout。

我的CollectionViewCell大小為302 X 457,我將最小行間距設置為18(每個單元格為9pix)

當您從該類擴展時,有一些方法需要被覆蓋。 其中之一是

  • (CGSize)collectionViewContentSize

在這個方法中,我需要加上UICollectionView中的總寬度。 這包括([datasource count] * widthOfCollectionViewCell)+([datasource count] * 18)

這是我的自定義UICollectionViewFlowLayout方法....

-(id)init
{
    if((self = [super init])){

       self.itemSize = CGSizeMake(302, 457);
       self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
       self.minimumInteritemSpacing = 0.0f;
       self.minimumLineSpacing = 18.0f;
       [self setScrollDirection:UICollectionViewScrollDirectionHorizontal];
   }
    return self;
}



-(CGSize)collectionViewContentSize{
   return CGSizeMake((numCellsCount * 302)+(numCellsCount * 18), 457);
}

這對我有用,所以我希望別人覺得它很有用!

您還可以將視圖寬度設置為“320 +間距”,然后將頁面啟用設置為yes。 每次都會滾動'320 +間距'。 我認為這是因為頁面啟用會滾動視圖的寬度而不是屏幕的寬度。

我想我確實有這個問題的解決方案。 但如果它是最好的,我不會。

UICollectionViewFlowLayout確實包含一個名為sectionInset的屬性。 因此,您可以將“插入”部分設置為您需要的任何內容,並使1頁等於一個部分。 因此,您的滾動應自動適合頁面(=部分)

我在分頁方面遇到了類似的問題。 即使單元格插入全部為0並且單元格的寬度和高度與UICollectionView大小UICollectionView ,但分頁不正確。

我注意到像一個錯誤的聲音在這個版本( UICollectionView ,iOS 6中):我可以看到,如果我有工作UICollectionView與寬度= 310px以上,高度= 538px,我遇到了麻煩。 但是,如果我將寬度減小到300px(相同的高度),我的工作就完美了!

對於將來的參考,我希望它有所幫助!

當嘗試在具有分段插入和單元格和行間距的3 x 3網格單元上進行水平分頁時,我遇到了類似的問題。

對我來說(在嘗試了許多建議 - 包括子類化UICollectionViewFlowLayout和各種UIScrollView委托解決方案之后)的答案很簡單。 我只是使用UICollectionView中的部分,將我的數據集分成9個項目(或更少)的部分,並使用numberOfSectionsInCollectionView和numberOfItemsInSection UICollectionView數據源方法。

UICollectionView的水平分頁現在可以很好地工作。 我推薦這種方法給目前在類似情況下撕掉頭發的人。

如果您正在為UICollectionView使用默認的流布局,並且不希望每個單元格之間有任何空格,則可以通過以下方式將其miniumumLineSpacing屬性設置為0:

((UICollectionViewFlowLayout *) self.collectionView.collectionViewLayout).minimumLineSpacing = 0;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM