web-dev-qa-db-ja.com

インターフェイスの方向を回転させながら、UICollectionViewにcontentOffsetを保持する

UICollectionViewControllerでインターフェイスの向きの変更を処理しようとしています。私が達成しようとしているのは、インターフェイスの回転後にsamecontentOffsetを取得したいということです。つまり、境界変更の比率に応じて変更する必要があります。

コンテンツオフセットが{bounds.size.width* 2、0}のポートレートで開始…

UICollectionView in portait

…ランドスケープ内のコンテンツオフセットは{bounds.size.width* 2、0}である必要があります(逆も同様).

UICollectionView in landscape

新しいオフセットを計算することは問題ではありませんが、スムーズなアニメーションを取得するために、どこで(またはいつ)設定するかはわかりません。私がそうしているのは、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 にあります。

70

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)
}
23
GSK

解決策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:デリゲートメソッドで管理できるセルサイズを設定します。

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

上記の「スナップ」という答えは、回転する前に表示されていたアイテムで終了しないことが多いため、私にとってはうまくいきませんでした。そこで、コンテンツオフセットの計算にフォーカスアイテム(設定されている場合)を使用するフローレイアウトを導き出しました。 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
13
troppoli

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
    }
}
11
Neilc

正しい解決策は、サブクラス化されたUICollectionViewFlowLayoutの-​​ -(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset メソッドをオーバーライドすることだと思います

ドキュメントから:

レイアウトの更新中、またはレイアウト間の移行時に、コレクションビューはこのメソッドを呼び出して、アニメーションの最後に使用するために提案されたコンテンツオフセットを変更する機会を提供します。アニメーションまたはトランジションにより、デザインに最適でない方法でアイテムが配置される可能性がある場合、このメソッドをオーバーライドできます。

7
2cupsOfTech

troppoli's ソリューションを便乗するために、View Controllerにコードを実装することを忘れずにカスタムクラスにオフセットを設定できます。 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
4
Joe

この問題も少し気になりました。最高の回答が私にとってはあまりにもハックすぎるように思えたので、私はそれを少し馬鹿にしただけで、ローテーションの前後でコレクションビューのアルファをそれぞれ変更しました。また、コンテンツオフセットの更新をアニメーション化しません。

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

かなり滑らかでハッキングが少ない。

4
mafiOSo

Fzのバリアントを使用します。回答(iOS 7および8):

回転前:

  1. 現在表示されているインデックスパスを保存する
  2. CollectionViewのスナップショットを作成します
  3. UIImageViewをコレクションビューの上に配置します

回転後:

  1. 保存されたインデックスまでスクロールします
  2. 画像ビューを削除します。

    @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];
    }
    
4
dulgan

この作品は魅力のようです:

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

インターフェイスの向きを回転した後、contentSizeとcontentOffsetを更新しないため、UICollectionViewCellは通常別の位置に移動します。

そのため、表示されているUICollectionViewCellは常に期待される位置に配置されません。

次のように画像を期待した目に見えるUICollectionView

予想した方向

UICollectionViewは、 『UICollectionViewDelegateFlowLayout』の関数[collectionView sizeForItemAtIndexPath]を委任する必要があります。

そして、この関数でアイテムのサイズを計算する必要があります。

カスタムUICollectionViewFlowLayoutは、次のように関数をオーバーライドする必要があります。

  1. -(void)prepareLayout

    。 itemSize、scrollDirectionなどを設定します。

  2. -(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity

    。ページ番号を計算するか、表示されるコンテンツオフセットを計算します。

  3. -(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset

    。ビジュアルコンテンツオフセットを返します。

  4. -(CGSize)collectionViewContentSize

    。 collectionViewの合計コンテンツサイズを返します。

ViewControllerは 『willRotateToInterfaceOrientation』をオーバーライドする必要があり、この関数では関数[XXXCollectionVew.collectionViewLayout invalidateLayout]を呼び出す必要があります。

ただし、 『willRotateToInterfaceOrientation』はiOS 9で非推奨になります。または、異なる方法で関数[XXXCollectionVew.collectionViewLayout invalidateLayout]を呼び出すことができます。

次のような例があります。 https://github.com/bcbod2002/CollectionViewRotationTest

2
Goston Wu

私の仕事はこれです:

  1. My UICollectionViewDelegateFlowLayoutメソッドからmyセルのサイズを設定します

    func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize
    {
        return collectionView.bounds.size
    }
    
  2. その後、このように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にありますが、ポイントを得て、「翻訳」するのは簡単です

1
Mihai Fratu

in 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のビューの幅になります。

1
Kyle KIM

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の割合もわずかに異なる方法でジョブを実行します。

1

Swift 4.2サブクラス:

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
    }
}
0
Bogdan Novikov

私の方法は、UICollectionViewFlowlayoutオブジェクトを使用することです。

水平方向にスクロールする場合は、ojbectの行間隔を設定します。

[flowLayout setMinimumLineSpacing:26.0f];

垂直方向にスクロールする場合は、アイテム間の間隔を設定します。

[flowLayout setMinimumInteritemSpacing:0.0f];

画面を回転させると動作が異なることに注意してください。私の場合、最小ライン間隔が26.0fになるように水平にスクロールします。次に、横方向に回転すると恐ろしいように見えます。回転を確認し、その方向の最小線間隔を0.0fに設定して正しくする必要があります。

それでおしまい!シンプル。

0
Itsraininggold

次の手順でこの問題を解決しました。

  1. 現在スクロールしているNSIndexPathを計算
  2. ICollectionViewでのスクロールとページネーションの無効化
  3. ICollectionViewに新しいフローレイアウトを適用
  4. ICollectionViewでスクロールとページネーションを有効にする
  5. ICollectionViewを現在の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];
}

これがお役に立てば幸いです。

0
Salman Khakwani

「ジャストスナップ」の答えは正しいアプローチであり、スナップショットオーバーレイ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);
}
0
John Scalo

私のプロジェクトで問題が発生しました。UICollectionViewに2つの異なるレイアウトを使用しました。

mCustomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"LandScapeCell" forIndexPath:indexPath];

theCustomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"PortraitCell" forIndexPath:indexPath];

次に、各方向について確認し、各方向について構成を使用します。

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

これにより、コンテンツのオフセットとセルのサイズを調整できます。

0
Nikhil Lihla

CollectionViewを(誤った)アニメーション中に非表示にし、代わりに正しく回転するセルのプレースホルダービューを表示することができます。

簡単なフォトギャラリーの場合、非常に見栄えの良い方法を見つけました。ここで私の答えを参照してください: 写真アプリに似たUICollectionViewを回転させ、現在のビューを中央に保つ方法?

0
Goodsquirrel

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

    }

}
0
Soto_iGhost

私はこれを使用する似たようなケースがあります

- (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の1つのサブクラスまたはcollectionviewを含むビューにマージできますが、私の現在のプロジェクトではこれが論理的な方法です。

0
Saren Inden

animateAlongsideTransitionanimateAlongsideTransitionブロックに問題がありました(以下のコードを参照)。

アニメーション中(ただし、前ではない)に呼び出されることに注意してください私のタスクは、表示されている一番上の行へのスクロールを使用してテーブルビューのスクロール位置を更新しました(デバイスでテーブルビューのセルが上に移動すると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];
    }];
}

しかし、うまくいきませんでした。たとえば、最上位セルのインデックスパスは(0、20)でした。ただし、デバイスの回転animateAlongsideTransitionブロックが呼び出され、[[weakSelf.tableView indexPathsForVisibleRows] firstObject]がインデックスパス(0、27)を返した場合。

問題はweakSelfへのインデックスパスの取得にあると思いました。したがって、私が移動した問題を解決するためにself.topVisibleRowIndexPath[coordinator animateAlongsideTransition: completion]メソッド呼び出し:

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

そして、私が発見した他の興味深いことは、廃止されたメソッドwillRotateToInterfaceOrientationwillRotateToInterfaceOrientationは、メソッドviewWillTransitionToSizeが再定義されていないiOS 8.0以降で呼び出されたままであるということです。

したがって、私の場合の問題を解決する別の方法は、新しいメソッドの代わりに非推奨のメソッドを使用することでした。私はそれが正しい解決策ではないと思いますが、他の方法がうまくいかない場合は試してみることが可能です:)

0
Yulia