iOS 7 - Как отобразить средство выбора даты в виде таблицы?

121

В видео WWDC 2013 Apple предлагает отображать средство выбора на месте в табличном представлении в iOS 7. Как вставить и анимировать представление между ячейками табличного представления?

Вот так из приложения календаря Apple:

Средство выбора даты на месте

XY
источник
Какое это видео WWDC? Я пытаюсь понять, почему это специфично для iOS7, и есть ли причина, по которой это нельзя было сделать в более ранних версиях.
Clafou
4
Нашел, это «Что нового в дизайне пользовательского интерфейса iOS» (спасибо ASCIIwwdc). Причина в том, что старый стиль выглядел неправильно, а новый плоский вид выглядит правильно.
Clafou
Пожалуйста, проверьте: Самый простой способ использовать DateCell в Swift
Ахмед Лотфи,

Ответы:

102

Для iOS7 Apple выпустила образец кода DateCell.

введите описание изображения здесь

Демонстрирует форматированное отображение объектов даты в ячейках таблицы и использование UIDatePicker для редактирования этих значений. В качестве делегата этой таблицы образец использует метод didSelectRowAtIndexPath для открытия элемента управления UIDatePicker.

Для iOS 6.x и более ранних версий UIViewAnimation используется для перемещения UIDatePicker вверх по экрану и вниз за пределы экрана. Для iOS 7.x UIDatePicker добавляется в виде таблицы.

Метод действия UIDatePicker напрямую устанавливает свойство NSDate пользовательской ячейки таблицы. Кроме того, в этом примере показано, как использовать класс NSDateFormatter для получения настраиваемой ячейки в формате даты.

введите описание изображения здесь

Вы можете скачать пример кода здесь: DateCell .

Нитин Гохель
источник
В коде Apple есть проблемы. См. Мой ответ ниже, если вы хотите сохранить свой статический tableView
Аарон Братчер
1
@AaronBratcher, я просто представил Datecell в своем ответе, и вы никогда не ожидаете, что получите точный код, который вам нужен. вы должны модифицировать и изменять в соответствии с вашими требованиями. ваше решение также хорошее, но помните, что вы никогда не получите точного определения того, что из другого кода вам требуется.
Нитин Гохель
3
самый ужасный образец кода. Они могли бы дать несколько советов о том, когда создавать метод из Code Complete
Анирудха Агаше
2
Ух ты, код Apple ... ну, допустим, основное внимание уделяется эффекту табличного представления. Я надеюсь, что их код в этом образце проекта никого не вдохновит: s
Daniel
84

Вы можете использовать ответ, который я ранее дал ниже, или использовать этот новый класс в Swift, который я сделал, чтобы сделать эту задачу намного проще и чище: https://github.com/AaronBratcher/TableViewHelper


Я считаю код, предоставленный Apple, проблематичным по нескольким причинам:

  • Вы не можете иметь статический tableView, потому что они используют метод tableView: cellForRowAtIndexPath
  • Код аварийно завершает работу, если у вас нет дополнительных строк ниже последней ячейки выбора даты.

Для таблиц статических ячеек я определяю ячейку выбора даты под ячейкой отображения даты и имею флаг, определяющий, редактирую ли я ее. Если да, я возвращаю соответствующую высоту ячейки, в противном случае я возвращаю нулевую высоту ячейки.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0 && indexPath.row == 2) { // this is my picker cell
        if (editingStartTime) {
            return 219;
        } else {
            return 0;
        }
    } else {
        return self.tableView.rowHeight;
    }
}

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

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0 && indexPath.row == 1) { // this is my date cell above the picker cell
        editingStartTime = !editingStartTime;
        [UIView animateWithDuration:.4 animations:^{
            [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:2 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView reloadData];
        }];
    }
}

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

Аарон Братчер
источник
Это сработало для меня, за исключением того, что в UITableView есть ошибка. В моем методе высота строки возвращалась правильно, но на самом деле он переключал высоту, противоположную той, которую я хотел. Если я дважды перезагрузил строки, это сработало. (Я не использовал tableView reloadData, потому что не хотел все перезагружать). Кроме того, в блоке анимации не было необходимости, кажется, что он анимирован сам по себе нормально.
Крис Гарретт,
Кто-нибудь использовал образец для создания средства выбора текста? если да, то как? Спасибо
TheGeezer
1
Вы хотите показать строку, чтобы пользователь мог вводить текст вместо выбора даты? Если да, то код тот же. Просто содержимое раскрытой строки отличается.
Аарон Братчер
22
В iOS7.1 есть ошибка, из-за которой объекты отображаются за пределами ячейки - например, с высотой 0 и средством выбора внутри. Решение состоит в том, чтобы получить выход для ячейки и установить cell.clipsToBounds = YES;
EmilDo
1
@Dunken - решение описано в одобренном выше комментарии: stackoverflow.com/questions/18973573/… Даже если строка имеет нулевую высоту, содержимое по умолчанию не обрезается, поэтому вы можете видеть их под следующий ряд.
Крис Нолет,
18

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

@interface StaticTableViewController: UITableViewController

@property (weak, nonatomic) IBOutlet UITableViewCell *dateTitleCell; // cell that will open the date picker. This is linked from the story board
@property (nonatomic, assign, getter = isDateOpen) BOOL dateOpen;

@end


@implementation StaticTableViewController

-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{

    // This is the index path of the date picker cell in the static table
    if (indexPath.section == 1 && indexPath.row == 1 && !self.isDateOpen){
        return 0;
    }
    return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}

-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
    [tableView beginUpdates];
    if (cell == self.dateTitleCell){
        self.dateOpen = !self.isDateOpen;
    }
    [tableView reloadData];
    [self.tableView endUpdates];
}
datinc
источник
здесь чего-то еще не хватает? Высота ячеек не меняется :)
DevC
2
Начиная с iOS 7.1, вы также должны выбрать «Clip to SubViews» в инспекторе атрибутов - это то, чего мне не хватало :)
DevC
Также строка «self.dateOpen =! Self.isDateOpen;» в начале следует читать «self.isDateOpen =», а не «self.dateOpen =»
Питер Джонсон
2
[tableView reloadData];Вызов не требуется. В настоящее время он отключает выделение строки, но лучше отменить выделение строки следующим образом:[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
Barry Haanstra
У меня были странные эффекты анимации, например, после «открытия» ячейка с датой была пустой, но это решило использование begin и endupdates. Теперь красиво и гладко. Спасибо, datinc!
nh32rg
5

Я взял исходный код DateCell от Apple и удалил файл раскадровки.

Если вам нужен вариант без раскадровки, взгляните на: https://github.com/ajaygautam/DateCellWithoutStoryboard

Аджай Гаутам
источник
Спасибо @Ajay, я использовал ваш измененный код и хочу заменить его UIDatePickerна UIPickerView. Я пробовал несколько вещей и смог показать pickerViewв каждой ячейке, но он не обновляется с каждой ячейкой. Я отправил свой вопрос и коды здесь . Пожалуйста, посмотрите, и было бы очень любезно, если бы вы могли предложить мне решение.
Mughees Musaddiq
Я видел ваш вопрос ... в нем слишком много информации и мало что объясняет. Возможно, вы можете загрузить свой код / ​​заархивировать где-нибудь, чтобы я посмотрел. Я могу отладить его - если я смогу запустить код ...
Аджай Гаутам
Спасибо, я как - то удалось заменить UIDatePickerс , UIPickerViewно с несколькими ошибками. 1. Вылетает при открытии UIPickerViewи прокрутке таблицы. 2. Он автоматически присваивает значение « UILabelПодробности» в нижних строках таблицы, когда значения присваиваются метке «Подробности» в верхних строках. Вот мой код
Mughees Musaddiq
Вы можете найти testProject, Удачи ??
Mughees Musaddiq
Извини, я немного занята. По-прежнему нужна помощь?
Аджай Гаутам
5

Одним из лучших руководств по этому поводу является встроенный в iOS 7 UIDatePicker - Часть 2. . В основном здесь я использую ячейки статического представления таблицы и реализую некоторые дополнительные методы. Для этого я использовал Xamarin и C #:

Вы должны активировать Clip Subviews .

Установка высоты:

public override float GetHeightForRow (UITableView tableView, NSIndexPath indexPath)
{
    if (indexPath.Row == 4) {
        return (datePickerIsShowing) ? 206f : 0.0f;
    }

    return base.GetHeightForRow(tableView,indexPath);
}

Чем переменная класса: private bool datePickerIsShowing = false;

Показать выбор даты:

private void showDatePickerCell(){
    datePickerIsShowing = true;
    this.TableView.BeginUpdates ();
    this.TableView.EndUpdates ();
    this.datePicker.Hidden = false;
    this.datePicker.Alpha = 0.0f;

    UIView.Animate (0.25, animation:
        () => {
            this.datePicker.Alpha = 1.0f;
        }
    );
} 

Скрыть выбор даты:

private void hideDatePickerCell(){
    datePickerIsShowing = false;
    this.TableView.BeginUpdates ();
    this.TableView.EndUpdates ();

    UIView.Animate (0.25,
        animation: () => {
            this.datePicker.Alpha = 0.0f;
        },
        completion: () => {
            this.datePicker.Hidden = true;
        }
    );
} 

И вызывая эти функции:

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    if (indexPath.Row == 3) {
        if (datePickerIsShowing) {
            hideDatePickerCell ();
        } else {
            showDatePickerCell ();
        }
    }

    this.TableView.DeselectRow (indexPath, true);
}
тестирование
источник
2
Если вы голосуете против, возможно, вы сможете объяснить, почему вы проголосовали против. Вместо размещения ссылок на сайты я также добавил код. В моем случае это C #. Не знаю, что в этом плохого.
тестирование
3

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

Вы можете найти его здесь вместе с примером проекта, демонстрирующим, как его использовать: https://github.com/ale84/ALEInlineDatePickerViewController

ale84
источник
3

Я нашел ответ на ошибку в примере apple datecell, где у вас должна быть строка под последней datecell, иначе вы получите ошибку. В методе CellForRowAtIndexPath замените строку ItemData на

NSArray *itemsArray = [self.dataArray objectAtIndex:indexPath.section];
NSDictionary *itemData = nil;

if(![indexPath isEqual:self.datePickerIndexPath])
    itemData = [itemsArray objectAtIndex:modelRow];

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

Я только что присоединился к stackoverflow, поэтому, если он находится не в том месте или где-то еще, прошу прощения.

pjmarshall1
источник
3

Ответ от Аарона Братчера работал, за исключением случаев, когда он использовался с несколькими разделами. Анимация была немного прерывистой и не очень хорошо скользила по следующим разделам. Чтобы исправить это, я перебрал следующий набор разделов и перевел строки на ту же величину, что и высота средства выбора даты.

Я отредактировал didSelectRowAtIndexPath так:

// Return Data to delegate: either way is fine, although passing back the object may be more efficient
// [_delegate imageSelected:currentRecord.image withTitle:currentRecord.title withCreator:currentRecord.creator];
// [_delegate animalSelected:currentRecord];
if (indexPath.section == 1 && indexPath.row == 0) { // this is my date cell above the picker cell
    editingStartTime = !editingStartTime;
    [UIView animateWithDuration:.4 animations:^{
        int height = 0;
        if (editingStartTime) {
            height = 162;
        }
        UITableViewCell* temp = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]];
        [temp setFrame:CGRectMake(temp.frame.origin.x, temp.frame.origin.y, temp.frame.size.width, height)];
        for (int x = 2; x < [tableView numberOfSections]; x++) {
            for (int y = 0; y < [tableView numberOfRowsInSection:x]; y++) {
                UITableViewCell* temp = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:y inSection:x]];
                int y_coord = temp.frame.origin.y-162;
                if (editingStartTime) {
                    y_coord = temp.frame.origin.y+162;
                }
                [temp setFrame:CGRectMake(temp.frame.origin.x, y_coord, temp.frame.size.width, temp.frame.size.height)];
            }
        }
    }completion:^(BOOL finished){
        [self.tableView reloadData];
    }];
}
Тимоти Логан
источник
Спасибо @Timmahh, анимация раздела тоже показалась мне прерывистой. Я успешно использовал ваше решение в своем первом приложении Swift! Я обнаружил, что cellForRowAtIndexPath возвращает nil, хотя ячейки находятся за пределами экрана. Чтобы избежать этого, я добавил IB выходную коллекцию, содержащую все мои ячейки, которые пришли после ячейки сборщика, и перебирал их с вашим новым y_coord. Это кажется негибким, поскольку я должен поддерживать коллекцию в актуальном состоянии при добавлении новых ячеек.
Джош
Ты прав. Фактически, я устранил проблему, но переполнение стека не позволяло мне редактировать код, который я там разместил.
Тимоти Логан
3

Добавляя к предыдущим ответам,

Я пробовал решения @datinc и @Aaron Bratcher, оба работали отлично, но анимация не была такой чистой в сгруппированном статическом tableView.

Немного поиграв с ним, я добрался до этого кода, который отлично работает для меня -

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0 && indexPath.row == 1)
    {
        if (self.isPickerOpened)
        {
            return 162;
        }
        else
        {
            return 0;
        }
    }
    else
    {
        return [super tableView:tableView heightForRowAtIndexPath:indexPath];
    }
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section == 0 && indexPath.row == 0) {
        [tableView beginUpdates];
        self.isPickerOpened = ! self.isPickerOpened;
        [super tableView:tableView heightForRowAtIndexPath:indexPath];
        [self.tableView endUpdates];
    }
}

Основное изменение заключается в использовании -

        [super tableView:tableView heightForRowAtIndexPath:indexPath];

чтобы обновить строку, таким образом остальные разделы и ячейки таблицы не будут анимированы.

Надеюсь, это кому-то поможет.

Шани

shannoga
источник
Это очень помогло; Спасибо! В частности, аспект [super tableView], связанный с сообщением Аарона Братчера, дал мне результаты, которые мне нужны для iOS 9.2. Еще раз спасибо!
Терренс Шоу
2

Добавление к предыдущим ответам и решению @Aaron Bratcher ...

Начиная с iOS 9, я получал прерывистую анимацию, а загрузка таблицы требовала времени, и этого было достаточно, чтобы раздражать. Я сузил его до того, что средства выбора даты медленно загружаются из раскадровки. Добавление сборщиков программным способом, а не в раскадровке, улучшило производительность загрузки, и, как побочный продукт, анимация стала более плавной.

Удалите средство выбора даты из раскадровки и получите пустую ячейку, для которой вы установите высоту, как в предыдущих ответах, а затем вызовите инициализацию для viewDidLoad:

- (void)initialiseDatePickers
{
    self.isEditingStartTime = NO;
    self.startTimePickerCell.clipsToBounds = YES;

    UIDatePicker *startTimePicker = [[UIDatePicker alloc] init];
    [startTimePicker addTarget:self action:@selector(startTimePickerChanged:) forControlEvents:UIControlEventValueChanged];
    [self.startTimePickerCell addSubview:startTimePicker];
}

Затем выполните действие, например

- (IBAction)startTimePickerChanged:(id)sender
{
    NSLog(@"start time picker changed");
}

Это загружает таблицу намного быстрее, чем раньше. Вы также удаляете линию анимации, didSelectRowAtIndexPathпоскольку она плавно анимируется без нее (ymmv).

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0 && indexPath.row == 1) { // this is my date cell above the picker cell
        editingStartTime = !editingStartTime;
    }
}
matt_s
источник
1

Использование этого ответа без анимации корректно работает в iOS 8.1. Я преобразовал его в Swift ниже:

import UIKit

class TableViewController: UITableViewController {

    var editingCell: Bool = false

    @IBOutlet weak var myCell: UITableViewCell!

    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

        // Change the section and row to the title cell the user taps to reveal 
        // the cell below
        if (indexPath.section == 0 && indexPath.row == 2 && !editingCell) {
            return 0
        } else {
            return self.tableView.rowHeight
        }
    }

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

        self.tableView.deselectRowAtIndexPath(indexPath, animated: false);

        var cell = tableView.cellForRowAtIndexPath(indexPath)

        self.tableView.beginUpdates()
        if (cell == self.myCell) {
            editingType = !editingType;
        }
        self.tableView.endUpdates()
    }
}
davidmytton
источник
1

Вот еще один способ решить проблему без статических постоянных чисел. Все ячейки можно использовать в статических и динамических табличных представлениях. В этом методе для выбора заголовка и даты используется одна ячейка!

Между прочим, у вас может быть столько инструментов для выбора даты в вашем столе, сколько захотите!

Создайте подкласс UITableViewCell :

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

//
//  CPTableViewCell.h
//
//  Copyright (c) CodePigeon. All rights reserved.
//

@class CPTableViewCell;

#define kUIAnimationDuration 0.33f

@protocol CPTableViewCellDelegate <NSObject>

@required
- (void)tableViewCellDidChangeValue:(CPTableViewCell *)cell;
@optional
- (void)tableViewCellDidBecomeFirstResponder:(CPTableViewCell *)cell;
- (void)tableViewCellResignedFirstResponder:(CPTableViewCell *)cell;
@end

@interface CPTableViewCell : UITableViewCell

@property (nonatomic, weak) IBOutlet UITableView *tableView;
@property (nonatomic, weak) IBOutlet CPTableViewCell *nextCell;
@property (nonatomic, weak) IBOutlet id<CPTableViewCellDelegate> delegate;

@property (nonatomic, copy) IBInspectable NSString *dataBindKey;
@property (nonatomic) IBInspectable CGFloat height;

@property (nonatomic, readonly) BOOL isFirstResponder;
@property (nonatomic) BOOL isEnabled;

- (void)commonInit;

- (id)value;
- (void)setValue:(id)value;

@end

//
//  CPTableViewCell.m
//
//  Copyright (c) CodePigeon. All rights reserved.
//

#import "CPTableViewCell.h"

@interface CPTableViewCell ()
@end

@implementation CPTableViewCell

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    if (!self)
        return nil;

    [self commonInit];

    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];

    if (!self)
        return nil;

    [self commonInit];

    return self;
}

- (void)commonInit
{
    _isFirstResponder = NO;
    _isEnabled = YES;
}

- (BOOL)canBecomeFirstResponder
{
    return _isEnabled;
}

- (BOOL)becomeFirstResponder
{
    if ([_delegate respondsToSelector:@selector(tableViewCellDidBecomeFirstResponder:)])
        [_delegate tableViewCellDidBecomeFirstResponder:self];

    return _isFirstResponder = YES;
}

- (BOOL)resignFirstResponder
{
    if (_isFirstResponder)
    {
        if ([_delegate respondsToSelector:@selector(tableViewCellResignedFirstResponder:)])
            [_delegate tableViewCellResignedFirstResponder:self];

        _isFirstResponder = NO;
    }

    return _isFirstResponder;
}

- (id)value
{
    [self doesNotRecognizeSelector:_cmd];

    return nil;
}

- (void)setValue:(id)value
{
    [self doesNotRecognizeSelector:_cmd];
}

@end

Создайте класс CPDatePickerTableViewCell из нашего CPTableViewCell

//
//  CPDatePickerTableViewCell.h
//
//  Copyright (c) CodePigeon. All rights reserved.
//

#import "CPTableViewCell.h"

@interface CPDatePickerTableViewCell : CPTableViewCell

@property (nonatomic, copy) IBInspectable NSString *dateFormat;

@property (nonatomic, weak) IBOutlet UILabel *titleLabel;
@property (nonatomic, weak) IBOutlet UILabel *dateLabel;

@property (nonatomic, weak) IBOutlet UIDatePicker *datePicker;

@end

//
//  CPDatePickerTableViewCell.m
//
//  Copyright (c) CodePigeon. All rights reserved.
//

#import "CPDatePickerTableViewCell.h"

#define kCPDatePickerTableViewCellPickerHeight 162.f

@interface CPDatePickerTableViewCell () <UITextFieldDelegate, UIPickerViewDelegate>
{
    NSDateFormatter *_dateFormatter;
    BOOL _isOpen;
}
@end

@implementation CPDatePickerTableViewCell

- (void)awakeFromNib
{
    [super awakeFromNib];

    _dateFormatter = [NSDateFormatter new];
    [_dateFormatter setDateFormat:_dateFormat];

    self.selectionStyle = UITableViewCellSelectionStyleNone;

    _dateLabel.text = [_dateFormatter stringFromDate:_datePicker.date];
    _datePicker.alpha = 0.f;
    _isOpen = NO;
}

- (BOOL)becomeFirstResponder
{
    if (_isOpen == NO)
    {
        self.height += kCPDatePickerTableViewCellPickerHeight;
    }
    else
    {
        self.height -= kCPDatePickerTableViewCellPickerHeight;
    }

    [UIView animateWithDuration:kUIAnimationDuration animations:^{
        _datePicker.alpha = _isOpen ? 0.0f : 1.0f;
    }];

    [self.tableView beginUpdates];
    [self.tableView endUpdates];

    _isOpen = !_isOpen;

    [self.tableView endEditing:YES];

    return [super becomeFirstResponder];
}

- (BOOL)resignFirstResponder
{
    if (_isOpen == YES)
    {
        self.height -= kCPDatePickerTableViewCellPickerHeight;

        [UIView animateWithDuration:kUIAnimationDuration animations:^{
            _datePicker.alpha = 0.0f;
        }];

        [self.tableView beginUpdates];
        [self.tableView endUpdates];

        _isOpen = NO;
    }

    return [super resignFirstResponder];
}

- (id)value
{
    return _datePicker.date;
}

- (void)setValue:(NSDate *)value
{
    _datePicker.date = value;
    _dateLabel.text = [_dateFormatter stringFromDate:_datePicker.date];
}

- (IBAction)datePickerValueChanged:(UIDatePicker *)sender
{
    [_dateLabel setText:[_dateFormatter stringFromDate:_datePicker.date]];

    [self.delegate tableViewCellDidChangeValue:self];
}

@end

В вашем контроллере представления реализованы эти два метода делегата

#pragma mark - UITableViewDelegate methods

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CPTableViewCell *cell = (CPTableViewCell *)[super tableView:tableView cellForRowAtIndexPath:indexPath];

    return [cell height];
}

- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
    CPTableViewCell *cell = (CPTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];

    if ([cell canBecomeFirstResponder])
    {
        [cell becomeFirstResponder];
    }

    if (cell != _selectedCell)
    {
        [_selectedCell resignFirstResponder];
    }

    _selectedCell = cell;

    return YES;
}

Пример настройки ограничений в построителе интерфейсов

Конструктор интерфейсов

Кроме того, я написал собственные классы ячеек для UITextField и UITextView, где tableView: didSelectRowAtIndexPath: вызывается при ячейки!

CPTextFieldTableViewCell

//
//  CPTextFieldTableViewCell.h
//
//  Copyright (c) CodePigeon. All rights reserved.
//

#import "CPTableViewCell.h"

@interface CPTextFieldTableViewCell : CPTableViewCell

@property (nonatomic, weak) IBOutlet UITextField *inputTextField;

@end

//
//  CPTextFieldTableViewCell.m
//
//  Copyright (c) CodePigeon. All rights reserved.
//

#import "CPTextFieldTableViewCell.h"

@interface CPTextFieldTableViewCell () <UITextFieldDelegate>
@end

@implementation CPTextFieldTableViewCell

- (void)awakeFromNib
{
    [super awakeFromNib];

    self.selectionStyle = UITableViewCellSelectionStyleNone;

    _inputTextField.userInteractionEnabled = NO;
    _inputTextField.delegate = self;
}

- (BOOL)becomeFirstResponder
{
    _inputTextField.userInteractionEnabled = YES;

    [_inputTextField becomeFirstResponder];

    return [super becomeFirstResponder];
}

- (BOOL)resignFirstResponder
{
    _inputTextField.userInteractionEnabled = NO;

    return [super resignFirstResponder];
}

- (void)setIsEnabled:(BOOL)isEnabled
{
    [super setIsEnabled:isEnabled];

    _inputTextField.enabled = isEnabled;
}

- (id)value
{
    return _inputTextField.text;
}

- (void)setValue:(NSString *)value
{
    _inputTextField.text = value;
}

#pragma mark - UITextFieldDelegate methods

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self.delegate tableViewCellDidChangeValue:self];
}

@end

CBTextViewTableViewCell

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

//
//  CBTextViewTableViewCell.h
//
//  Copyright (c) CodePigeon. All rights reserved.
//

#import "CPTableViewCell.h"

@interface CPTextViewTableViewCell : CPTableViewCell

@property (nonatomic, weak) IBOutlet UITextView *inputTextView;

@end

//
//  CBTextViewTableViewCell.m
//
//  Copyright (c) CodePigeon. All rights reserved.
//

#import "CPTextViewTableViewCell.h"

@interface CPTextViewTableViewCell () <UITextViewDelegate>
{
    UITextView *_heightTextView;
}

@end

@implementation CPTextViewTableViewCell

@synthesize height = _height;

- (void)awakeFromNib
{
    [super awakeFromNib];

    self.selectionStyle = UITableViewCellSelectionStyleNone;

    _inputTextView.userInteractionEnabled = NO;
    _inputTextView.delegate = self;
    _inputTextView.contentInset = UIEdgeInsetsZero;
    _inputTextView.scrollEnabled = NO;
}

- (CGFloat)height
{
    if (!_heightTextView)
    {
        CGRect frame = (CGRect) {
            .origin = CGPointMake(0.f, 0.f),
            .size = CGSizeMake(_inputTextView.textInputView.frame.size.width, 0.f)
        };

        _heightTextView = [[UITextView alloc] initWithFrame:frame];
        _heightTextView.font = [UIFont systemFontOfSize:_inputTextView.font.pointSize];
        _heightTextView.textColor = UIColor.whiteColor;
        _heightTextView.contentInset = UIEdgeInsetsZero;
    }

    _heightTextView.text = _inputTextView.text;

    CGSize size = [_heightTextView sizeThatFits:CGSizeMake(_inputTextView.textInputView.frame.size.width, FLT_MAX)];

    return size.height > _height ? size.height + _inputTextView.font.pointSize : _height;
}

- (BOOL)becomeFirstResponder
{
    _inputTextView.userInteractionEnabled = YES;

    [_inputTextView becomeFirstResponder];

    return [super becomeFirstResponder];
}

- (BOOL)resignFirstResponder
{
    _inputTextView.userInteractionEnabled = NO;

    return [super resignFirstResponder];
}

- (void)setIsEnabled:(BOOL)isEnabled
{
    [super setIsEnabled:isEnabled];

    _inputTextView.editable = isEnabled;
}

- (id)value
{
    return _inputTextView.text;
}

- (void)setValue:(NSString *)value
{
    _inputTextView.text = value;

    [_inputTextView setNeedsLayout];
    [_inputTextView layoutIfNeeded];
}

#pragma mark - UITextViewDelegate methods

- (void)textViewDidChange:(UITextView *)textView
{
    [self.delegate tableViewCellDidChangeValue:self];

    [self.tableView beginUpdates];
    [self.tableView endUpdates];
}

@end
Ако
источник
0

Самый простой способ использовать DateCell в версии Swift: используйте этот пример .

  1. Откройте этот пример и проверьте его (сделайте Xcode для преобразования в Swift 2)
  2. Перетащите класс DateCellTableViewController.swift в свой проект.

  3. Откройте «Main.storyboard», скопируйте объект ViewController « DateCell » и вставьте его в свою раскадровку.

Ахмед Лотфи
источник