web-dev-qa-db-ja.com

UICollectionViewFlowLayoutが方向変更の権利を無効化しない

uICollectionViewFlowLayoutを備えたUICollectionViewがあります。 UICollectionViewDelegateFlowLayoutプロトコルも実装しています。

私のデータソースには、カスタムプロトコルに応答するUIViewControllersの束があり、それらのサイズやその他の情報を要求できます。

FlowLayoutデリゲートで、sizeForItemAtIndexPath:を要求すると、プロトコルから取得したアイテムサイズを返します。私のプロトコルを実装するViewControllersは、向きに応じて異なるアイテムサイズを返します。

ここで、デバイスの向きを縦から横に変更しても問題はありません(アイテムの横が大きい)が、元に戻すと、次の警告が表示されます。

    the item width must be less that the width of the UICollectionView minus the section insets left and right values.
    Please check the values return by the delegate.

それはまだ機能しますが、警告が表示されたくないので、私が間違っていることを教えてもらえますか?また別の問題があります。 collectionViews collectionViewLayoutにwillAnimateRotationToInterfaceOrientationで無効化するように指示しないと、sizeForItemAtIndexPath:が呼び出されることはありません。

あなたが私が何を意味するのか理解してほしい。追加情報が必要な場合はお知らせください:)

22
xxtesaxx

1つの解決策は、KVOを使用して、コレクションビューのスーパービューのフレームへの変更をリッスンすることです。 -reloadDataを呼び出すと機能し、警告が表示されなくなります。ここに根本的なタイミングの問題があります...

// -loadView
[self.view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];

// -dealloc
[self.view removeObserver:self forKeyPath:@"frame"];

// KVO Method
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    [collectionView_ reloadData];
}
1
kwigbo

この答えは遅いですが、受け入れられた答えは私にはうまくいきませんでした。発火して忘れるUICollectionViewFlowLayoutが欲しいというOPに共感します。ビューコントローラのレイアウトを無効にすることが実際に最良の解決策であることをお勧めします。

ポートレートとランドスケープの両方で、ビューの中央にあるセルの単一の水平スクロール行が必要でした。

UICollectionViewFlowLayoutをサブクラス化しました。

PrepareLayoutをオーバーライドしてインセットを再計算してから、[super prepareLayout]を呼び出します。

CollectionViewContentSizeのゲッターをオーバーライドして、certainのコンテンツサイズは正しいものにしました。

境界が向きの変更に伴って変化しても、レイアウトはそれ自体では無効になりませんでした。

- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    // does the superclass do anything at this point?
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];

    // do whatever else you need before rotating toInterfaceOrientation

    // tell the layout to recalculate
    [self.collectionViewLayout invalidateLayout];
}

UICollectionViewFlowLayoutは、方向間のスクロール位置を維持します。同じセルが中央に配置されますonly正方形の場合。

36
Bob Wakefield

ここで2つの答えの組み合わせを使用して、私たちが実際にやろうとしていることは、コレクションビューをテーブルビューのように見せることですが、UITableViewを使用せず、単一の列でアイテムを取得することです(ほとんどの場合)

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    [self.collectionView.collectionViewLayout invalidateLayout];
}
8
cynistersix

@meelawshの回答のバリエーションですが、Swiftであり、self.sizeForCollectionViewプロパティはありません。

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    guard let collectionView = self.collectionView else {
        return
    }

    if size.width > collectionView.frame.width {
        // bigger

        let animation: (UIViewControllerTransitionCoordinatorContext)->(Void) = {
            context in
            collectionView.collectionViewLayout.invalidateLayout()
        }

        coordinator.animateAlongsideTransition(animation, completion: nil)
    } else {
        // smaller
        collectionView.collectionViewLayout.invalidateLayout()
    }
}
3
Kenny Winker

iOS8:私が警告を黙らせることができる唯一の方法は、大きなサイズに回転するときの移行中、および小さなサイズに回転するときの移行前に、無効化を呼び出すことです。

    self.sizeForCollectionView = size; //used to determine collectionview size in sizeForItem
    if (size.width <= size.height) {
        [self.collectionView.collectionViewLayout invalidateLayout];
    }

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        if (size.width > size.height) {
            [self.collectionView.collectionViewLayout invalidateLayout];
        }
    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
    }];
2
meelawsh

それが誰かに役立つかどうかはわかりませんが、UIViewsUIScrollViewに使用しているため、最後の答えは役に立ちません。

一部のイベントに応じて、プログラムでUIViewを開いています。したがって、- (void) willRotateToInterfaceOrientation:およびinvalidateLayoutまたはreloadDataを使用できません。
1。 invalidateLayout "次のビューレイアウト更新サイクル中に発生します。"
2。 reloadData "効率を上げるために、コレクションビューには、表示されているセルと補足ビューのみが表示されます。"

私にとってうまくいったのは、collectionViewの親(UIView)の幅を使用することでした:

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
int currentWidth = self.frame.size.width;

UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)collectionView.collectionViewLayout sectionInset];
int fixedWidth = currentWidth - (sectionInset.left + sectionInset.right);


何らかの理由で、collectionViewフレームには、この関数を呼び出すときに縦長の幅がありました... int currentWidth = self.frame.widthを使用して修正するのに役立ちます。

2
MiguelHincapieC

@ 0mahc0の答えは私には有効でしたが、部分的にしか機能しませんでした。

アプリを縦向きモードにするだけなので、回転の問題はありませんが、私の解決策は同じ問題を持つ他のユーザーを助けることができます。

警告は、ビューが表示されるたびに表示されます。警告の理由は非常に明確です。UICollectionViewの左右にセクションインセットを定義しました。インターフェースビルダーを確認すると、定義された値がメトリックに表示されます。

@ 0mahc0の解決策は機能しますが、セルを最大幅に引き伸ばしたいので、セクションのインセットを削除しました。そうしないと、左と右に「マージン」ができます。

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
    return UIEdgeInsetsZero;
}

デリゲートメソッドを実装する代わりに、インターフェイスビルダーに移動して、値を右インセットと左インセットのゼロに変更できます。

1
Pedro Romão