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;
'IT박스' 카테고리의 다른 글
CSS3 열-강제 중단 / 분할 요소? (0) | 2020.12.14 |
---|---|
symfony2 및 doctrine을 사용하여 기존 데이터베이스에서 단일 엔티티 생성 (0) | 2020.12.14 |
Asp.net 신원 확인 오류 (0) | 2020.12.14 |
Spring Security에 대한 로깅을 어떻게 활성화합니까? (0) | 2020.12.14 |
Git 오류 치명적 : 인증 실패 (0) | 2020.12.14 |