[ios] UICollectionView flowLayout이 셀을 올바르게 래핑하지 않음

나는이 UICollectionViewFlowLayout가와. 대부분의 경우 예상대로 작동하지만 때때로 세포 중 하나가 제대로 포장되지 않습니다. 예를 들어, 실제로 두 번째 행에서 후행하고 있어야하는 곳에 빈 공간이있는 경우 세 번째 행의 첫 번째 “열”에 있어야하는 셀입니다 (아래 다이어그램 참조). 이 루즈 셀에서 볼 수있는 것은 왼쪽 (나머지는 잘림)이고 그것이 있어야 할 곳은 비어 있습니다.

이것은 일관되게 발생하지 않습니다. 항상 같은 행이 아닙니다. 그런 다음 위로 스크롤 한 다음 뒤로 스크롤하면 셀이 고정됩니다. 또는 셀을 눌렀다가 (푸시를 통해 다음보기로 이동 함) 뒤로 튀어 나오면 셀이 잘못된 위치에있는 것을보고 올바른 위치로 점프합니다.

스크롤 속도가 문제를 재현하기 쉽게 만드는 것 같습니다. 천천히 스크롤하면 가끔 잘못된 위치에있는 셀을 볼 수 있지만 곧바로 올바른 위치로 점프합니다.

섹션 삽입을 추가했을 때 문제가 시작되었습니다. 이전에는 셀이 컬렉션 경계 (삽입이 거의 또는 전혀 없음)에 대해 거의 플러시되었고 문제를 알아 차리지 못했습니다. 그러나 이것은 컬렉션 뷰의 오른쪽과 왼쪽이 비어 있음을 의미합니다. 즉, 스크롤 할 수 없습니다. 또한 스크롤 막대가 오른쪽으로 플러시되지 않았습니다.

Simulator와 iPad 3 모두에서 문제가 발생하도록 할 수 있습니다.

왼쪽과 오른쪽 섹션 삽입으로 인해 문제가 발생하는 것 같습니다 …하지만 값이 잘못되면 동작이 일관 될 것으로 기대합니다. 이것이 Apple의 버그인지 궁금합니다. 또는 아마도 이것은 삽입물이나 이와 유사한 것의 축적 때문일 것입니다.

문제 및 설정 그림


후속 조치 : 나는 2 년 넘게 Nick의 아래 대답을 문제없이 사용하고 있습니다 (사람들이 그 대답에 구멍이 있는지 궁금해하는 경우-아직 찾지 못했습니다). 잘 했어 Nick.



답변

UICollectionViewFlowLayout의 layoutAttributesForElementsInRect 구현에는 섹션 삽입과 관련된 특정 경우 단일 셀에 대해 두 개의 속성 개체를 반환하는 버그가 있습니다. 리턴 된 속성 오브젝트 중 하나가 유효하지 않고 (컬렉션 뷰의 경계 밖에 있음) 다른 하나는 유효합니다. 다음은 컬렉션 뷰의 경계를 벗어난 셀을 제외하여 문제를 해결하는 UICollectionViewFlowLayout의 하위 클래스입니다.

// NDCollectionViewFlowLayout.h
@interface NDCollectionViewFlowLayout : UICollectionViewFlowLayout
@end

// NDCollectionViewFlowLayout.m
#import "NDCollectionViewFlowLayout.h"
@implementation NDCollectionViewFlowLayout
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
  NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
  NSMutableArray *newAttributes = [NSMutableArray arrayWithCapacity:attributes.count];
  for (UICollectionViewLayoutAttributes *attribute in attributes) {
    if ((attribute.frame.origin.x + attribute.frame.size.width <= self.collectionViewContentSize.width) &&
        (attribute.frame.origin.y + attribute.frame.size.height <= self.collectionViewContentSize.height)) {
      [newAttributes addObject:attribute];
    }
  }
  return newAttributes;
}
@end

참조 .

다른 답변은 shouldInvalidateLayoutForBoundsChange에서 YES를 반환하는 것을 제안하지만 이로 인해 불필요한 재 계산이 발생하고 문제가 완전히 해결되지도 않습니다.

내 솔루션은 버그를 완전히 해결하며 Apple이 근본 원인을 해결할 때 문제를 일으키지 않아야합니다.


답변

컬렉션 뷰를 소유하는 viewController에 이것을 넣으십시오.

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [self.collectionView.collectionViewLayout invalidateLayout];
}


답변

내 iPhone 응용 프로그램에서 비슷한 문제를 발견했습니다. Apple 개발자 포럼을 검색하면 제 경우에 효과가 있었고 아마도 귀하의 경우에도 마찬가지 일 것입니다.

아강 UICollectionViewFlowLayout 및 재정의 shouldInvalidateLayoutForBoundsChange를 반환 YES합니다.

//.h
@interface MainLayout : UICollectionViewFlowLayout
@end

//.m
#import "MainLayout.h"
@implementation MainLayout
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}
@end


답변

Nick Snyder의 답변의 Swift 버전 :

class NDCollectionViewFlowLayout : UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect)
        let contentSize = collectionViewContentSize
        return attributes?.filter { $0.frame.maxX <= contentSize.width && $0.frame.maxY < contentSize.height }
    }
}


답변

여백이 삽입 된 기본 gridview 레이아웃에서도이 문제가 발생했습니다. 지금까지 수행 한 제한된 디버깅은 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rectUICollectionViewFlowLayout 하위 클래스에서 구현하고 문제를 명확하게 보여주는 수퍼 클래스 구현이 반환하는 내용을 로깅하는 것입니다.

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *attrsList = [super layoutAttributesForElementsInRect:rect];

    for (UICollectionViewLayoutAttributes *attrs in attrsList) {
        NSLog(@"%f %f", attrs.frame.origin.x, attrs.frame.origin.y);
    }

    return attrsList;
}

구현 - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath하면 itemIndexPath.item == 30에 대해 잘못된 값을 반환하는 것으로 보일 수도 있습니다. 이것은 내 gridview의 줄당 셀 수의 요소 10이며 관련성이 있는지 확실하지 않습니다.

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
    UICollectionViewLayoutAttributes *attrs = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];

    NSLog(@"initialAttrs: %f %f atIndexPath: %d", attrs.frame.origin.x, attrs.frame.origin.y, itemIndexPath.item);

    return attrs;
}

더 많은 디버깅을위한 시간이 부족하여 지금까지 수행 한 해결 방법은 왼쪽 및 오른쪽 여백과 동일한 양으로 collectionviews 너비를 줄이는 것입니다. 여전히 전체 너비가 필요한 헤더가 있으므로 collectionview에서 clipsToBounds = NO를 설정 한 다음 왼쪽 및 오른쪽 삽입도 제거하고 작동하는 것 같습니다. 헤더 뷰가 제자리에 유지 되려면 헤더 뷰에 대한 layoutAttributes를 반환하는 작업을 수행하는 레이아웃 메서드에서 프레임 이동 및 크기 조정을 구현해야합니다.


답변

Apple에 버그 보고서를 추가했습니다. 나를 위해 작동하는 것은 bottom sectionInset을 top inset보다 작은 값으로 설정하는 것입니다.


답변

나는 a를 사용하여 iPhone에서 동일한 세포 교체 문제를 경험하고 UICollectionViewFlowLayout있었기 때문에 귀하의 게시물을 찾아서 기뻤습니다. 나는 당신이 iPad에서 문제가 있다는 것을 알고 있지만, 나는 그것이 일반적인 문제라고 생각하기 때문에 이것을 게시하고 있습니다.UICollectionView . 그래서 여기에 제가 알아 낸 것이 있습니다.

sectionInset해당 문제와 관련이 있음을 확인할 수 있습니다 . 그 외에도headerReferenceSize 세포의 이동 여부에 영향을 미칩니다. (원점을 계산하는 데 필요하므로 의미가 있습니다.)

불행히도 다른 화면 크기도 고려해야합니다. 이 두 속성의 값을 가지고 놀 때 특정 구성이 (3.5 “및 4”) 둘 다에서 작동하거나 없음 또는 화면 크기 중 하나에서만 작동하는 것을 경험했습니다. 보통 그들 중 누구도. (이것은 또한 의미가 있습니다.UICollectionView 변화 있으므로 망막과 비 망막 사이의 차이를 경험하지 않았습니다.)

화면 크기에 따라 sectionInset및 설정을 끝냈습니다 headerReferenceSize. 문제가 더 이상 발생하지 않고 레이아웃이 시각적으로 허용되는 값을 찾을 때까지 약 50 개의 조합을 시도했습니다. 두 화면 크기 모두에서 작동하는 값을 찾는 것은 매우 어렵습니다.

요약하자면, 값을 가지고 놀면서 다른 화면 크기에서 확인하고 Apple이이 문제를 해결하기를 바랍니다.