web-dev-qa-db-ja.com

サイズ変更時にコレクションビューのレイアウトを無効にする適切な方法

コレクションビューの境界に基づいてアイテムのサイズが変更されるコレクションビューを実装しています。したがって、デバイスの回転などによってサイズが変更された場合は、レイアウトを無効にして、新しいコレクションビューの境界を考慮してセルのサイズを変更する必要があります。私はこれをviewWillTransitionToSizeAPIを介して行いました。

これは、ユーザーがコレクションビューを含むビューコントローラー上にモーダルビューコントローラーを提示し、デバイスを回転させてから閉じるまではうまく機能します。その場合、アイテムのサイズは適切なサイズに更新されていません。 viewWillTransitionToSizeが呼び出され、期待どおりにレイアウトが無効になりますが、コレクションビューの境界は古い値のままです。たとえば、縦向きから横向きに回転する場合、コレクションビューの境界値の高さは幅よりも大きくなります。なぜそうなるのかわかりませんが、サイズ変更時に無効にするのにこれが最善の方法かどうか疑問に思っています。それを行うためのより良い方法はありますか?

また、UICollectionViewFlowLayoutをサブクラス化してshouldInvalidateLayoutForBoundsChangeをオーバーライドしてYESを返すことも試みましたが、何らかの理由で、モーダルプレゼンテーションなしで回転しても機能しません。また、適切なコレクションビュー境界も使用しません。

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

    [self.collectionView.collectionViewLayout invalidateLayout];

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  __nonnull context) {
        [self.collectionView.collectionViewLayout invalidateLayout];
    } completion:^(id<UIViewControllerTransitionCoordinatorContext>  __nonnull context) {
        //finished
    }];
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    //collectionView.bounds.size is not always correct here after invalidating layout as explained above
}

また、完了ブロックで無効にしようとしましたが、それでも適切なコレクションビュー境界を使用していません。

viewWillAppearでレイアウトを再度無効にすると、適切なコレクションビュー境界が使用され、モーダルに表示されたビューコントローラーで回転する際の問題が解決されます。しかし、これは必要ではないはずです。おそらく、これが適切なサイズに設定されない他の状況があります。

10
Jordan H

私は問題が何であるかを知っています。 invalidateLayout内(アニメーションブロックまたは完了ブロックのいずれか)でanimateAlongsideTransitionを呼び出すと、モーダルビューコントローラーが全画面に表示されている場合、実際にはレイアウトが再計算されません(ただし、それが現在のコンテキストを超えている場合はそうなります)。しかし、私が行っていたようにアニメーションブロックの外で無効にすると、無効になります。ただし、その時点では、コレクションビューは新しいサイズにレイアウトされていません。これが、その境界に古い値が表示されていた理由を説明しています。この動作の理由は、invalidateLayoutによってレイアウトがすぐに再計算されるわけではなく、次のレイアウトパス中に発生するようにスケジュールされているためです。このレイアウトパスは、全画面にモーダルで表示されるViewControllerがある場合は発生しません。レイアウトパスを強制するには、[self.collectionView layoutIfNeeded];の直後にanimateAlongsideTransitionブロック内で[self.collectionView.collectionViewLayout invalidateLayout];を呼び出すだけです。これにより、レイアウトが期待どおりに再計算されます。

24
Jordan H