IT박스

UICollectionView 가로 페이징 스크롤 뷰에서 누락 된 논리 정렬

itboxs 2020. 12. 14. 08:00
반응형

UICollectionView 가로 페이징 스크롤 뷰에서 누락 된 논리 정렬


내가있어 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.0 점 . 10.0흐름 레이아웃에서 minimumLineSpacing 의 기본값 임을 깨달았습니다 . 이 I로 설정하면 0.0 I의 경계 폭의 절반으로 설정없이 드리프트, 각 페이지 경계의 추가 절반 표류.

minimumInteritemSpacing변경해 도 효과없습니다 .

편집 -UICollectionViewFlowLayout에 대한 문서에서 :

@property (비 원자) CGFloat minimumLineSpacing;

토론

...

수직 스크롤 그리드의 경우이 값은 연속 행 사이의 최소 간격을 나타냅니다. 가로 스크롤 그리드의 경우이 값은 연속 열 사이의 최소 간격을 나타냅니다. 이 간격은 머리글과 첫 번째 줄 사이 또는 마지막 줄과 바닥 글 사이의 공간에는 적용되지 않습니다.

이 속성의 기본값은 10.0입니다.


여기에 이미지 설명 입력

다음 기사의 솔루션은 우아하고 간단합니다. 주요 아이디어는 모든 contentOffset 값을 전달하여 collectionView 위에 scrollView를 생성하는 것입니다.

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

이 메서드를 구현하여 말해야합니다.

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

나는 pagingEnabled = YES에서 일어나는 것처럼 부드러운 애니메이션을 얻지 못했습니다.


나는이 질문이 오래되었다는 것을 알고있다.

이 문제를 해결하기 위해해야 ​​할 일은 MinimumInterItemSpacing을 0으로 설정하고 콘텐츠의 프레임을 줄이는 것입니다.


나는 문제를 이해한다고 생각한다. 나는 당신도 그것을 이해하도록 노력할 것입니다.

자세히 살펴보면이 문제가 첫 페이지 스 와이프뿐만 아니라 점진적으로 만 발생한다는 것을 알 수 있습니다.

여기에 이미지 설명 입력

내가 올바르게 이해한다면, 현재 앱에서 모든 UICollectionView 항목은 우리가 보는 둥근 상자이며 모든 항목 사이에 일정한 오프셋 / 여백이 있습니다. 이것이 문제의 원인입니다.

대신해야 할 일은 전체 뷰 너비의 1/3 인 UICollectionView 항목을 만든 다음 그 안에 둥근 이미지 뷰를 추가하는 것입니다. 이미지를 참조하려면 녹색이 검은 색이 아닌 UICollectionViewItem이어야합니다.


@devdavid는 flowLayout.minimumLineSpacing에서 0이되었습니다.

레이아웃 편집기에서 행의 최소 간격을 0으로 설정하여 수행 할 수도 있습니다.

더 쉬운 방법


당신은 자신을 굴려 UICollectionViewFlowLayout?

그렇다면 추가 -(CGPoint) targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity하면 scrollview가 중지되어야하는 위치를 계산하는 데 도움이됩니다.

작동 할 수 있습니다 (주의 : 테스트 완료!) :

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

your UICollectionView's width should be an exact multiplication of the cell size width + the left and right insets. In your example, if the cell width is 96, then the UICollectionView's width should be (96 + 5 + 5) * 3 = 318. Or, if you wish to keep UICollectionView's 320 width, your cell size width should be 320 / 3 - 5 - 5 = 96.666.

If this does not help, your UICollectionView's width might be different than what is set in the xib file, when the application runs. To check this - add an NSLog statement to printout the view's size in runtime:

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

This is the same problem that I was experiencing and i posted my solution on another post, so I'll post it again here.

I found a solution to it, and it involved subclassing the UICollectionViewFlowLayout.

My CollectionViewCell size is 302 X 457 and i set my minimum line spacing to be 18 (9pix for each cell)

When you extend from that class there are a few methods that need to be over-ridden. One of them is

  • (CGSize)collectionViewContentSize

In this method, I needed to add up the total width of what was in the UICollectionView. That includes the ([datasource count] * widthOfCollectionViewCell) + ([datasource count] * 18)

Here is my custom UICollectionViewFlowLayout methods....

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

This worked for me, so I hope someone else finds it useful!


you also can set view's width to '320+spacing', and then set page enable to yes. it will scroll '320+spacing' for every time. i think it because page enable will scroll view's width but not screen's width.


I think I do have a solution for this issue. But I do not if it's the best.

UICollectionViewFlowLayout does contain a property called sectionInset. So you could set the section Inset to whatever your need is and make 1 page equalling one section. Therefore your scrolling should automatically fit properly in the pages ( = sections)


I had a similar problem with paging. Even though the cell insets were all 0 and the cell was exactly the same size in width and height as the UICollectionView, the paging wasn't proper.

What I noticed sounds like a bug in this version (UICollectionView, iOS 6): I could see that if I worked with a UICollectionView with width = 310px or above, and a height = 538px, I was in trouble. However, if I decreased the width to, say, 300px (same height) I got things working perfectly!

For some future reference, I hope it helps!


I encountered a similar issue when trying to get horizontal paging working on a 3 x 3 grid of cells with section insets and cell & line spacing.

The answer for me (after trying many of the suggestions - including subclassing UICollectionViewFlowLayout and various UIScrollView delegate solutions) was simple. I simply used sections in the UICollectionView by breaking my dataset up into sections of 9 items (or fewer), and utilising the numberOfSectionsInCollectionView and numberOfItemsInSection UICollectionView datasource methods.

The UICollectionView's horizontal paging now works beautifully. I recommend this approach to anyone currently tearing their hair out over a similar scenario.


UICollectionView에 기본 흐름 레이아웃을 사용하고 각 셀 사이에 공간을 원하지 않는 경우 다음을 통해 miniumumLineSpacing 속성을 0으로 설정할 수 있습니다.

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

참고 URL : https://stackoverflow.com/questions/13228600/uicollectionview-align-logic-missing-in-horizontal-paging-scrollview

반응형