У меня есть UITableViewCell
с UISwitch
как аксессуар для каждой ячейки. Когда я изменяю значение переключателя в ячейке, как я могу узнать, в какой строке находится переключатель? Мне нужен номер строки в событии изменения значения переключателя.
Как узнать номер строки UITableview
Ответы (10)
Теги, подклассы или навигация по иерархии представлений - это слишком много работы!. Сделайте это в своем методе действий:
CGPoint hitPoint = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *hitIndex = [self.tableView indexPathForRowAtPoint:hitPoint];
Работает с любым типом представления, многосекционными таблицами, всем, что вы можете в него вложить - пока источник вашего отправителя находится в пределах фрейма ячейки (спасибо, rob!), Что обычно и бывает.
И вот оно в расширении UITableView Swift:
extension UITableView {
func indexPath(for view: UIView) -> IndexPath? {
let location = view.convert(CGPoint.zero, to: self)
return self.indexPathForRow(at: location)
}
}
sender
- person jrturton; 25.06.2013
terminating with uncaught exception of type NSException
Я знаю, что не использую быстрое расширение правильно. Если кто-то может внести свой вклад в расширение этого ответа и добавить, как его использовать, сделайте это.
- person Jesus Rodriguez; 29.01.2016
[sender center]
вместо CGPointZero
? Также не забудьте задействовать какие-либо подпредставления, sender
может быть обернут в вашем convertTo:
(в моем случае кнопка находится в скользящем UIView меньшего размера, чем родительская ячейка).
- person David Jirman; 06.04.2016
cell = button.superview.superview
, а теги бесполезны и должны погибнуть в огне.
- person jrturton; 10.01.2017
Если вы установите для свойства tag
номер строки (как предлагается в других ответах), вам придется обновлять его каждый раз в tableView:cellForRowAtIndexPath:
(поскольку ячейку можно повторно использовать для разных строк).
Вместо этого, когда вам нужен номер строки, вы можете пройти вверх по цепочке superview
от UISwitch
(или любого другого представления) к UITableViewCell
, а затем к UITableView
, и запросить у табличного представления путь индекса ячейки:
static NSIndexPath *indexPathForView(UIView *view) {
while (view && ![view isKindOfClass:[UITableViewCell class]])
view = view.superview;
if (!view)
return nil;
UITableViewCell *cell = (UITableViewCell *)view;
while (view && ![view isKindOfClass:[UITableView class]])
view = view.superview;
if (!view)
return nil;
UITableView *tableView = (UITableView *)view;
return [tableView indexPathForCell:cell];
}
В tableView:cellForRowAtIndexPath:
для этого ничего не требуется.
UISwitch
окажется за пределами его ячейки, что, по общему признанию, маловероятно.
- person rob mayoff; 14.02.2012
в cellForRowAtIndexPath:
установите для свойства tag
вашего элемента управления значение indexPath.row
Принятое решение - хитрый прием.
Однако зачем нам использовать хитпоинт, если мы можем использовать уже доступное свойство tag
в UIView? Можно сказать, что тег может хранить только строку или раздел, поскольку это единственный Int.
Что ж ... Не забывайте свои корни, ребята (CS101). Один Int может хранить два целых числа вдвое меньшего размера. А вот расширение для этого:
extension Int {
public init(indexPath: IndexPath) {
var marshalledInt: UInt32 = 0xffffffff
let rowPiece = UInt16(indexPath.row)
let sectionPiece = UInt16(indexPath.section)
marshalledInt = marshalledInt & (UInt32(rowPiece) << 16)
marshalledInt = marshalledInt + UInt32(sectionPiece)
self.init(bitPattern: UInt(marshalledInt))
}
var indexPathRepresentation: IndexPath {
let section = self & 0x0000ffff
let pattern: UInt32 = 0xffff0000
let row = (UInt32(self) & pattern) >> 16
return IndexPath(row: Int(row), section: Int(section))
}
}
Затем в вашем tableView(_:, cellForRowAt:)
вы можете:
cell.yourSwitch.tag = Int(indexPath: indexPath)
А затем в обработчике действий вы можете:
func didToogle(sender: UISwitch){
print(sender.tag.indexPathRepresentation)
}
Однако обратите внимание на это ограничение: строка и раздел не должны быть больше 65535. (UInt16.max)
Я сомневаюсь, что ваши индексы tableView будут такими высокими, но если они это сделают, бросьте вызов себе и реализуйте более эффективную схему упаковки. Скажем, если у нас есть очень маленький раздел, нам не нужны все 16 бит для представления раздела. У нас может быть наш int-макет, например:
{section area length}{all remaining}[4 BITS: section area length - 1]
то есть наши 4 LSB указывают длину области раздела - 1, при условии, что мы выделяем как минимум 1 бит для раздела. Таким образом, если наш раздел равен 0, строка может занимать до 27 бит ([1] [27] [4]), чего определенно должно быть достаточно.
Я предпочитаю использовать subviews, если вы знаете свой макет, он, как правило, очень простой и короткий в 1 строку ...
NSIndexPath *indexPath = [tableView indexPathForCell:(UITableViewCell *)[[sender superview] superview]];
То есть, если он более вложен, добавьте больше супервизоров.
Немного больше информации:
все, что вы делаете, - это запрос родительского представления и его родительского представления, которым является ячейка. Затем вы запрашиваете у своего tableview путь к индексу только что полученной ячейки.
Один из распространенных способов сделать это - установить tag
элемента управления (в вашем случае переключатель) на то, что можно использовать для идентификации строки или представленного объекта.
Например, в tableView:cellForRowAtIndexPath:
установите для свойства tag
переключателя значение indexPath.row
, а в своем методе действия вы можете получить тег от отправителя.
Лично мне такой подход не нравится, и я предпочитаю создать подкласс от UITableViewCell. Кроме того, может быть хорошей идеей добавить «смещение» к тегу, чтобы предотвратить любые конфликты с тегами других представлений.
Принятый ответ на этот пост совершенно нормален. Я хотел бы предложить читателям, что следующее, заимствованное из @robmayoff в этом посте, также совершенно нормально:
- (NSIndexPath *)indexPathForView:(UIView *)view inTableView:(UITableView *)tableView {
while (view && ![view isKindOfClass:[UITableViewCell class]])
view = view.superview;
UITableViewCell *cell = (UITableViewCell *)view;
return [tableView indexPathForCell:cell];
}
Некоторые утверждали, что этот подход содержит слишком много вычислительной работы из-за цикла while. Альтернатива, преобразование источника представления в координатное пространство табличного представления и вызов indexPathForRowAtPoint:
, скрывает еще больше работы.
Некоторые утверждали, что этот подход небезопасен по отношению к возможным изменениям SDK. Фактически, Apple уже однажды изменила иерархию ячеек tableview, добавив contentView
к ячейке. Этот подход работает до и после такого изменения. Пока предки представлений могут быть найдены через цепочку супервизоров (что так же важно, как и все в UIKit), это хороший код.
for (; view && ![view isKindOfClass:[UITableViewCell class]]; view = view.superview)
- person danh; 10.01.2017
Коллега предложил следующее, которое я сделал в категории UITableView:
+(UITableViewCell*)findParentCellForSubview:(UIView*)view
{
while (([view isKindOfClass:[UITableViewCell class]] == NO) && ([view superview] != nil))
view = [view superview];
if ([view superview] != nil)
return (UITableViewCell*)view;
return nil;
}
По-прежнему хакерски - но работает.
Еще один вариант использования superView. Работает как категория для UIView.
- (UITableViewCell *)superCell
{
if (!self.superview) {
return nil;
}
if ([self.superview isKindOfClass:[UITableViewCell class]]) {
return (UITableViewCell *)self.superview;
}
return [self.superview superCell];
}
я не знаю о нескольких разделах, но могу дать вам по одному разделу ...
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger index=indexPath.row;
NSString *string=[[NSString alloc]initWithFormat:@"%ld",(long)index];
}
отсюда вы можете получить номер строки и сохранить его в строке ....
tableView:cellForRowAtIndexPath:
, потому что ячейку можно повторно использовать для разных строк. - person rob mayoff   schedule 14.02.2012