Обнаружение, которое UIButton было нажато в UITableView
У меня есть UITableView
с 5 UITableViewCells
. Каждая ячейка содержит UIButton
, которая настраивается следующим образом:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = @"identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
[cell autorelelase];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
[button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
[button setTag:1];
[cell.contentView addSubview:button];
[button release];
}
UIButton *button = (UIButton *)[cell viewWithTag:1];
[button setTitle:@"Edit" forState:UIControlStateNormal];
return cell;
}
Мой вопрос таков: в методе buttonPressedAction:
, как узнать, какая кнопка была нажата. Я рассмотрел использование тегов, но я не уверен, что это лучший маршрут. Я хотел бы как-то пометить indexPath
на элементе управления.
- (void)buttonPressedAction:(id)sender
{
UIButton *button = (UIButton *)sender;
// how do I know which button sent this message?
// processing button press for this row requires an indexPath.
}
Какой стандартный способ сделать это?
Edit:
Я как-то решил это, выполнив следующее. Я все же хотел бы получить мнение, является ли это стандартным способом его выполнения или есть лучший способ?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = @"identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
[cell autorelelase];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
[button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button];
[button release];
}
UIButton *button = (UIButton *)[cell.contentView.subviews objectAtIndex:0];
[button setTag:indexPath.row];
[button setTitle:@"Edit" forState:UIControlStateNormal];
return cell;
}
- (void)buttonPressedAction:(id)sender
{
UIButton *button = (UIButton *)sender;
int row = button.tag;
}
Важно отметить, что я не могу установить тег при создании ячейки, поскольку ячейка может быть удалена. Он чувствует себя очень грязным. Должен быть лучший способ.
Ответы
Ответ 1
В Apple Accessory используется следующий метод:
[button addTarget:self action:@selector(checkButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
Затем в полученном координате касания обработчик касания и путь указателя вычисляется из этой координаты:
- (void)checkButtonTapped:(id)sender
{
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
if (indexPath != nil)
{
...
}
}
Ответ 2
Я нашел метод использования супервизора супервизора для получения ссылки на ячейку indexPath работал отлично. Благодаря iphonedevbook.com(macnsmith) для подсказки текст ссылки
-(void)buttonPressed:(id)sender {
UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
...
}
Ответ 3
Вот как я это делаю. Простой и лаконичный:
- (IBAction)buttonTappedAction:(id)sender
{
CGPoint buttonPosition = [sender convertPoint:CGPointZero
toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
...
Ответ 4
Нашел хорошее решение этой проблемы в другом месте, не путаясь с тегами на кнопке:
- (void)buttonPressedAction:(id)sender {
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];
do stuff with the indexPath...
}
Ответ 5
Как отправить информацию, подобную NSIndexPath
в UIButton
, используя инъекцию времени выполнения.
1) Для импорта требуется время выполнения
2) добавить статическую константу
3) добавьте NSIndexPath
к вашей кнопке во время выполнения, используя:
(void) setMetaData: (id) target withObject: (id) newObj
4) при нажатии кнопки получить метаданные, используя:
(ID) Metadata: (ID) целевой
Enjoy
#import <objc/runtime.h>
static char const * const kMetaDic = "kMetaDic";
#pragma mark - Getters / Setters
- (id)metaData:(id)target {
return objc_getAssociatedObject(target, kMetaDic);
}
- (void)setMetaData:(id)target withObject:(id)newObj {
objc_setAssociatedObject(target, kMetaDic, newObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#On the cell constructor
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
....
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
....
[btnSocial addTarget:self
action:@selector(openComments:)
forControlEvents:UIControlEventTouchUpInside];
#add the indexpath here or another object
[self setMetaData:btnSocial withObject:indexPath];
....
}
#The action after button been press:
- (IBAction)openComments:(UIButton*)sender{
NSIndexPath *indexPath = [self metaData:sender];
NSLog(@"indexPath: %d", indexPath.row);
//Reuse your indexpath Now
}
Ответ 6
Чтобы сделать (@Vladimir) ответ Swift:
var buttonPosition = sender.convertPoint(CGPointZero, toView: self.tableView)
var indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)!
Хотя проверка на indexPath != nil
дает мне палец... "NSIndexPath не является подтипом NSString"
Ответ 7
func buttonAction(sender:UIButton!)
{
var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tablevw)
let indexPath = self.tablevw.indexPathForRowAtPoint(position)
let cell: TableViewCell = tablevw.cellForRowAtIndexPath(indexPath!) as TableViewCell
println(indexPath?.row)
println("Button tapped")
}
Ответ 8
Я бы использовал свойство тега, как вы сказали, установив тег следующим образом:
[button setTag:indexPath.row];
затем получив тег внутри кнопки ButtonPressedAction следующим образом:
((UIButton *)sender).tag
или
UIButton *button = (UIButton *)sender;
button.tag;
Ответ 9
Хотя мне нравится способ тегов... если вы не хотите использовать теги по какой-либо причине,
вы могли бы создать элемент NSArray
готовых кнопок:
NSArray* buttons ;
затем создайте эти кнопки перед рендерингом tableView и вставьте их в массив.
Затем внутри функции tableView:cellForRowAtIndexPath:
вы можете:
UIButton* button = [buttons objectAtIndex:[indexPath row] ] ;
[cell.contentView addSubview:button];
Затем в функции buttonPressedAction:
вы можете сделать
- (void)buttonPressedAction:(id)sender {
UIButton* button = (UIButton*)sender ;
int row = [buttons indexOfObject:button] ;
// Do magic
}
Ответ 10
ОБРАТИТЬ СЕКЦИИ - я сохранил NSIndexPath в пользовательском UITableViewCell
В CLKIndexPricesHEADERTableViewCell.xib
IN IB Добавить UIButton в XIB - DONT добавить действие!
Добавить выход @property (сохранить, неатомный) IBOutlet UIButton * buttonIndexSectionClose;
НЕ CTRL + DRAG действие в IB (сделано в коде ниже)
@interface CLKIndexPricesHEADERTableViewCell : UITableViewCell
...
@property (retain, nonatomic) IBOutlet UIButton *buttonIndexSectionClose;
@property (nonatomic, retain) NSIndexPath * indexPathForCell;
@end
В viewForHeaderInSection (также должен работать для cellForRow.... и т.д., если в таблице есть только 1 раздел)
- viewForHeaderInSection is called for each section 1...2...3
- get the cell CLKIndexPricesHEADERTableViewCell
- getTableRowHEADER just does the normal dequeueReusableCellWithIdentifier
- STORE the indexPath IN the UITableView cell
- indexPath.section = (NSInteger)section
- indexPath.row = 0 always (we are only interested in sections)
- (UIView *) tableView:(UITableView *)tableView1 viewForHeaderInSection:(NSInteger)section {
//Standard method for getting a UITableViewCell
CLKIndexPricesHEADERTableViewCell * cellHEADER = [self getTableRowHEADER];
... используйте раздел для получения данных для вашей ячейки
... заполните его
indexName = ffaIndex.routeCode;
indexPrice = ffaIndex.indexValue;
//
[cellHEADER.buttonIndexSectionClose addTarget:self
action:@selector(buttonDELETEINDEXPressedAction:forEvent:)
forControlEvents:UIControlEventTouchUpInside];
cellHEADER.indexPathForCell = [NSIndexPath indexPathForRow:0 inSection:section];
return cellHEADER;
}
USER нажимает кнопку DELETE на заголовке раздела, и это вызывает
- (void)buttonDELETEINDEXPressedAction:(id)sender forEvent:(UIEvent *)event
{
NSLog(@"%s", __PRETTY_FUNCTION__);
UIView * parent1 = [sender superview]; // UiTableViewCellContentView
//UIView *myContentView = (UIView *)parent1;
UIView * parent2 = [parent1 superview]; // custom cell containing the content view
//UIView * parent3 = [parent2 superview]; // UITableView containing the cell
//UIView * parent4 = [parent3 superview]; // UIView containing the table
if([parent2 isMemberOfClass:[CLKIndexPricesHEADERTableViewCell class]]){
CLKIndexPricesHEADERTableViewCell *myTableCell = (CLKIndexPricesHEADERTableViewCell *)parent2;
//UITableView *myTable = (UITableView *)parent3;
//UIView *mainView = (UIView *)parent4;
NSLog(@"%s indexPath.section,row[%d,%d]", __PRETTY_FUNCTION__, myTableCell.indexPathForCell.section,myTableCell.indexPathForCell.row);
NSString *key = [self.sortedKeysArray objectAtIndex:myTableCell.indexPathForCell.section];
if(key){
NSLog(@"%s DELETE object at key:%@", __PRETTY_FUNCTION__,key);
self.keyForSectionIndexToDelete = key;
self.sectionIndexToDelete = myTableCell.indexPathForCell.section;
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Remove Index"
message:@"Are you sure"
delegate:self
cancelButtonTitle:@"No"
otherButtonTitles:@"Yes", nil];
alertView.tag = kALERTVIEW_REMOVE_ONE_INDEX;
[alertView show];
[alertView release];
//------
}else{
NSLog(@"ERROR: [%s] key is nil for section:%d", __PRETTY_FUNCTION__,myTableCell.indexPathForCell.section);
}
}else{
NSLog(@"ERROR: [%s] CLKIndexPricesHEADERTableViewCell not found", __PRETTY_FUNCTION__);
}
}
В этом примере я добавил кнопку "Удалить", поэтому должен показать UIAlertView, чтобы подтвердить это.
Я храню раздел и ключ в словаре, хранящем информацию о разделе в ivar в VC
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if(alertView.tag == kALERTVIEW_REMOVE_ONE_INDEX){
if(buttonIndex==0){
//NO
NSLog(@"[%s] BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
//do nothing
}
else if(buttonIndex==1){
//YES
NSLog(@"[%s] BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
if(self.keyForSectionIndexToDelete != nil){
//Remove the section by key
[self.indexPricesDictionary removeObjectForKey:self.keyForSectionIndexToDelete];
//sort the keys so sections appear alphabetically/numbericsearch (minus the one we just removed)
[self updateTheSortedKeysArray];
//Delete the section from the table using animation
[self.tableView beginUpdates];
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:self.sectionIndexToDelete]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
//required to trigger refresh of myTableCell.indexPathForCell else old values in UITableViewCells
[self.tableView reloadData];
}else{
NSLog(@"ERROR: [%s] OBJECT is nil", __PRETTY_FUNCTION__);
}
}
else {
NSLog(@"ERROR: [%s] UNHANDLED BUTTON:%d", __PRETTY_FUNCTION__,buttonIndex);
}
}else {
NSLog(@"ERROR: [%s] unhandled ALERTVIEW TAG:%d", __PRETTY_FUNCTION__,alertView.tag);
}
}
Ответ 11
A better way would be to subclass your button and add a indexPath property to it.
//Implement a subclass for UIButton.
@interface NewButton:UIButton
@property(nonatomic, strong) NSIndexPath *indexPath;
Make your button of type NewButton in the XIB or in the code whereever you are initializing them.
Then in the cellForRowAtIndexPath put the following line of code.
button.indexPath = indexPath;
return cell; //As usual
Now in your IBAction
-(IBAction)buttonClicked:(id)sender{
NewButton *button = (NewButton *)sender;
//Now access the indexPath by buttons property..
NSIndexPath *indexPath = button.indexPath; //:)
}
Ответ 12
Это работает и для меня, спасибо @Cocoanut
Я нашел метод использования супервизора супервизора для получения ссылки на ячейку indexPath работал отлично. Благодаря iphonedevbook.com(macnsmith) для текста ссылки с текстом
-(void)buttonPressed:(id)sender {
UITableViewCell *clickedCell = (UITableViewCell *)[[sender superview] superview];
NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
...
}
Ответ 13
Я всегда использую теги.
Вам нужно подклассифицировать UITableviewCell
и отредактировать кнопку нажатием оттуда.
Ответ 14
вы можете использовать шаблон тега:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = @"identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
[cell autorelelase];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 5, 40, 20)];
[button addTarget:self action:@selector(buttonPressedAction:) forControlEvents:UIControlEventTouchUpInside];
[button setTag:[indexPath row]]; //use the row as the current tag
[cell.contentView addSubview:button];
[button release];
}
UIButton *button = (UIButton *)[cell viewWithTag:[indexPath row]]; //use [indexPath row]
[button setTitle:@"Edit" forState:UIControlStateNormal];
return cell;
}
- (void)buttonPressedAction:(id)sender
{
UIButton *button = (UIButton *)sender;
//button.tag has the row number (you can convert it to indexPath)
}
Ответ 15
Я что-то упустил? Не можете ли вы использовать отправителя для идентификации кнопки. Отправитель даст вам такую информацию:
<UIButton: 0x4b95c10; frame = (246 26; 30 30); opaque = NO; tag = 104; layer = <CALayer: 0x4b95be0>>
Затем, если вы хотите изменить свойства кнопки, скажите фоновое изображение, которое вы просто скажете отправителю:
[sender setBackgroundImage:[UIImage imageNamed:@"new-image.png"] forState:UIControlStateNormal];
Если вам нужен тег, то метод ACBurk в порядке.
Ответ 16
// how do I know which button sent this message?
// processing button press for this row requires an indexPath.
Довольно просто:
- (void)buttonPressedAction:(id)sender
{
UIButton *button = (UIButton *)sender;
CGPoint rowButtonCenterInTableView = [[rowButton superview] convertPoint:rowButton.center toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:rowButtonCenterInTableView];
MyTableViewItem *rowItem = [self.itemsArray objectAtIndex:indexPath.row];
// Now you're good to go.. do what the intention of the button is, but with
// the context of the "row item" that the button belongs to
[self performFooWithItem:rowItem];
}
Хорошо работает для меня: P
если вы хотите настроить настройку целевого действия, вы можете включить параметр события в метод, а затем использовать касания этого события для разрешения координат касания. Координаты все же должны быть разрешены в границах сенсорного экрана, но для некоторых людей это может показаться легким.
Ответ 17
создать nsmutable массив и поместить все кнопки в этот массив usint [array addObject: yourButton];
в методе нажатия кнопки
-
(void)buttonPressedAction:(id)sender
{
UIButton *button = (UIButton *)sender;
for(int i=0;i<[yourArray count];i++){
if([buton isEqual:[yourArray objectAtIndex:i]]){
//here write wat u need to do
}
}
Ответ 18
Небольшая вариация ответа Cocoanuts (что помогло мне это решить), когда кнопка была в нижнем колонтитуле таблицы (что мешает вам найти "щелкнув по ячейке":
-(IBAction) buttonAction:(id)sender;
{
id parent1 = [sender superview]; // UiTableViewCellContentView
id parent2 = [parent1 superview]; // custom cell containing the content view
id parent3 = [parent2 superview]; // UITableView containing the cell
id parent4 = [parent3 superview]; // UIView containing the table
UIView *myContentView = (UIView *)parent1;
UITableViewCell *myTableCell = (UITableViewCell *)parent2;
UITableView *myTable = (UITableView *)parent3;
UIView *mainView = (UIView *)parent4;
CGRect footerViewRect = myTableCell.frame;
CGRect rect3 = [myTable convertRect:footerViewRect toView:mainView];
[cc doSomethingOnScreenAtY:rect3.origin.y];
}
Ответ 19
Это просто; сделайте пользовательскую ячейку и отпустите кнопку
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = @"identifier";
customCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
cell.yourButton.tag = indexPath.Row;
- (void)buttonPressedAction:(id)sender
изменить идентификатор в указанном выше методе на (UIButton *)
Вы можете получить значение, которое используется для нажатия кнопки sender.tag.
Ответ 20
Подкласс кнопки для сохранения требуемого значения, возможно, создаст протокол (ControlWithData или что-то еще). Установите значение при добавлении кнопки в ячейку таблицы. В вашем событии касания проверьте, подчиняется ли отправитель протоколу и извлекает данные. Обычно я сохраняю ссылку на фактический объект, который отображается в ячейке представления таблицы.
Ответ 21
Обратите внимание, что я использую пользовательскую ячейку, этот код отлично работает для меня
@IBAction func call(sender: UIButton)
{
var contentView = sender.superview;
var cell = contentView?.superview as EmployeeListCustomCell
if (!(cell.isKindOfClass(EmployeeListCustomCell)))
{
cell = (contentView?.superview)?.superview as EmployeeListCustomCell
}
let phone = cell.lblDescriptionText.text!
//let phone = detailObject!.mobile!
let url:NSURL = NSURL(string:"tel://"+phone)!;
UIApplication.sharedApplication().openURL(url);
}
Ответ 22
ОБНОВЛЕНИЕ SWIFT 2
Здесь, как узнать, какая кнопка была нажата + отправить данные в другой ViewController с этой кнопки indexPath.row
, поскольку я предполагаю, что точка для большинства!
@IBAction func yourButton(sender: AnyObject) {
var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(position)
let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as
UITableViewCell
print(indexPath?.row)
print("Tap tap tap tap")
}
Для тех, кто использует класс ViewController и добавил tableView, я использую ViewController вместо TableViewController, поэтому я вручную добавил tableView для доступа к нему.
Вот код для передачи данных другому VC при нажатии этой кнопки и передаче ячейки indexPath.row
@IBAction func moreInfo(sender: AnyObject) {
let yourOtherVC = self.storyboard!.instantiateViewControllerWithIdentifier("yourOtherVC") as! YourOtherVCVIewController
var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(position)
let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as
UITableViewCell
print(indexPath?.row)
print("Button tapped")
yourOtherVC.yourVarName = [self.otherVCVariable[indexPath!.row]]
self.presentViewController(yourNewVC, animated: true, completion: nil)
}
Ответ 23
Решение Криса Швердта, но затем в Свифте работало для меня:
@IBAction func rateButtonTapped(sender: UIButton) {
let buttonPosition : CGPoint = sender.convertPoint(CGPointZero, toView: self.ratingTableView)
let indexPath : NSIndexPath = self.ratingTableView.indexPathForRowAtPoint(buttonPosition)!
print(sender.tag)
print(indexPath.row)
}
Ответ 24
Эта проблема состоит из двух частей:
1) Получение указательного пути UITableViewCell
, который содержит нажатый UIButton
Есть несколько предложений вроде:
-
Обновление UIButton
tag
в cellForRowAtIndexPath:
с использованием параметра index path row
. Это нехорошее решение, так как оно требует обновления tag
непрерывно, и оно не работает с представлениями таблиц с несколькими разделами.
-
Добавление свойства NSIndexPath
в пользовательскую ячейку и обновление его вместо UIButton
tag
в cellForRowAtIndexPath:
. Это решает проблему с несколькими разделами, но все еще не очень хорошо, так как требует обновления.
-
Сохранение слабой привязки к родительскому UITableView
в пользовательской ячейке при ее создании и использование метода indexPathForCell:
для получения указателя. Кажется немного лучше, нет необходимости обновлять что-либо в методе cellForRowAtIndexPath:
, но по-прежнему требует установки слабой ссылки при создании пользовательской ячейки.
-
Использование свойства cell superView
для получения ссылки на родительский UITableView
. Не нужно добавлять какие-либо свойства в пользовательскую ячейку, и нет необходимости устанавливать/обновлять что-либо при создании/позже. Но cell superView
зависит от деталей реализации iOS. Поэтому его нельзя использовать напрямую.
Но это может быть достигнуто с помощью простого цикла, так как мы уверены, что соответствующая ячейка должна находиться в UITableView:
UIView* view = self;
while (view && ![view isKindOfClass:UITableView.class])
view = view.superview;
UITableView* parentTableView = (UITableView*)view;
Таким образом, эти предложения могут быть объединены в простой и безопасный метод пользовательских ячеек для получения указательного пути:
- (NSIndexPath *)indexPath
{
UIView* view = self;
while (view && ![view isKindOfClass:UITableView.class])
view = view.superview;
return [(UITableView*)view indexPathForCell:self];
}
С этого момента этот метод может быть использован для обнаружения нажатия UIButton
.
2) Информирование других участников о событии нажатия кнопки
После того, как внутренне известно, какая UIButton
нажата, в какой пользовательской ячейке с точным маршрутом указателя, эту информацию нужно отправить другим сторонам (скорее всего, диспетчер представлений обрабатывает UITableView
). Таким образом, это событие нажатия кнопки может обрабатываться с аналогичным абстракционным и логическим уровнем до didSelectRowAtIndexPath:
метода делегата UITableView.
Для этого можно использовать два подхода:
a) Делегирование: Пользовательская ячейка может иметь свойство delegate
и может определять протокол. Когда кнопка нажата, она просто выполняет ее делегирование на свойстве delegate
. Но это свойство delegate
должно быть установлено для каждой настраиваемой ячейки при их создании. В качестве альтернативы, пользовательская ячейка может выбрать для выполнения своих методов делегирования в нем также представление родительской таблицы delegate
.
b) Центр уведомлений: пользовательские ячейки могут определять имя пользовательского уведомления и отправлять это уведомление с информацией об указателе и родительской таблице, представленной в объекте userInfo
. Не нужно ничего устанавливать для каждой ячейки, достаточно добавить наблюдателя для пользовательского уведомления ячейки.
Ответ 25
Я использую решение подкласса UIButton
, и я думал, что должен просто поделиться им здесь, коды в Swift:
class ButtonWithIndexPath : UIButton {
var indexPath:IndexPath?
}
Тогда не забудьте обновить его indexPath в cellForRow(at:)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let returnCell = tableView.dequeueReusableCell(withIdentifier: "cellWithButton", for: indexPath) as! cellWithButton
...
returnCell.button.indexPath = IndexPath
returnCell.button.addTarget(self, action:#selector(cellButtonPressed(_:)), for: .touchUpInside)
return returnCell
}
Поэтому, отвечая на событие кнопки, вы можете использовать его как
func cellButtonPressed(_ sender:UIButton) {
if sender is ButtonWithIndexPath {
let button = sender as! ButtonWithIndexPath
print(button.indexPath)
}
}
Ответ 26
В Swift 4.2 и iOS 12 вы можете выбрать один из следующих полных примеров, чтобы решить вашу проблему.
# 1. Используя UIView
convert(_:to:)
и UITableview
indexPathForRow(at:)
import UIKit
private class CustomCell: UITableViewCell {
let button = UIButton(type: .system)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
button.setTitle("Tap", for: .normal)
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
import UIKit
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.button.addTarget(self, action: #selector(customCellButtonTapped), for: .touchUpInside)
return cell
}
@objc func customCellButtonTapped(_ sender: UIButton) {
let point = sender.convert(CGPoint.zero, to: tableView)
guard let indexPath = tableView.indexPathForRow(at: point) else { return }
print(indexPath)
}
}
# 2. Используя UIView
convert(_:to:)
и UITableview
indexPathForRow(at:)
(альтернатива)
Это альтернатива предыдущему примеру, где мы передаем nil
target
параметру в addTarget(_:action:for:)
. Таким образом, если первый респондент не реализует действие, оно будет отправлено следующему респонденту в цепочке респондентов до тех пор, пока не будет найдена правильная реализация.
import UIKit
private class CustomCell: UITableViewCell {
let button = UIButton(type: .system)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
button.setTitle("Tap", for: .normal)
button.addTarget(nil, action: #selector(TableViewController.customCellButtonTapped), for: .touchUpInside)
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
import UIKit
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
return cell
}
@objc func customCellButtonTapped(_ sender: UIButton) {
let point = sender.convert(CGPoint.zero, to: tableView)
guard let indexPath = tableView.indexPathForRow(at: point) else { return }
print(indexPath)
}
}
# 3. Использование UITableview
indexPath(for:)
и шаблона делегата
В этом примере мы устанавливаем контроллер представления в качестве делегата ячейки. При нажатии кнопки ячейки запускается вызов соответствующего метода делегата.
import UIKit
protocol CustomCellDelegate: AnyObject {
func customCellButtonTapped(_ customCell: CustomCell)
}
class CustomCell: UITableViewCell {
let button = UIButton(type: .system)
weak var delegate: CustomCellDelegate?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
button.setTitle("Tap", for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func buttonTapped(sender: UIButton) {
delegate?.customCellButtonTapped(self)
}
}
import UIKit
class TableViewController: UITableViewController, CustomCellDelegate {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.delegate = self
return cell
}
// MARK: - CustomCellDelegate
func customCellButtonTapped(_ customCell: CustomCell) {
guard let indexPath = tableView.indexPath(for: customCell) else { return }
print(indexPath)
}
}
# 4. Использование UITableview
indexPath(for:)
и закрытие для делегирования
Это альтернатива предыдущему примеру, где мы используем замыкание вместо объявления протокола-делегата для обработки нажатия кнопки.
import UIKit
class CustomCell: UITableViewCell {
let button = UIButton(type: .system)
var buttontappedClosure: ((CustomCell) -> Void)?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
button.setTitle("Tap", for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
button.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1).isActive = true
button.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func buttonTapped(sender: UIButton) {
buttontappedClosure?(self)
}
}
import UIKit
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.buttontappedClosure = { [weak tableView] cell in
guard let indexPath = tableView?.indexPath(for: cell) else { return }
print(indexPath)
}
return cell
}
}