UICollectionCell для вызова метода в CollectionViewController

У меня есть стандартный контроллер представления, который содержит представление коллекции.

Я заполняю представление коллекции пользовательскими ячейками, созданными из пользовательского подкласса UICollectionViewCell.

  1. Какова наилучшая практика вызова метода в классе представления коллекции из класса ячейки представления коллекции

  2. Почему следующий init не вызывается в моем классе ячеек представления коллекции, я думал, что это будет вызываться, когда класс collectionView создает мои ячейки.

    (id)initWithFrame:(CGRect)frame

Причина, по которой я хочу это сделать, заключается в том, что я удаляю ячейку представления коллекции, функция удаления находится внутри класса ячейки представления коллекции. Я хочу воспроизвести звук, когда он удаляется, и предпочел бы инициализировать звук в классе контроллера представления коллекции, а не для каждой ячейки.


person DogCoffee    schedule 16.09.2013    source источник


Ответы (2)


По умолчанию UICollectionViewCell не имеет ссылки на UICollectionView, который его содержит. Поэтому, если вы хотите обмениваться данными из ячейки с представлением коллекции, вам нужно добавить свойство или ivar в ячейку.

cell = [UICollectionViewCell ...];
cell.collectionView = self.collectionView;

Во-вторых, при создании экземпляра UICollectionViewCell из пера initWithFrame: не вызывается; вместо этого вызывается initWithCoder:. Вы можете переопределить initWithCoder: (и вызвать super) или реализовать -awakeFromNib.

Иногда, если мне нужно реализовать два метода инициализации в одном классе (например, initWithFrame: и initWithCoder:), я делаю так: каждая реализация вызывает один метод с именем commonInit.

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self commonInit];
    } 
    return self;
}

- (id)initWithCoder:(NSCoder *)encoder
{
    self = [super initWithCoder:encoder];
    if (self) {
        [self commonInit];
    } 
    return self;
}

- (void)commonInit
{
// set up your instance
}

Это устраняет дублирование кода и обеспечивает согласованное поведение.

Ссылка: Документация UIView для initWithFrame:

person bneely    schedule 16.09.2013
comment
не используя перо, используя раскадровку, но имеете собственный класс ячеек, связанный с ячейкой представления коллекции? Или все так же? - person DogCoffee; 16.09.2013
comment
Я не использовал UICollectionView с раскадровкой, но вы можете переопределить как initWithFrame:, так и initWithCoder: и установить точки останова в каждом, чтобы увидеть, как инициализируются ваши ячейки. - person bneely; 16.09.2013
comment
-awakeFromNib действительно работает. Не был уверен, что есть лучший способ, чем установка iVar на ячейку. Спасибо, проблемы решены. Как и ваш код выше, который я буду использовать вместо этого. - person DogCoffee; 16.09.2013
comment
На самом деле очень плохая идея иметь ссылку из ячейки на collectionView, особенно в качестве iVar или свойства, особенно с сильной ссылкой. Это называется перекрестной ссылкой и приводит ко многим, возможно, серьезным проблемам. См. мой отдельный ответ, как определить делегата. - person auco; 04.10.2013

Вам действительно не следует добавлять обратную ссылку из ячейки в CollectionView как iVar или свойство, потому что вы получите действительно (действительно) плохую перекрестную ссылку (objA указывает на objB, а objB указывает на objA). Помимо незначительных проблем, таких как ошибки компиляции (которые могут быть плохо исправлены с помощью операторов @class), это также приводит к серьезным проблемам, таким как невыделяемые объекты, утечки памяти, зомби и так далее.

Эмпирическое правило:

родители знают о своих детях, но дети не должны знать о своих родителях.

Другими словами: CollectionView знает свои ячейки, но ячейки не должны знать свои CollectionView.

Возможные решения

  1. переделайте свою задачу и свое решение. Возможно, добавьте распознаватель жестов касания в collectionView, а не в ячейку. Существует -indexPathForItemAtPoint: чтобы дать вам выбранную ячейку.

  2. Если вам абсолютно необходима обратная ссылка: определите протокол и используйте делегат. Это распространенный шаблон проектирования в Cocoa/Cocoa Touch. Вы должны прочитать о шаблоне проектирования делегата, но вкратце, вот как вы это делаете:


определите протокол в вашей ячейке (помните, ячейка не знает о типе родителя, определяя этот протокол, она просто точно знает, что в «родителе» есть один или несколько методов)

// in cell.h
@protocol MyCellProtocol
- (IBAction)doSomething:(id)sender;
@end

добавьте делегат типа id (это означает, что это может быть любой объект, если он соответствует протоколу. Другими словами: это будет ваш collectionView, но вам не нужно иметь ссылку на него!

// in cell.h
@property (assign) id<MyCellProtocol> cellDelegate;

теперь вы можете вызвать делегата в своей ячейке:

// in cell.m, some method:
[self.cellDelegate doSomething:nil];

наконец, вам нужно установить делегата. Когда вы создаете/настраиваете свою ячейку в UICollectionViewController, установите свой контроллер в качестве делегата:

// in collectionViewController.m
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                  cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = 
      [self.collectionView dequeueReusableCellWithReuseIdentifier:@"myCell" 
                                                     forIndexPath:indexPath];
    cell.cellDelegate = (id<MyCellProtocol>)self;
person auco    schedule 04.10.2013