UIButton в ячейке UITableView, например Удалить событие

Я хочу добавить кнопку в ячейку таблицы. Меня вдохновила функция «Удалить событие» в приложении календаря... (похожий случай — «Поделиться контактом» в контактах)

На данный момент есть

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  //..yadayadayada
  cell = [tableView dequeueReusableCellWithIdentifier:@"buttonCell"];
  if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"buttonCell"] autorelease];
  }
  UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoDark];
  [button setBackgroundColor:[UIColor redColor]];
  button.titleLabel.text = @"Foo Bar";
  [cell.contentView addSubview:button];

который действительно создает кнопку. Выглядит пока не так, как должно (видимо, с кнопками в айфоне я еще не имел дела), но это хотя бы правильный подход?


person Marcel Stör    schedule 02.07.2009    source источник
comment
Будет ли результирующее действие кнопки отличаться в зависимости от того, в какой строке она находится? (например, отправить электронное письмо текущему контакту в этой строке и т. д.)   -  person Reed Olsen    schedule 03.07.2009
comment
Если это так, вы можете установить тег кнопки каждой ячейки, чтобы сказать строку indexPath, а затем прочитать тег отправителя в селекторе, который выполняется при касании.   -  person mmc    schedule 03.07.2009
comment
Что ж, как и в случае с Удалить событие, у меня будет только одна кнопка внизу пользовательского интерфейса, то есть в последней строке.   -  person Marcel Stör    schedule 03.07.2009


Ответы (5)


То, как вы это делаете сейчас, каждый раз, когда отображается ячейка, вы выделяете кнопку, устанавливаете ее значение и добавляете ее в contentView ячейки. Когда ячейка будет использоваться повторно (через dequeueReusableCellWithIdentifier), вы создадите еще одну новую кнопку, добавите ее в ячейку (поверх старой) и т. д. Тот факт, что она прошла через addSubview, но без явного выпуск означает, что счетчик удержания каждой кнопки никогда не обнуляется, поэтому все они останутся. Через некоторое время прокрутки вверх и вниз в ячейке появятся сотни подвидов кнопок, что, вероятно, не то, что вам нужно.

Несколько советов:

  • Никогда не выделяйте содержимое внутри вызова cellForRowAtIndexPath, если только это не делается, когда dequeueReusableCellWithIdentifier возвращает nil, а вы инициализируете ячейку. Все остальные последующие разы вам будет возвращена кэшированная ячейка, которую вы уже настроили, поэтому все, что вам нужно сделать, это изменить метки или значки. Вы захотите переместить весь этот материал с размещением кнопок внутри условного оператора if сразу после кода выделения ячеек.

  • У кнопки должна быть позиция, а также цель, установленная для нее, чтобы она что-то делала при нажатии. Если каждая ячейка будет иметь эту кнопку, изящный трюк состоит в том, чтобы все они указывали на один и тот же целевой метод, но устанавливали значение tag кнопки равным indexPath.row ячейки (вне блока выделения ячеек, поскольку оно различается для каждой ячейки). Обычный обработчик касания для кнопки будет использовать значение тега отправителя для поиска базовых данных в списке dataSource.

  • Вызовите release на кнопке после того, как вы сделали addSubview. Таким образом, счетчик сохранения упадет до нуля, и объект фактически будет освобожден, когда родитель будет освобожден.

  • Вместо того, чтобы добавлять кнопку через addSubview, вы можете вернуть ее как accessoryView для ячейки, чтобы вам не пришлось беспокоиться о ее позиционировании (если только вы уже не используете аксессуар для чего-то другого, например, для кнопок раскрытия).

person Ramin    schedule 02.07.2009
comment
Большое спасибо за ценные подсказки и пояснения. Такой новичок в программировании для iPhone, как я, наверняка это оценит. Что касается совета 4, как и в случае с Удалить событие/Поделиться контактом, у меня будет только одна кнопка внизу пользовательского интерфейса, то есть в последней строке. Таким образом, это не может быть аксессуарView, но он должен заполнить всю строку. Я все еще борюсь с этим. - person Marcel Stör; 03.07.2009
comment
Что касается № 3, отпускание кнопки приведет к сбою вашего приложения. Он не распределяет. - person DougW; 23.12.2009

Я использовал другой подход к созданию эквивалента кнопки «Удалить событие» в приложении «Календарь». Вместо того, чтобы добавлять кнопку в качестве подвида, я добавил к ячейкам два фоновых вида (красный и темно-красный, с красивыми градиентами), а затем закруглил углы и установил серую границу.

Приведенный ниже код создает повторно используемую ячейку (обычным способом). Два упомянутых изображения («redUp.png» и «redDown.png») были взяты из скриншота кнопки «Удалить событие» в календаре. (Это казалось быстрее, чем создание градиентов программно.) Есть возможность немного более тонкой настройки, чтобы сделать его еще ближе к внешнему виду «События удаления» календаря, но это довольно близко.

Действие кнопки запускается методом делегата tableView tableView:didSelectRowAtIndexPath: метод.

// create a button from a table row like the Calendar's 'Delete Event' button
// remember to have an #import <QuartzCore/QuartzCore.h> some above this code

static NSString *CellWithButtonIdentifier = @"CellWithButton";

 UITableViewCell *cell = [self dequeueReusableCellWithIdentifier:CellWithButtonIdentifier];
 if (cell == nil) {
  cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellWithButtonIdentifier] autorelease];
  [[cell textLabel] setTextAlignment: UITextAlignmentCenter];

  UIImageView* upImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"redUp.png"]];
  UIImageView* downImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"redDown.png"]];

  [cell setBackgroundView: upImage];
  [cell setSelectedBackgroundView: downImage];

  [[upImage layer] setCornerRadius:8.0f];
  [[upImage layer] setMasksToBounds:YES];
  [[upImage layer] setBorderWidth:1.0f];
  [[upImage layer] setBorderColor: [[UIColor grayColor] CGColor]];

  [[downImage layer] setCornerRadius:8.0f];
  [[downImage layer] setMasksToBounds:YES];
  [[downImage layer] setBorderWidth:1.0f];
  [[downImage layer] setBorderColor: [[UIColor grayColor] CGColor]];

  [[cell textLabel] setTextColor: [UIColor whiteColor]];
  [[cell textLabel] setBackgroundColor:[UIColor clearColor]];
  [cell  setBackgroundColor:[UIColor clearColor]]; // needed for 3.2 (not needed for later iOS versions)
  [[cell textLabel] setFont:[UIFont boldSystemFontOfSize:20.0]];

  [upImage release];
  [downImage release];
 }
 return cell;
person Obliquely    schedule 27.12.2010
comment
Вы должны добавить [cell.textLabel setShadowColor:[UIColor blackColor]]; [cell.textLabel setShadowOffset:CGSizeMake(0, -1)]; в код. Это придает тексту слегка рельефный эффект, поэтому он больше похож на нативную кнопку. - person Greg; 20.04.2012

Да, это в целом правильный подход.

Подсказка:

  • Установите обратный вызов для событий вашей кнопки, чтобы он действительно что-то делал при нажатии.

    [myButton addTarget:self action:@selector(whatMyButtonShouldDo:)   
      forControlEvents:UIControlEventTouchUpInside];
    

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

person mmc    schedule 02.07.2009
comment
В его примере кода нет необходимости отпускать кнопку, потому что [UIButton buttonWithType:] возвращает автоматически освобожденный экземпляр. - person Daniel Rinser; 03.07.2009
comment
Вы совершенно правы, я скопировал это из кода I, где я использовал свой initWithFrame. Регулировка. - person mmc; 03.07.2009
comment
Спасибо за ответ. Подсказка с релизом/авторелизом была отмечена. Действительно ли нужно возиться с рамой? Я просто хочу, чтобы кнопка заполнила всю ячейку таблицы (т.е. не беспокоилась о позиции и размере); Удалить событие прекрасно это демонстрирует. - person Marcel Stör; 03.07.2009
comment
Да, я слишком все усложнил, скопировав свой код, в котором я не использую системные кнопки, а устанавливаю рамку и фоновые изображения вручную. Я значительно сократил свой ответ. - person mmc; 03.07.2009

Да, вы на правильном пути, это самый простой способ добавить подвид в ячейку (другой — это подкласс UITableViewCell).

Проверьте Руководство Apple для получения дополнительной информации.

person Marco Mustapic    schedule 02.07.2009

Чтобы избежать проблем с позиционированием и управлением памятью, вы можете создать специализированный класс ячеек и связать его с XIB. Это звучит как самый чистый подход для меня. Вот ссылка: http://icocodeblog.com/2009/05/24/custom-uitableviewcell-using-interface-builder/

person Jagan    schedule 20.01.2011