Ссылка от UITableViewCell на родительский UITableView? - PullRequest
67 голосов
/ 10 июля 2009

Есть ли способ получить доступ к владельцу UITableView из UITableViewCell?

Ответы [ 7 ]

116 голосов
/ 10 июля 2009

Сохраните ссылку weak на tableView в ячейке, которую вы установите в -tableView:cellForRowAtIndexPath: источника данных вашей таблицы.

Это лучше, чем полагаться на self.superview, чтобы всегда быть точно табличным представлением хрупким. Кто знает, как Apple может реорганизовать иерархию представлений UITableView в будущем.

18 голосов
/ 03 декабря 2012

Вот лучший способ сделать это, который не зависит от какой-либо конкретной иерархии UITableView. Он будет работать с любой будущей версией iOS, при условии, что UITableView не меняет имя класса вообще. Мало того, что это крайне маловероятно, но если это произойдет, вам все равно придется ретушировать код.

Просто импортируйте категорию ниже и получите ссылку с [myCell parentTableView]

@implementation UIView (FindUITableView)

-(UITableView *) parentTableView {
    // iterate up the view hierarchy to find the table containing this cell/view
    UIView *aView = self.superview;
    while(aView != nil) {
        if([aView isKindOfClass:[UITableView class]]) {
            return (UITableView *)aView;
        }
        aView = aView.superview;
    }
    return nil; // this view is not within a tableView
}

@end


// To use it, just import the category and invoke it like so:
UITableView *myTable = [myTableCell parentTableView];

// It can also be used from any subview within a cell, from example
// if you have a UILabel within your cell, you can also do:
UITableView *myTable = [myCellLabel parentTableView];

// NOTE:
// If you invoke this on a cell that is not part of a UITableView yet
// (i.e., on a cell that you just created with [[MyCell alloc] init]),
// then you will obviously get nil in return. You need to invoke this on cells/subviews
// that are already part of a UITableView.


UPDATE
В комментариях обсуждается, является ли сохранение слабой ссылки лучшим подходом. Это зависит от ваших обстоятельств. Обход иерархии представления приводит к небольшим потерям времени выполнения, пока вы выполняете цикл, пока не будет идентифицирован целевой UIView. Насколько глубоки ваши взгляды? С другой стороны, сохранение ссылки на каждую ячейку имеет минимальные потери памяти (слабая ссылка - указатель в конце концов), и вообще добавление отношений объекта, где они не нужны, считается плохой практикой проектирования ОО по многим причинам и должно следует избегать (подробности см. в комментариях ниже).

Что еще более важно, хранение ссылок на таблицы внутри ячеек увеличивает сложность кода и может привести к ошибкам, поскольку UITableViewCells можно использовать повторно. Не случайно, что UIKit не включает свойство cell.parentTable. Если вы определяете свой собственный, вы должны добавить код для управления им, а если вам не удастся сделать это эффективно, вы можете вызвать утечки памяти (т. Е. Ячейки живут дольше времени жизни своей таблицы).

Поскольку обычно вы будете использовать указанную выше категорию, когда пользователь взаимодействует с ячейкой (выполнить для одной ячейки), а не при размещении таблицы в [tableView:cellForRowAtIndexPath:] (выполнить для всех видимых ячеек), среда выполнения стоимость должна быть незначительной.

13 голосов
/ 14 июля 2015

Xcode 7 beta, Swift 2.0

Это прекрасно работает для меня, по моему мнению, это не имеет ничего общего с иерархией или чем-то еще До сих пор у меня не было проблем с этим подходом. Я использовал это для многих асинхронных обратных вызовов (например, когда выполняется запрос API).

TableViewCell класс

class ItemCell: UITableViewCell {

    var updateCallback : ((updateList: Bool)-> Void)? //add this extra var

    @IBAction func btnDelete_Click(sender: AnyObject) {
        let localStorage = LocalStorage()
        if let description = lblItemDescription.text
        {
            //I delete it here, but could be done at other class as well.
            localStorage.DeleteItem(description) 
        }
        updateCallback?(updateList : true)

    }
}

Класс табличного представления, реализующий DataSource и Delegate

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell: ItemCell = self.ItemTableView.dequeueReusableCellWithIdentifier("ItemCell") as! ItemCell!
    cell.updateCallback = UpdateCallback //add this extra line
    cell.lblItemDescription?.text = self.SomeList[indexPath.row].Description
    return cell
}

func UpdateCallback(updateTable : Bool) //add this extra method
{
    licensePlatesList = localStorage.LoadNotificationPlates()
    LicenseTableView.reloadData()
}

Конечно, вы можете поместить любую переменную в updateCallback и изменить ее функцию в tableView соответственно.

Кто-то, возможно, захочет сказать мне, если это сохранить для использования, просто чтобы быть уверенным.

7 голосов
/ 10 июля 2009

При создании ячейки табличного представления необходимо добавить ссылку обратно в UITableView.

Однако почти наверняка вам действительно нужна ссылка на ваш UITableViewController ... для которого требуется то же самое: установите его в качестве делегата ячейки при построении ячейки и передайте его в табличное представление.

Альтернативный подход, если вы подключаете действия, состоит в создании ячеек в IB с контроллером табличного представления в качестве владельца файлов - затем подключите кнопки в ячейке к действиям в контроллере табличного представления. Когда вы загружаете xib ячейки с помощью loadNibNamed, передайте контроллер представления в качестве владельца, и действия кнопки будут переданы обратно в контроллер представления таблицы.

4 голосов
/ 17 июля 2009

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

cell.h

 // interface
 id root;

 // propery 
 @property (nonatomic, retain) id root;

cell.m

@ синтезировать корень;

tableviewcontroller.m

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  // blah blah, traditional cell declaration
  // but before return cell;
  cell.root = tableView;
}

Теперь вы можете вызывать любой из методов таблицы в вашей ячейке, используя корневую переменную. (например, [root reloadData]);

Ах, возвращает меня к старым добрым временам флэш-программирования.

1 голос
/ 28 ноября 2014
UITableView *tv = (UITableView *) self.superview.superview;
UITableViewController *vc = (UITableViewController *) tv.dataSource;
1 голос
/ 30 апреля 2014

В других ответах есть два метода: (A) сохранить ссылку на таблицу или (B) просмотреть суперпредставления.

Я бы всегда использовал что-то вроде (A) для объектов модели и (B) для ячеек таблицы.

Клетка

Если вы имеете дело с UITableViewCell, то, AFAIK, вы должны либо иметь UITableView под рукой (скажем, вы используете метод делегата таблицы), либо иметь дело с видимой ячейкой в ​​иерархии представления. В противном случае вы вполне можете делать что-то не так (обратите внимание на «может хорошо»).

Ячейки используются повторно, и если у вас есть такой, который не виден, то единственная реальная причина существования ячейки - это оптимизация производительности iOS UITableView (более медленная версия iOS выпустила бы и, надеюсь, освободила ячейку, когда она переместился за пределы экрана) или потому что у вас есть конкретная ссылка на него. Я предполагаю, что это, вероятно, причина того, что ячейки таблицы не наделены методом экземпляра tableView.

Итак (B) дает правильный результат для всех iOS и всех будущих, пока они радикально не изменят работу представлений.

Хотя, чтобы избежать многократного написания обобщенного кода, я бы использовал это:

+ (id)enclosingViewOfView:(UIView *)view withClass:(Class)returnKindOfClass {
  while (view&&![view isKindOfClass:returnKindOfClass]) view=view.superview;
  return(view);
}

и удобный метод:

+ (UITableView *)tableForCell:(UITableViewCell *)cell {
  return([self enclosingViewOfView:cell.superview withClass:UITableView.class]);
}

(или категории, если хотите)

Кстати, если вас беспокоит влияние цикла с примерно 20 итерациями такого размера на производительность вашего приложения, не надо.

Модель

Если вы говорите об объекте модели, отображаемом в ячейке, то определенно эта модель может / должна знать о своей родительской модели, которую можно использовать для поиска или запуска изменений в таблице (таблицах), в которой модель ячейки может быть отображена в. Это похоже на (A), но менее хрупкое с будущими обновлениями iOS (например, однажды они могут заставить кэш повторного использования UITableViewCell существовать для каждого повторного идентификатора, а не для повторного идентификатора для табличного представления), в тот день все реализации, которые используют метод слабой ссылки, сломаются ).

Th модель метод будет использоваться для изменений данных, отображаемых в ячейке (т. Е. Изменения модели), поскольку изменения будут распространяться везде, где отображается модель (например, какой-то другой UIViewController где-то еще в приложении, ведение журнала, ...)

Метод ячейка будет использоваться для действий просмотра таблицы, что, вероятно, всегда будет плохой идеей, если ячейка даже не является представлением таблицы (хотя это ваш код, сходите с ума).

В любом случае, используйте модульный тест, а не предполагайте, что на первый взгляд более чистый код работает только при обновлении iOS.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...