Ссылка от UITableViewCell на родительский UITableView?

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

Ответы на вопрос(7)

который не зависит от какой-либо конкретной иерархии 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:] (выполнить для всех видимых ячеек), затраты времени выполнения должны быть незначительными.

 17 апр. 2014 г., 20:21
@CameronLowellPalmer Нет, это все еще отлично работает в iOS7 и будет работать в будущих выпусках iOS. Многие из наших приложений в магазине приложений все еще используют этот код без проблем, просто поместите его в нужное место, как это предлагается в комментариях к коду. Хранение ссылок повсюду не способствует чистому дизайну, и многие книги по дизайну предполагают, что этого следует избегать.
 24 апр. 2014 г., 02:41
@CameronLowellPalmer хороший дизайн ОО и, в частности, Закон Деметры, гласят, что «единицы должны иметь только ограниченные знания о других единицах и делать это только в случае крайней необходимости». Это должно способствовать слабому соединению блока и возможности его повторного использования. Сохранение ссылок на подпредставления на самом деле является нарушением LoD, а не обходом представлений в независимой от иерархии представлений форме. Последнее способствует слабому сцеплению и хорошему дизайну многократного использования OO, и на самом деле хорошо уважает LoD. Возможно, то, что вам не нравится, - это обход подпредставлений, опираясь на фиксированную жестко запрограммированную иерархию, что здесь не так.
 16 сент. 2013 г., 15:52
@mattcurtis Если вы обнаруживаете, что ходите на подпредставления, вы должны спросить себя, почему? И эта плохая идея лучше всего иллюстрируется изменением iOS 7 на UITableViewCell, которое будет всегда возвращать ноль. Слабая ссылка - это путь, если вам нужно взять табличное представление из ячейки.
 18 мая 2014 г., 20:43
@CameronLowellPalmer aUITableViewCell ограничено дизайномUIKit жить подUITableView, Находясь в поискеan экземпляр этогоUITableView вUIView hierarchy-agnostic Манера не вводит никаких дополнительных отношений.UITableViewCell затем говорят, чтоloosely coupled в Software Eng. Напротив, поддерживая ссылку наparticular UITableView экземпляр внутриUITableViewCell, считается тесной связью, потому что вводится прямая связь. Не случайно, что нетUITableViewCell.parentTable недвижимость предлагается в UIKit.
 23 апр. 2014 г., 02:58
@CameronLowellPalmer, если вы хотите, чтобы читатели учитывали ваше мнение, было бы полезно обосновать его. Утверждение идеи как догмы без иллюстрации проблемы не помогает никому оценить ее или извлечь из нее уроки. Приведенный выше код является общим и не тормозит, и я предоставил комментарии, чтобы показать плюсы и минусы подхода со слабыми ссылками. Если у вас есть что-то еще, пожалуйста, уточните. До сих пор мы видели, что вы не полностью поняли, как работает приведенный выше код, поскольку вы ошибочно утверждали, что он не работает в iOS7.
Решение Вопроса

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

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

 07 авг. 2014 г., 05:26
@JamesDonald Apple не добавила & quot; parentTableView & quot; либо метод, так что не очень понятно, что мотивировало Apple в любом случае. Во многих случаях ссылки на таблицу не требуются, поэтому добавление ссылки приведет к разбору байтов. Добавление ссылки может вызвать проблемы, поскольку люди склонны ссылаться на таблицу в ячейке. Конфигурирование ячейки может затем непреднамеренно сделать вызовы к таблице, когда ячейка используется повторно. Учитывая простоту кода, я иду со слабой ссылкой. Лучше использовать немного памяти, чем разряжать чью-то батарею, постоянно повышая иерархию.
 14 сент. 2013 г., 12:21
Да, они изменили это с IOS 7 :)
 31 авг. 2012 г., 06:03
на самом деле, это кажется немного хакерским, потому что если в какой-то конструкции таблицы сначала есть несколько контейнеров, и они содержат ячейки, то в этом случае,cell.superview может не быть таблицей, поэтому здесь делается предположение о реализации UITableView
 10 июл. 2009 г., 19:55
Чтобы быть в безопасности, вы можете добавить & apos; [self.superview isKindOfClass: [UITableView class]] & apos; проверьте, на случай, если иерархия представления изменится в будущем.
 17 апр. 2014 г., 20:01
Вы можете просматривать подпредставления, не полагаясь на какую-либо определенную иерархию представлений, как это предлагается вCategory ниже. Это гарантирует, что это будет работать в будущих версиях. Таким образом, вам не нужно разбрасывать ссылки по всему коду и увеличивать связывание объектов.

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

cell.h

 // interface
 id root;

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

cell.m

@synthesize root;

tableviewcontroller.m

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

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

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

 01 февр. 2013 г., 15:08
Да, слабый в ARC.
 31 авг. 2012 г., 06:19
@ безбумажный, так что в ARC этоweak, право?
 09 сент. 2011 г., 18:07
Вы действительно не хотите, чтобы это было сохранено. Табличное представление обычно владеет ячейкой. Это было бы лучше@property (nonatomic, assign) id root;

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

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

Cells

Если вы имеете дело с 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 итерациями такого размера на производительность вашего приложения, не делайте этого.

Models

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

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

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

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

Xcode 7 beta, Swift 2.0

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

TableViewCell class

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)

    }
}

Inside table view class that implements the DataSource and 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 соответственно.

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

 29 июл. 2015 г., 23:19
Отличное решение! Нужно больше голосов!
 08 мар. 2016 г., 23:46
Спасибо за этот ответ. Если я могу добавить, вы можете сэкономить проверку updateCallback на ноль, используя необязательные цепочки, как это:updateCallback?(updateList : true), Если обратный вызов установлен, вызов произойдет.
 30 дек. 2015 г., 21:54
Вы устанавливаете это?cell.updateCallback2 = UpdateCallback2?
 26 апр. 2016 г., 21:47
Очень полезное решение, решил мою проблему
 30 дек. 2015 г., 21:52
Я реализовал этот код для двух разных ячеек. В первой ячейке у меня есть updateCallback, для второй ячейки у меня есть updateCallback2. Я знаю, что все мои методы и переменные на 100% названы правильно, однако updateCallback2 равен нулю (updateCallback - это нормально). Что может быть причиной того, что это будет ноль?

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

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

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

UITableView *tv = (UITableView *) self.superview.superview;
UITableViewController *vc = (UITableViewController *) tv.dataSource;

Ваш ответ на вопрос