4

UICollectionView: やり方が間違っています。方法がわかりません。

マイセットアップ

iOS 6.0.1 を搭載した iPhone 4S でこれを実行しています。

私の目標

1 つのセクションが画像専用のテーブル ビューがあります。テーブル ビュー セクションのスクリーン ショット

ユーザーが [画像を追加...] セルをタップすると、フォト ライブラリから画像を選択するか、カメラで新しい画像を撮影するように求められます。アプリのその部分は正常に動作しているようです。

ユーザーが最初に画像を追加すると、「No Images」ラベルが 2 番目のテーブル セルから削除され、プログラムで作成された UICollectionView がその場所に追加されます。その部分もうまく機能しているようです。

コレクション ビューには、追加された画像が表示されるはずですが、ここで問題が発生します。(画像の数が増えるにつれてテーブルビューのセルを拡大するために、いくつかのフープをジャンプする必要があることはわかっています。まだそこまでではありません。)

コレクション ビューに項目を挿入しようとすると、例外がスローされます。それについては後で詳しく説明します。

マイコード

コレクション ビューのデリゲートおよびデータソースとしても機能するテーブル ビューを担当する UITableViewController を用意しました。関連するコードは次のとおりです (この問題とは無関係と思われるコントローラーのビットを省略しました。たとえば、 のようなメソッドの行も含まれます-viewDidLoad。関連性がないと思われるため、画像取得コードのほとんども省略しました)。

#define ATImageThumbnailMaxDimension 100

@interface ATAddEditActivityViewController ()
{
    UICollectionView* imageDisplayView;
    NSMutableArray* imageViews;
}

@property (weak, nonatomic) IBOutlet UITableViewCell *imageDisplayCell;
@property (weak, nonatomic) IBOutlet UILabel *noImagesLabel;
@end

@implementation ATAddEditActivityViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init];
    flowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;

    imageDisplayView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 290, 120) collectionViewLayout:flowLayout];  // The frame rect still needs tweaking
    imageDisplayView.delegate = self;
    imageDisplayView.dataSource = self;
    imageDisplayView.backgroundColor = [UIColor yellowColor];  // Just so I can see the actual extent of the view
    imageDisplayView.opaque = YES;
    [imageDisplayView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];

    imageViews = [NSMutableArray array];
}

#pragma mark - UIImagePickerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    /* ...code defining imageToSave omitted... */

    [self addImage:imageToSave toCollectionView:imageDisplayView];


    [self dismissModalViewControllerAnimated:YES];
}

#pragma mark - UICollectionViewDelegate
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

#pragma mark - UICollectionViewDatasource
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    [[cell  contentView] addSubview:imageViews[indexPath.row]];
    return cell;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return [imageViews count];
}

#pragma mark - UICollectionViewDelegateFlowLayout
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return ((UIImageView*)imageViews[indexPath.item]).bounds.size;
}

#pragma mark - Image Handling
- (void)addImage:(UIImage*)image toCollectionView:(UICollectionView*)cv
{
    if ([imageViews count] == 0)  {
        [self.noImagesLabel removeFromSuperview];
        [self.imageDisplayCell.contentView addSubview:cv];
    }

    UIImageView* imageView = [[UIImageView alloc] initWithImage:image];
    /* ...code that sets the bounds of the image view omitted... */

    [imageViews addObject:imageView];
    [cv insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:[imageViews count]-1 inSection:0]]];
    [cv reloadData];
}

@end

要約する:

  • コレクション ビューはインスタンス化され、-viewDidLoadメソッドで構成されます。
  • 選択された画像呼び出しを受け取る UIImagePickerDelegate メソッド-addImage:toCollectionView
  • ...画像を保持する新しい画像ビューを作成し、それをデータソース配列とコレクション ビューに追加します。これは、例外を生成する行です。
  • UICollectionView データソース メソッドは、imageViews 配列に依存しています。

例外

キャッチされない例外 'NSInternalInconsistencyException' が原因でアプリを終了しています。理由: '無効な更新: セクション 0 の項目数が無効です。更新後の既存のセクションに含まれる項目の数 (1) は、そのセクションに含まれる項目の数と等しくなければなりません更新前のセクション (1)、そのセクションから挿入または削除されたアイテムの数 (1 挿入、0 削除) のプラスまたはマイナス、およびそのセクションに移動したアイテムの数 (0 移動、0 移動)アウト)。'

私がこれを正しく解析している場合、これが私に伝えていることは、(真新しい!) コレクション ビューは、それが単一のアイテムで作成されたと考えているということです。したがって、-addImage:toCollectionViewこの理論をテストするためにログを追加しました。

NSLog(@"%d", [cv numberOfItemsInSection:0]);

その行があると、例外がスローされることはありません! への呼び出し-numberOfItemsInSection:は、コレクション ビューにそのデータ ソースを参照させ、アイテムがないことを認識させる必要があります。か何か。私はここで推測しています。しかし、まあ、まあ、この時点ではまだコレクション ビューにアイテムが表示されていないので、何か間違ったことをしていて、何が原因なのかわかりません。

結論は

  1. 新しく作成され挿入されたコレクション ビューにアイテムを追加しようとすると、奇妙な例外が発生します...-numberOfItemsInSection:挿入を試みる前に呼び出した場合を除きます。
  2. いかがわしい回避策で例外を回避できたとしても、アイテムはコレクション ビューに表示されません。

質問の小説ですみません。何か案は?

4

4 に答える 4

0

[セル数] が [実際のインデックス数] + [インデックスの挿入] と等しくないためです。場合によっては、dispatch_async に配列挿入データのブロックと insertItemsAtIndexPaths が含まれていないことがあります。時々クラッシュするという問題がありました。クラッシュのたびに発生するわけではありません。

于 2017-10-15T19:19:30.207 に答える
-1

推測: 最初の画像を挿入する時点では、コレクション ビューはまだデータをロードしていない可能性があります。ただし、例外メッセージでは、コレクション ビューは挿入前のアイテム数を「認識」していると主張しています (1)。したがって、データを遅延ロードしinsertItemsAtIndexPaths:、結果を「前」の状態として取得する可能性があります。また、挿入後にデータをリロードする必要はありません。

簡単に言えば、

[cv reloadData];

得るまで

if ([imageViews count] == 0)  {
    [self.noImagesLabel removeFromSuperview];
    [self.imageDisplayCell.contentView addSubview:cv];
    [cv reloadData];
}
于 2012-11-15T15:14:16.283 に答える