web-dev-qa-db-ja.com

UICollectionView:デリゲートメソッドで設定した後、特定のアイテムのアイテムサイズを取得する方法

新しいUICollectionViewクラスとUICollectionViewLayoutクラスをいじっています。 UICollectionViewFlowLayoutをサブクラス化して、カスタムレイアウトを作成しました。

セルサイズが動的に変化し、以下のデリゲートメソッドを使用してアイテムサイズを設定します

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout*)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

      NSLog(@"SETTING SIZE FOR ITEM AT INDEX %d", indexPath.row);
      return CGSizeMake(80, 80);
}

次に、カスタムUICollectionViewFlowLayoutクラスのprepareLayoutメソッドの下で、これらのサイズ変数にアクセスして、それらを配置し、layoutAttributesForItemAtIndexPathにキャッシュする方法を計算できるようにする必要があります。

ただし、UICollectionViewまたはUICollectionViewFlowLayoutの下に、デリゲートメソッドで設定したカスタムアイテムのサイズに到達するためのプロパティが見つからないようです。

12
SarpErdag

自分で見つけた。

UICollectionViewDelegateFlowLayoutを省略せずにカスタムクラスを実装する

@interface SECollectionViewCustomLayout : UICollectionViewFlowLayout 
                                          <UICollectionViewDelegateFlowLayout>

そして、あなたは呼び出すことができます

CGSize size = [self collectionView:self.collectionView 
                            layout:self 
            sizeForItemAtIndexPath:indexPath];
13
SarpErdag

さまざまな_UICollectionView..._ヘッダーファイルを見て、 WWDC 2012セッション219-高度なコレクションビューとカスタムレイアウトの構築 ビデオ(約6:50以降)を見ると、拡張可能なデリゲートパターンのようです動的型付けを利用して、レイアウトが拡張デリゲートメソッドに適切にアクセスできるようにします。


要するに...

  1. 独自のデリゲートを使用してカスタムレイアウトを定義する場合は、レイアウトのヘッダーファイルでそのデリゲートプロトコルを定義します。
  2. デリゲートオブジェクト(通常、コレクションビューを管理するUI(Collection)ViewController)は、このカスタムプロトコルをサポートするように宣言する必要があります。
    • レイアウトがUICollectionViewFlowLayoutまたはそのサブクラスである場合、これは単にUICollectionViewDelegateFlowLayoutへの準拠を宣言することを意味します。
    • レイアウトヘッダーをデリゲートのインターフェイスに_.m_したくない場合は、_#import_ファイルのクラス拡張でこれを自由に実行してください。
  3. レイアウトからデリゲートメソッドにアクセスするには、コレクションビューのデリゲートを呼び出します。
    • レイアウトのcollectionViewプロパティを使用し、必要なプロトコルに準拠したオブジェクトにデリゲートをキャストして納得させるコンパイラー
    • オプションのデリゲートメソッドを呼び出す前に、通常どおりデリゲート_respondsToSelector:_を確認することを忘れないでください。実際、必要に応じて、型キャストによってデリゲートが必要なメソッドを実装することさえ保証されるランタイムがないため、すべてのメソッドでこれを実行しても害はありません。


コードで...

したがって、情報の一部にデリゲートが必要なカスタムレイアウトを実装する場合、ヘッダーは次のようになります。

_@protocol CollectionViewDelegateCustomLayout <UICollectionViewDelegate>
- (BOOL)collectionView:(UICollectionView *)collectionView
                layout:(UICollectionViewLayout *)layout
shouldDoSomethingMindblowingAtIndexPath:(NSIndexPath *)indexPath;
@end

@interface CustomLayout : UICollectionViewLayout
// ...
@end
_


あなたのデリゲートは適合を宣言します(私はここの実装ファイルでそうしました):

_#import "CustomLayout.h"

@interface MyCollectionViewController () <CollectionViewDelegateCustomLayout>
@end

@implementation
// ...
- (BOOL)collectionView:(UICollectionView *)collectionView
                layout:(UICollectionViewLayout *)layout
shouldDoSomethingMindblowingAtIndexPath:(NSIndexPath *)indexPath
{
    return [self canDoSomethingMindblowing];
}
// ...
@end
_


そして、レイアウトの実装では、次のようにメソッドにアクセスします:

_BOOL blowMind;
if ([self.collectionView.delegate respondsToSelector:@selecor(collectionView:layout:shouldDoSomethingMindblowingAtIndexPath:)]) {
    blowMind = [(id<CollectionViewDelegateCustomLayout>)self.collectionView.delegate collectionView:self.collectionView
                                                                                             layout:self
                                                            shouldDoSomethingMindblowingAtIndexPath:indexPath];
} else {
    // Perhaps the layout also has a property for this, if the delegate
    // doesn't support dynamic layout properties...?
    // blowMind = self.blowMind;
}
_

とにかくデリゲートがそのメソッドに事前に応答することを確認しているので、ここでは型キャストしても安全です。


確たる証拠...

これは単なる推測にすぎませんが、AppleがUICollectionViewDelegateFlowLayoutプロトコルを管理する方法はこれだと思います。

  • フローレイアウトにはdelegateプロパティがないため、呼び出しはコレクションビューのデリゲートを経由する必要があります。
  • UICollectionViewControllerは、拡張フローレイアウトデリゲートに公に準拠していません(別のプライベートヘッダーでは準拠していないと思います)。
  • UICollectionViewdelegateプロパティは、 'base' UICollectionViewDelegateプロトコルへの準拠のみを宣言します。繰り返しますが、型キャストの必要性を防ぐために、フローレイアウトで使用されているUICollectionViewのプライベートサブクラス/カテゴリがあるとは思いません。この点にさらに重みを付けるには、Appleは、ドキュメント内のサブクラス化UICollectionViewをまったく推奨しません( iOSのコレクションビュープログラミングガイド:カスタムレイアウトの作成 ) :

UICollectionViewをサブクラス化しないでください。コレクションビューの外観はほとんどまたはまったくありません。代わりに、データソースオブジェクトからすべてのビューを取得し、レイアウトオブジェクトからすべてのレイアウト関連情報を取得します。

さあ、行きます。複雑ではありませんが、パラダイムに適した方法を実行する方法を知っておく価値はあります。

8
Stuart

Swiftバージョンがあります:

self.collectionView(self.collectionView, layout: self.collectionView.collectionViewLayout, sizeForItemAtIndexPath: indexPath)
6
tounaobun

GitHubで ICollectionView-FlowLayout を確認してください。同じ考え方ですが、これにより、flowLayoutの拡張デリゲートメソッドへのアクセスが少しクリーンになります。

4
Charlie Elliott

私の場合、レイアウト、セルレイアウトなどに関するすべてがUIViewControllerのnib内とUICollectionViewCellの個別のnib内で定義されています。 MyCollectionViewCellにはUIImageViewが含まれており、セルに自動レイアウトされ、パディング/マージンが付いていますが、四角形です。

四角形ではなく丸いアイコンが必要ですが、iPhoneとiPadのどちらのペン先を使用するか気にしたくありません(デバイス用と向き用のペン先が別にあります)。

ビューコントローラーに@selector(collectionView:layout:sizeForItemAtIndexPath:)を実装したくありません。

したがって、collectionView:cellForItemAtIndexPath:使用できます

CGSize size = cell.imageView.bounds.size;
cell.imageView.layer.masksToBounds = YES;
cell.imageView.layer.cornerRadius = size.height/2.0;

collectionView:layout:sizeForItemAtIndexPath:の前に呼び出すcollectionView:cellForItemAtIndexPath:とレイアウトが完了しました。

下の丸いアバターを確認できます

enter image description here

0
WINSergey

後の読者のために、IOS 7にはそれを定義したUICollectionViewFlowLayoutがあります。

0
tomjpsun