인터페이스 방향을 회전하는 동안 UICollectionView에서 contentOffset 유지
UICollectionViewController에서 인터페이스 방향 변경을 처리하려고합니다. 내가 달성하려는 것은 인터페이스 회전 후 동일한 contentOffset 을 갖기를 원한다는 것 입니다. 즉, 경계 변경 비율에 따라 변경해야합니다.
콘텐츠 오프셋 { bounds.size.width * 2, 0}…

… 가로 방향의 콘텐츠 오프셋도 { bounds.size.width * 2, 0} (반대도 마찬가지) 이어야 합니다.

새 오프셋을 계산하는 것은 문제가 아니지만 부드러운 애니메이션을 얻기 위해 설정해야하는 위치 (또는시기)를 모릅니다. 내가 그렇게하는 것은 요금이에서 레이아웃을 무효화 willRotateToInterfaceOrientation:duration:하고 콘텐츠 오프셋을 재설정하는 것입니다 didRotateFromInterfaceOrientation:.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration;
{
self.scrollPositionBeforeRotation = CGPointMake(self.collectionView.contentOffset.x / self.collectionView.contentSize.width,
self.collectionView.contentOffset.y / self.collectionView.contentSize.height);
[self.collectionView.collectionViewLayout invalidateLayout];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
CGPoint newContentOffset = CGPointMake(self.scrollPositionBeforeRotation.x * self.collectionView.contentSize.width,
self.scrollPositionBeforeRotation.y * self.collectionView.contentSize.height);
[self.collectionView newContentOffset animated:YES];
}
회전 후 콘텐츠 오프셋이 변경됩니다.
회전하는 동안 어떻게 설정합니까? 새 콘텐츠 오프셋을 설정하려고 willAnimateRotationToInterfaceOrientation:duration:했지만 이로 인해 매우 이상한 동작이 발생합니다.
GitHub의 내 프로젝트에서 예제를 찾을 수 있습니다 .
다음은 Swift 3.1의 코드이며 Swift 4.2에서도 동일하게 작동합니다.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
{
super.viewWillTransition(to: size, with: coordinator)
let offset = self.collectionView?.contentOffset;
let width = self.collectionView?.bounds.size.width;
let index = round(offset!.x / width!);
let newOffset = CGPoint(x: index * size.width, y: offset!.y)
self.collectionView?.setContentOffset(newOffset, animated: false)
coordinator.animate(alongsideTransition: { (context) in
self.collectionView?.reloadData()
self.collectionView?.setContentOffset(newOffset, animated: false)
}, completion: nil)
}
솔루션 1, "딱 스냅"
contentOffset이 올바른 위치에서 끝나는지만 확인하는 것이 필요한 경우 UICollectionViewLayout의 하위 클래스를 만들고 targetContentOffsetForProposedContentOffset : 메서드를 구현할 수 있습니다 . 예를 들어 다음과 같이 페이지를 계산할 수 있습니다.
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
NSInteger page = ceil(proposedContentOffset.x / [self.collectionView frame].size.width);
return CGPointMake(page * [self.collectionView frame].size.width, 0);
}
하지만 직면하게 될 문제는 해당 전환에 대한 애니메이션이 매우 이상하다는 것입니다. 제 사건에 대해 제가하는 일은 (당신의 사건과 거의 동일합니다) :
솔루션 2, "부드러운 애니메이션"
1) 먼저 collectionView : layout : sizeForItemAtIndexPath : delegate 메소드로 다음과 같이 관리 할 수있는 셀 크기를 설정합니다 .
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return [self.view bounds].size;
}
[self.view bounds]는 기기 회전에 따라 변경됩니다.
2) 장치가 회전하려고 할 때 모든 크기 조정 마스크가있는 컬렉션 뷰 위에 imageView를 추가합니다. 이 뷰는 (위에 있기 때문에) collectionView 기이함을 실제로 숨기고 willRotatoToInterfaceOrientation : 메서드가 애니메이션 블록 내에서 호출되기 때문에 그에 따라 회전합니다. 또한 표시된 indexPath에 따라 다음 contentOffset을 유지하므로 회전이 완료되면 contentOffset을 수정할 수 있습니다.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration
{
// Gets the first (and only) visible cell.
NSIndexPath *indexPath = [[self.collectionView indexPathsForVisibleItems] firstObject];
KSPhotoViewCell *cell = (id)[self.collectionView cellForItemAtIndexPath:indexPath];
// Creates a temporary imageView that will occupy the full screen and rotate.
UIImageView *imageView = [[UIImageView alloc] initWithImage:[[cell imageView] image]];
[imageView setFrame:[self.view bounds]];
[imageView setTag:kTemporaryImageTag];
[imageView setBackgroundColor:[UIColor blackColor]];
[imageView setContentMode:[[cell imageView] contentMode]];
[imageView setAutoresizingMask:0xff];
[self.view insertSubview:imageView aboveSubview:self.collectionView];
// Invalidate layout and calculate (next) contentOffset.
contentOffsetAfterRotation = CGPointMake(indexPath.item * [self.view bounds].size.height, 0);
[[self.collectionView collectionViewLayout] invalidateLayout];
}
UICollectionViewCell의 하위 클래스에는 공용 imageView 속성이 있습니다.
3) 마지막으로 마지막 단계는 콘텐츠 오프셋을 유효한 페이지에 "스냅"하고 임시 이미지 뷰를 제거하는 것입니다.
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[self.collectionView setContentOffset:contentOffsetAfterRotation];
[[self.view viewWithTag:kTemporaryImageTag] removeFromSuperview];
}
위의 "그냥 스냅"대답은 회전하기 전에보기에 있던 항목에서 자주 끝나지 않았기 때문에 저에게 효과적이지 않았습니다. 그래서 콘텐츠 오프셋을 계산하기 위해 포커스 항목 (설정된 경우)을 사용하는 흐름 레이아웃을 파생했습니다. willAnimateRotationToInterfaceOrientation에서 항목을 설정하고 didRotateFromInterfaceOrientation에서 항목을 지 웁니다. 컬렉션보기가 상단 표시 줄 아래에 배치 될 수 있기 때문에 IOS7에서 삽입 조정이 필요한 것 같습니다.
@interface HintedFlowLayout : UICollectionViewFlowLayout
@property (strong)NSIndexPath* pathForFocusItem;
@end
@implementation HintedFlowLayout
-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
if (self.pathForFocusItem) {
UICollectionViewLayoutAttributes* layoutAttrs = [self layoutAttributesForItemAtIndexPath:self.pathForFocusItem];
return CGPointMake(layoutAttrs.frame.origin.x - self.collectionView.contentInset.left, layoutAttrs.frame.origin.y-self.collectionView.contentInset.top);
}else{
return [super targetContentOffsetForProposedContentOffset:proposedContentOffset];
}
}
@end
iOS 8 이상을 사용하는 경우 willRotateToInterfaceOrientation 및 didRotateFromInterfaceOrientation 이 더 이상 사용되지 않습니다.
이제 다음을 사용해야합니다.
/*
This method is called when the view controller's view's size is changed by its parent (i.e. for the root view controller when its window rotates or is resized).
If you override this method, you should either call super to propagate the change to children or manually forward the change to children.
*/
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
// Update scroll position during rotation animation
self.collectionView.contentOffset = (CGPoint){contentOffsetX, contentOffsetY};
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
// Whatever you want to do when the rotation animation is done
}];
}
스위프트 3 :
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (context:UIViewControllerTransitionCoordinatorContext) in
// Update scroll position during rotation animation
}) { (context:UIViewControllerTransitionCoordinatorContext) in
// Whatever you want to do when the rotation animation is done
}
}
올바른 해결책은 -(CGPoint) targetContentOffsetForProposedContentOffset : (CGPoint) proposedContentOffset 메서드를 하위 클래스 로 재정의 하는 것입니다.UICollectionViewFlowLayout
문서에서 :
레이아웃 업데이트 중 또는 레이아웃간에 전환 할 때 컬렉션 뷰는이 메서드를 호출하여 애니메이션 끝에서 사용할 제안 된 콘텐츠 오프셋을 변경할 수있는 기회를 제공합니다. 애니메이션 또는 전환으로 인해 디자인에 적합하지 않은 방식으로 항목이 배치 될 수있는 경우이 방법을 재정의 할 수 있습니다.
이 문제는 나에게도 조금 괴로웠다. 가장 많이 득표 한 답변은 나에게 너무 엉망인 것처럼 보였기 때문에 조금 더 멍청하게 만들고 회전 전후에 각각 컬렉션 뷰의 알파를 변경했습니다. 또한 콘텐츠 오프셋 업데이트에 애니메이션을 적용하지 않습니다.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
self.collectionView.alpha = 0;
[self.collectionView.collectionViewLayout invalidateLayout];
self.scrollPositionBeforeRotation = CGPointMake(self.collectionView.contentOffset.x / self.collectionView.contentSize.width,
self.collectionView.contentOffset.y / self.collectionView.contentSize.height);
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
CGPoint newContentOffset = CGPointMake(self.scrollPositionBeforeRotation.x * self.collectionView.contentSize.width,
self.scrollPositionBeforeRotation.y * self.collectionView.contentSize.height);
[self.collectionView setContentOffset:newContentOffset animated:NO];
self.collectionView.alpha = 1;
}
상당히 매끄럽고 덜 해커.
나는 fz의 변형을 사용합니다. 답변 (iOS 7 및 8) :
회전 전 :
- 현재 보이는 인덱스 경로 저장
- collectionView의 스냅 샷 만들기
- 컬렉션 뷰 위에 UIImageView를 놓습니다.
회전 후 :
- 저장된 색인으로 스크롤
이미지보기를 제거하십시오.
@property (nonatomic) NSIndexPath *indexPath; - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { self.indexPathAfterRotation = [[self.collectionView indexPathsForVisibleItems] firstObject]; // Creates a temporary imageView that will occupy the full screen and rotate. UIGraphicsBeginImageContextWithOptions(self.collectionView.bounds.size, YES, 0); [self.collectionView drawViewHierarchyInRect:self.collectionView.bounds afterScreenUpdates:YES]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; [imageView setFrame:[self.collectionView bounds]]; [imageView setTag:kTemporaryImageTag]; [imageView setBackgroundColor:[UIColor blackColor]]; [imageView setContentMode:UIViewContentModeCenter]; [imageView setAutoresizingMask:0xff]; [self.view insertSubview:imageView aboveSubview:self.collectionView]; [[self.collectionView collectionViewLayout] invalidateLayout]; } - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { [self.collectionView scrollToItemAtIndexPath:self.indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO]; [[self.view viewWithTag:kTemporaryImageTag] removeFromSuperview]; }
troppoli의 솔루션 을 피기 백 하기 위해 뷰 컨트롤러에서 코드를 구현하는 것을 기억할 필요없이 커스텀 클래스에서 오프셋을 설정할 수 있습니다. prepareForAnimatedBoundsChange장치를 회전 할 때 호출되어야합니다 finalizeAnimatedBoundsChange.
@interface OrientationFlowLayout ()
@property (strong)NSIndexPath* pathForFocusItem;
@end
@implementation OrientationFlowLayout
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset {
if (self.pathForFocusItem) {
UICollectionViewLayoutAttributes* layoutAttrs = [self layoutAttributesForItemAtIndexPath:
self.pathForFocusItem];
return CGPointMake(layoutAttrs.frame.origin.x - self.collectionView.contentInset.left,
layoutAttrs.frame.origin.y - self.collectionView.contentInset.top);
}
else {
return [super targetContentOffsetForProposedContentOffset:proposedContentOffset];
}
}
- (void)prepareForAnimatedBoundsChange:(CGRect)oldBounds {
[super prepareForAnimatedBoundsChange:oldBounds];
self.pathForFocusItem = [[self.collectionView indexPathsForVisibleItems] firstObject];
}
- (void)finalizeAnimatedBoundsChange {
[super finalizeAnimatedBoundsChange];
self.pathForFocusItem = nil;
}
@end
이것은 매력처럼 작동합니다.
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.view.bounds.size;
}
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
int currentPage = collectionMedia.contentOffset.x / collectionMedia.bounds.size.width;
float width = collectionMedia.bounds.size.height;
[UIView animateWithDuration:duration animations:^{
[self.collectionMedia setContentOffset:CGPointMake(width * currentPage, 0.0) animated:NO];
[[self.collectionMedia collectionViewLayout] invalidateLayout];
}];
}
인터페이스 방향을 회전 한 후 UICollectionViewCell은 일반적으로 contentSize 및 contentOffset을 업데이트하지 않기 때문에 다른 위치로 이동합니다.
따라서 보이는 UICollectionViewCell은 항상 예상 위치에 있지 않습니다.
다음과 같이 이미지를 예상 한 보이는 UICollectionView
UICollectionView는 『UICollectionViewDelegateFlowLayout』의 [collectionView sizeForItemAtIndexPath] 함수를 위임해야합니다.
그리고이 함수에서 항목 크기를 계산해야합니다.
사용자 정의 UICollectionViewFlowLayout은 다음과 같이 함수를 재정의해야합니다.
-(void)prepareLayout.itemSize, scrollDirection 등을 설정합니다.
-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity.페이지 번호를 계산하거나 보이는 콘텐츠 오프셋을 계산합니다.
-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset.시각적 콘텐츠 오프셋을 반환합니다.
-(CGSize)collectionViewContentSize.collectionView의 전체 콘텐츠 크기를 반환합니다.
viewController는 『willRotateToInterfaceOrientation』을 재정의해야하며이 함수에서 [XXXCollectionVew.collectionViewLayout invalidateLayout] 함수를 호출해야합니다.
하지만 『willRotateToInterfaceOrientation』은 iOS 9에서 더 이상 사용되지 않거나 [XXXCollectionVew.collectionViewLayout invalidateLayout] 함수를 다르게 호출 할 수 있습니다.
다음과 같은 예가 있습니다. https://github.com/bcbod2002/CollectionViewRotationTest
targetContentOffsetForProposedContentOffset을 사용하는 것이 모든 시나리오에서 작동하지 않고 didRotateFromInterfaceOrientation을 사용할 때의 문제는 시각적 아티팩트를 제공한다는 것을 발견 한 경우입니다. 내 완벽하게 작동하는 코드는 다음과 같습니다.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
_indexPathOfFirstCell = [self indexPathsForVisibleItems].firstObject;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
if (_indexPathOfFirstCell) {
[UIView performWithoutAnimation:^{
[self scrollToItemAtIndexPath:self->_indexPathOfFirstCell atScrollPosition:UICollectionViewScrollPositionTop animated:NO];
}];
_indexPathOfFirstCell = nil;
}
}
핵심은 willRotateToInterfaceOrientation 메서드를 사용하여 스크롤하려는 뷰의 부분을 결정하고 willAnimationRotationToInterfaceOrientation을 사용하여 뷰가 크기를 변경했을 때 다시 계산하고 (프레임 워크에서이 메서드를 호출 할 때 경계가 이미 변경됨) 실제로 애니메이션없이 새 위치로 스크롤합니다. 내 코드에서 첫 번째 시각적 셀의 인덱스 경로를 사용했지만 contentOffset.y / contentSize.height의 백분율도 약간 다른 방식으로 작업을 수행합니다.
나를위한 일은 무엇입니까?
내
UICollectionViewDelegateFlowLayout방법 에서 내 세포의 크기 설정func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize { return collectionView.bounds.size }그 후 이렇게 구현
willRotateToInterfaceOrientationToInterfaceOrientation:duration:합니다override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) { let currentPage = Int(collectionView.contentOffset.x / collectionView.bounds.size.width) var width = collectionView.bounds.size.height UIView.animateWithDuration(duration) { self.collectionView.setContentOffset(CGPointMake(width * CGFloat(currentPage), 0.0), animated: false) self.collectionView.collectionViewLayout.invalidateLayout() } }
위의 코드는 Swift에 있지만 요점을 알 수 있으며 "번역"하기 쉽습니다.
Swift 3에서
indexPath.item, x 좌표 또는 다른 항목으로 회전하기 전에 표시되는 셀 항목 (페이지)을 추적해야합니다. 그런 다음 UICollectionView에서 :
override func collectionView(_ collectionView: UICollectionView, targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
let page:CGFloat = pageNumber // your tracked page number eg. 1.0
return CGPoint(x: page * collectionView.frame.size.width, y: -(topInset))
//the 'y' value would be '0' if you don't have any top EdgeInset
}
제 경우에는 viewDidLayoutSubviews ()의 레이아웃을 무효화하여 collectionView.frame.size.width가 회전 된 collectionVC의 뷰 너비가되도록합니다.
(잘못된) 애니메이션 동안 collectionView를 숨기고 대신 올바르게 회전하는 셀의 자리 표시 자보기를 표시 할 수 있습니다.
간단한 사진 갤러리의 경우 꽤 좋아 보이는 방법을 찾았습니다. 여기 내 대답을 참조하십시오 : 사진 앱과 유사한 UICollectionView를 회전하고 현재 뷰를 중앙에 유지하는 방법은 무엇입니까?
내 방법은 UICollectionViewFlowlayout 객체를 사용하는 것입니다.
가로로 스크롤되는 경우 ojbect 줄 간격을 설정합니다.
[flowLayout setMinimumLineSpacing:26.0f];
세로로 스크롤하는 경우 항목 간 간격을 설정합니다.
[flowLayout setMinimumInteritemSpacing:0.0f];
화면을 회전 할 때 다르게 작동합니다. 제 경우에는 가로로 스크롤되므로 최소 간격은 26.0f입니다. 그런 다음 가로 방향으로 회전하면 끔찍해 보입니다. 회전을 확인하고 올바른 방향으로 0.0f 방향으로 최소 선 간격을 설정해야합니다.
그게 다야! 단순한.
내 프로젝트에 문제가 있었고 UICollectionView에 대해 두 가지 다른 레이아웃을 사용했습니다.
mCustomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"LandScapeCell" forIndexPath:indexPath];
theCustomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"PortraitCell" forIndexPath:indexPath];
그런 다음 각 방향에 대해 확인하고 각 방향에 대한 구성을 사용하십시오.
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGSize pnt = CGSizeMake(70, 70);
return pnt; }
-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
// UIEdgeInsetsMake(<#CGFloat top#>, <#CGFloat left#>, <#CGFloat bottom#>, <#CGFloat right#>)
return UIEdgeInsetsMake(3, 0, 3, 0); }
이렇게하면 콘텐츠 오프셋과 셀 크기를 조정할 수 있습니다.
사용 <CollectionViewDelegateFlowLayout>과 방법에 didRotateFromInterfaceOrientation:CollectionView의 데이터를 다시로드.
구현 collectionView:layout:sizeForItemAtIndexPath:방법 <CollectionViewDelegateFlowLayout>및 방법에서 인터페이스 방향을 확인하고 각 셀의 사용자 지정 크기를 적용합니다.
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (UIInterfaceOrientationIsPortrait(orientation)) {
return CGSizeMake(CGFloat width, CGFloat height);
} else {
return CGSizeMake(CGFloat width, CGFloat height);
}
}
나는 이것을 사용하는 비슷한 경우가 있습니다.
- (void)setFrame:(CGRect)frame
{
CGFloat currentWidth = [self frame].size.width;
CGFloat offsetModifier = [[self collectionView] contentOffset].x / currentWidth;
[super setFrame:frame];
CGFloat newWidth = [self frame].size.width;
[[self collectionView] setContentOffset:CGPointMake(offsetModifier * newWidth, 0.0f) animated:NO];
}
collectionView를 포함하는보기입니다. 슈퍼 뷰에서도 이렇게합니다
- (void)setFrame:(CGRect)frame
{
UICollectionViewFlowLayout *collectionViewFlowLayout = (UICollectionViewFlowLayout *)[_collectionView collectionViewLayout];
[collectionViewFlowLayout setItemSize:frame.size];
[super setFrame:frame];
}
이것은 셀 크기를 전체 화면으로 조정하는 것입니다 (정확히 전체보기;)). 여기서이 작업을 수행하지 않으면 셀 크기가 collectionview보다 크고 이에 대한 동작이 정의되지 않았고 bla bla bla .....라는 오류 메시지가 많이 나타날 수 있습니다.
이러한 to 메서드는 collectionview의 한 하위 클래스 또는 collectionview를 포함하는 뷰에 병합 될 수 있지만 현재 프로젝트에서는 이것이 논리적으로 갈 수있는 방법이었습니다.
"스냅 샷"대답은 올바른 접근 방식이며 스냅 샷 오버레이 IMO로 추가 평활화가 필요하지 않습니다. 그러나 어떤 경우에는 올바른 페이지가 스크롤되지 않는 이유를 설명하는 문제가 있습니다. 페이지를 계산할 때 너비가 아닌 높이를 사용하는 것이 좋습니다. 왜? 뷰 지오메트리는 이미 targetContentOffsetForProposedContentOffset이 호출 될 때까지 회전했기 때문에 너비가 이제 높이가됩니다. 또한 반올림은 천장보다 더 합리적입니다. 그래서:
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
NSInteger page = round(proposedContentOffset.x / self.collectionView.bounds.size.height);
return CGPointMake(page * self.collectionView.bounds.size.width, 0);
}
다음 단계로이 문제를 해결했습니다.
- 현재 스크롤 된 NSIndexPath 계산
- UICollectionView에서 스크롤 및 페이지 매김 비활성화
- UICollectionView에 새 흐름 레이아웃 적용
- UICollectionView에서 스크롤 및 페이지 매김 활성화
- UICollectionView를 현재 NSIndexPath로 스크롤
위 단계를 보여주는 코드 템플릿은 다음과 같습니다.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration;
{
//Calculating Current IndexPath
CGRect visibleRect = (CGRect){.origin = self.yourCollectionView.contentOffset, .size = self.yourCollectionView.bounds.size};
CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
self.currentIndexPath = [self.yourCollectionView indexPathForItemAtPoint:visiblePoint];
//Disable Scrolling and Pagination
[self disableScrolling];
//Applying New Flow Layout
[self setupNewFlowLayout];
//Enable Scrolling and Pagination
[self enableScrolling];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
//You can also call this at the End of `willRotate..` method.
//Scrolling UICollectionView to current Index Path
[self.yourCollectionView scrollToItemAtIndexPath:self.currentIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];
}
- (void) disableScrolling
{
self.yourCollectionView.scrollEnabled = false;
self.yourCollectionView.pagingEnabled = false;
}
- (void) enableScrolling
{
self.yourCollectionView.scrollEnabled = true;
self.yourCollectionView.pagingEnabled = true;
}
- (void) setupNewFlowLayout
{
UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
flowLayout.minimumInteritemSpacing = 0;
flowLayout.minimumLineSpacing = 0;
[flowLayout setItemSize:CGSizeMake(EXPECTED_WIDTH, EXPECTED_HEIGHT)];
[self.yourCollectionView setCollectionViewLayout:flowLayout animated:YES];
[self.yourCollectionView.collectionViewLayout invalidateLayout];
}
이게 도움이 되길 바란다.
animateAlongsideTransition블록 인에 몇 가지 문제가 있습니다 animateAlongsideTransition(아래 코드 참조).
애니메이션 중 (그러나 이전에는 아님) 호출됩니다. 내 작업은 상단 보이는 행으로 스크롤하여 테이블보기 스크롤 위치를 업데이트했습니다 (iPad에서 테이블보기 셀이 장치가 위로 이동했을 때 문제가 발생했습니다 따라서 그 문제에 대한 해결책을 찾았습니다.) 그러나 contentOffset에도 유용 할 수 있습니다.
다음과 같은 방법으로 문제를 해결하려고했습니다.
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
__weak TVChannelsListTableViewController *weakSelf = self;
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
weakSelf.topVisibleRowIndexPath = [[weakSelf.tableView indexPathsForVisibleRows] firstObject];
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[weakSelf.tableView scrollToRowAtIndexPath:weakSelf.topVisibleRowIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
}];
}
But it didn’t work. For instance, index path of the top cel was (0, 20). But when the device rotation animateAlongsideTransition block was called and [[weakSelf.tableView indexPathsForVisibleRows] firstObject] returned index path (0, 27).
I thought the problem was in retrieving index paths to weakSelf. Therefore to solve the problem I’ve moved self.topVisibleRowIndexPath before [coordinator animateAlongsideTransition: completion] method calling:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
__weak TVChannelsListTableViewController *weakSelf = self;
self.topVisibleRowIndexPath = [[weakSelf.tableView indexPathsForVisibleRows] firstObject];
[coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[weakSelf.tableView scrollToRowAtIndexPath:weakSelf.topVisibleRowIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
}];
}
And the other interesting thing that I’ve discovered is that the deprecated methods willRotateToInterfaceOrientation and willRotateToInterfaceOrientation are still successful called in iOS later 8.0 when method viewWillTransitionToSize is not redefined.
So the other way to solve the problem in my case was to use deprecated method instead of new one. I think it would be not right solution, but it is possible to try if other ways don’t work :)
Swift 4.2 subclass:
class RotatableCollectionViewFlowLayout: UICollectionViewFlowLayout {
private var focusedIndexPath: IndexPath?
override func prepare(forAnimatedBoundsChange oldBounds: CGRect) {
super.prepare(forAnimatedBoundsChange: oldBounds)
focusedIndexPath = collectionView?.indexPathsForVisibleItems.first
}
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
guard let indexPath = focusedIndexPath
, let attributes = layoutAttributesForItem(at: indexPath)
, let collectionView = collectionView else {
return super.targetContentOffset(forProposedContentOffset: proposedContentOffset)
}
return CGPoint(x: attributes.frame.origin.x - collectionView.contentInset.left,
y: attributes.frame.origin.x - collectionView.contentInset.left)
}
override func finalizeAnimatedBoundsChange() {
super.finalizeAnimatedBoundsChange()
focusedIndexPath = nil
}
}
이 테스트되지 않은 코드를 시도해 볼 수 있습니다.
- (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) toInterfaceOrientation
duration: (NSTimeInterval) duration
{
[UIView animateWithDuration: duration
animation: ^(void)
{
CGPoint newContentOffset = CGPointMake(self.scrollPositionBeforeRotation.x *
self.collectionView.contentSize.height,
self.scrollPositionBeforeRotation.y *
self.collectionView.contentSize.width);
[self.collectionView setContentOffset: newContentOffset
animated: YES];
}];
}
'program story' 카테고리의 다른 글
| MSBuild 스크립트 및 VS2010 게시는 Web.config 변환 적용 (0) | 2020.11.02 |
|---|---|
| Scala 유형 키워드 : 여러 클래스에서 사용하는 가장 좋은 방법 (0) | 2020.11.02 |
| 캡처 그룹으로 Java Regex 바꾸기 (0) | 2020.11.02 |
| switch 문에서 continue 사용 (0) | 2020.11.02 |
| D3 힘 방향 그래프에서 선택한 노드, 링크 및 하위 항목을 강조 표시합니다. (0) | 2020.11.02 |