且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在旋转接口方向时保持contentOffset在UICollectionView

更新时间:2023-01-25 09:26:55

解决方案1,just snap



您只需要确保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:委托方法管理,如下所示:

   - (CGSize)collectionView:(UICollectionView *)collectionView 
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return [self.view bounds] .size;
}

请注意,[self.view bounds]会根据设备旋转而改变。



2)当设备要旋转时,我在所有调整大小的掩码的集合视图顶部添加一个imageView。这个视图实际上将隐藏collectionView奇怪(因为它在它的顶部),并且由于willRotatoToInterfaceOrientation:方法在动画块内被调用,它将相应地旋转。我还保持下一个contentOffset根据显示的indexPath所以我可以修复contentOffset一旦旋转完成:

  (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 
duration:(NSTimeInterval)duration
{
//获取第一个(且只有)可见单元格。
NSIndexPath * indexPath = [[self.collectionView indexPathsForVisibleItems] firstObject];
KSPhotoViewCell * cell =(id)[self.collectionView cellForItemAtIndexPath:indexPath];

//创建一个临时的imageView,它将占据全屏并旋转。
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];

//使布局无效并计算(下一个)contentOffset。
contentOffsetAfterRotation = CGPointMake(indexPath.item * [self.view bounds] .size.height,0);
[[self.collectionView collectionViewLayout] invalidateLayout];请注意,我的UICollectionViewCell的子类有一个公共的imageView属性。请注意,我的子类UICollectionViewCell有一个公共的imageView属性。

3)最后,最后一步是将内容偏移量捕获到有效的页面并删除临时的imageview。

   - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation 
{
[self.collectionView setContentOffset:contentOffsetAfterRotation];
[[self.view viewWithTag:kTemporaryImageTag] removeFromSuperview];
}


I'm trying to handle interface orientation changes in a UICollectionViewController. What I'm trying to achieve is, that I want to have the same contentOffset after an interface rotation. Meaning, that it should be changed corresponding to the ratio of the bounds change.

Starting in portrait with a content offset of {bounds.size.width * 2, 0} …

… should result to the content offset in landscape also with {bounds.size.width * 2, 0} (and vice versa).

Calculating the new offset is not the problem, but don't know, where (or when) to set it, to get a smooth animation. What I'm doing so fare is invalidating the layout in willRotateToInterfaceOrientation:duration: and resetting the content offset in 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];
}

This changes the content offset after the rotation.

How can I set it during the rotation? I tried to set the new content offset in willAnimateRotationToInterfaceOrientation:duration: but this results into a very strange behavior.

An example can be found in my Project on GitHub.

Solution 1, "just snap"

If what you need is only to ensure that the contentOffset ends in a right position, you can create a subclass of UICollectionViewLayout and implement targetContentOffsetForProposedContentOffset: method. For example you could do something like this to calculate the page:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
    NSInteger page = ceil(proposedContentOffset.x / [self.collectionView frame].size.width);
    return CGPointMake(page * [self.collectionView frame].size.width, 0);
}

But the problem that you'll face is that the animation for that transition is extremely weird. What I'm doing on my case (which is almost the same as yours) is:

Solution 2, "smooth animation"

1) First I set the cell size, which can be managed by collectionView:layout:sizeForItemAtIndexPath: delegate method as follows:

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout  *)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return [self.view bounds].size;
}

Note that [self.view bounds] will change according to the device rotation.

2) When the device is about to rotate, I'm adding an imageView on top of the collection view with all resizing masks. This view will actually hide the collectionView weirdness (because it is on top of it) and since the willRotatoToInterfaceOrientation: method is called inside an animation block it will rotate accordingly. I'm also keeping the next contentOffset according to the shown indexPath so I can fix the contentOffset once the rotation is done:

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

Note that my subclass of UICollectionViewCell has a public imageView property.

3) Finally, the last step is to "snap" the content offset to a valid page and remove the temporary imageview.

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [self.collectionView setContentOffset:contentOffsetAfterRotation];
    [[self.view viewWithTag:kTemporaryImageTag] removeFromSuperview];
}