iOS 7 - How to display a date picker in place in a table view?
Asked Answered
A

13

121

In WWDC 2013 video, Apple suggests displaying picker in place in a table view in iOS 7. How to insert and animate a view between table view cells?

Like this, from the Apple calendar app:

In-place date picker

Atwell answered 24/9, 2013 at 5:26 Comment(3)
Which WWDC video is this? I'm trying to understand why this is iOS7-specific, and if there is a reason why this could not be done on earlier versions.Mathers
Found it, it's "What’s New in iOS User Interface Design" (thanks ASCIIwwdc). The rationale is that the old styling didn't look right inline, but the new flat look does.Mathers
Please Check: The easiest way to use DateCell in SwiftIrreverent
A
102

With iOS7, Apple released the sample code DateCell.

enter image description here

Demonstrates formatted display of date objects in table cells and use of UIDatePicker to edit those values. As a delegate to this table, the sample uses the method "didSelectRowAtIndexPath" to open the UIDatePicker control.

For iOS 6.x and earlier, UIViewAnimation is used for sliding the UIDatePicker up on-screen and down off-screen. For iOS 7.x, the UIDatePicker is added in-line to the table view.

The action method of the UIDatePicker will directly set the NSDate property of the custom table cell. In addition, this sample shows how to use NSDateFormatter class to achieve the custom cell's date-formatted appearance.

enter image description here

You can download the sample code here: DateCell.

Abisia answered 24/9, 2013 at 5:33 Comment(4)
Apple's code has problems. See my answer below if you want to keep your static tableViewSynthesize
@AaronBratcher i just introduce Datecell in my answer and you never expect you get exact code that you want. you have to modify and change as par you requirement. your solution is also good but remember you never get exact you what from other code that you requirement.Abisia
most horrible sample code. They could do with some advice on when to create a method from Code CompleteMide
Wow the Apple code is.... well, lets say the focus is on the table view effect. I hope no one gets inspired by their code in this sample project :sChiliasm
S
84

You can use the answer I had previously given below or use this new class in Swift I made to make this task a lot simpler and cleaner: https://github.com/AaronBratcher/TableViewHelper


I find the code provided by Apple to be problematic in a couple of ways:

  • You can't have a static tableView because they are using the tableView:cellForRowAtIndexPath method
  • The code crashes if you don't have additional rows below the last date picker cell

For static cell tables, I define my date picker cell below my date display cell and have a flag identifying if I'm editing it. If I am, I return a cell height appropriate, otherwise I return a cell height of zero.

- (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;
    }
}

When the row showing the date is clicked, I change the flag and do the update animation to show the picker.

-(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];
        }];
    }
}

If I have multiple date/time pickers in the same table, I set the flags accordingly on the click and reload the appropriate rows. I've found that I can keep my static table, use a lot less code, and it is easier to understand what is happening.

Synthesize answered 13/11, 2013 at 18:32 Comment(15)
This worked for me, except there appears to be a bug in UITableView. The row height was being returned correctly in my method, but it actually toggled the opposite height that I wanted. If I reloaded the rows twice it worked. (I didn't use tableView reloadData because I didn't want to reload the whole thing). Also the animation block wasn't necessary, it seems to animate on its own just fine.Bes
Has anyone used the sample to create a text picker? if so, how? ThanksFell
You mean to reveal a row so the user can enter text instead of select a date? If so, the code is the same. Just the contents of the revealed row is different.Synthesize
This is a lot simpler (and thus much less error prone) IMHO than Apple's sample project, which inserts and deletes rows rather than showing and hiding. Thanks +1Hola
I used the apple example but Aarons' example is much better and easy to implement.Galilee
There us a bug with iOS7.1 that shows objects out of the cell bounds - like the one with height 0 and picker inside. The solution is to get an outlet for the cell and set cell.clipsToBounds = YES;Galilee
I just noticed that bug and was about to look at a workaround. Thanks!Synthesize
Overwrite the UIView animate block with this: [tableView beginUpdates]; [tableView endUpdates]; // simpler solutionSerrulate
I tried the beginUpdates / endUpdates calls in place of the UIView animateWithDuration and found it isn't reliable.Synthesize
@Emilio What do you mean by getting the outlet? I set the cell.clipsToBounds = YES; in tableView:cellForRowAtIndexPath: Does not work for me. I had to set the cell.picker.hidden = ! editingStartTime;Obtund
Hi see the "collapsed" cell in the background... Why is this? Are all the cells transparent by default?Uranic
@Uranic - A solution is described in the up-voted comment above: #18974073 Even though the row has a height of zero, the contents aren't clipped by default, so you can see them underneath the following row.Baudekin
@AaronBratcher I realize this is an older thread. But I found your repository really helped me figure out how to get a smoothly animated Date Picker. I use Xamarin so I wrote a small port of your example to C# using the MonoTouch framework. github.com/CheezeCoder/AaronBratcher_TableViewHelper_Xamarin I hope it helps someone!Crosstree
Why the 219 constant for the height? Shouldn't it be 216? (default height of a date picker)Discontinuance
This works for me if I omit the last [self.tableView reloadData]; inside the animation block. If I leave it, the size change is instantaneous (animation is negated). - iOS 9Discontinuance
C
18

Using the storyboard and a static table I was able to achieve the same result using the following code. This is a great solution because if you have many oddly shaped cells or want to have multiple cells that are dynamically shown/hidden this code will still work.

@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];
}
Capet answered 7/2, 2014 at 22:44 Comment(5)
is there something else missing here? The height of the cells arent changing :)Osana
Since iOS 7.1 you must also select "Clip to SubViews" in the Attributes inspector - thats what I was missing :)Osana
Also, the line "self.dateOpen = !self.isDateOpen;" should read "self.isDateOpen =" at the start, not "self.dateOpen ="Shellans
The [tableView reloadData]; call is not required. Currently it disables the row selection, but it is better to deselect the row like this: [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];Lanti
I had weird animation effects like the date cell would be blank after "opening" but using the begin and endupdates solved that. Nice and smooth now. Thanks datinc!Marymarya
K
5

I have taken the DateCell source from Apple, and removed the storyboard file.

If you want one without storyboard, take a look at: https://github.com/ajaygautam/DateCellWithoutStoryboard

Kilo answered 9/3, 2014 at 19:36 Comment(6)
Thanks @Ajay, I used your amended code and want to replace UIDatePicker with UIPickerView. I tried few things and able to show pickerView on each cell but it doesn't gets updated with each cell. I have posted my question and code here. Please have a look and it would be very kind if you could suggest me a solution.Fescue
I saw your question... it has too much info, and doesn't really explain much. Perhaps you can upload your code / zipped up somewhere for me to take a look. I can debug it - if I can run the code...Kilo
Thanks, I somehow managed to replace UIDatePicker with UIPickerView but with couple of bugs. 1. It crashes when you open UIPickerView and scroll the table. 2. It automatically assigns the value to UILabel Detail at the bottom rows of the table when values are being assigned to Details label on top rows. Here is my codeFescue
Are you able to find the testProject, Any luck??Fescue
Sorry, I got a bit busy. Still need help with this?Kilo
Yes please, If you could look at the bugs. I think there is a problem with index path. Please have a look Here is my codeFescue
G
5

One of the best tutorials about this is iOS 7 in-line UIDatePicker – Part 2. Basically here I use static table view cells and implement some additional methods. I used Xamarin and C# for this:

You have to active Clip Subviews.

Setting the height:

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

    return base.GetHeightForRow(tableView,indexPath);
}

Than a class variable: private bool datePickerIsShowing = false;

Show date picker:

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;
        }
    );
} 

Hide date picker:

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;
        }
    );
} 

And calling this functions:

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

    this.TableView.DeselectRow (indexPath, true);
}
Gibert answered 18/9, 2014 at 10:18 Comment(1)
If you downvote, perhaps you can explain why you downvote. Instead of posting links to sites I also included some code. In my case it's C#. Don't know what's wrong with that.Gibert
E
3

I have made my own custom view controller to simplify the process of adding an inline picker inline in a tableview. You just subclass it and follow some simple rules and it handles the date picker presentation.

You can find it here along with an example project that demonstrates how to use it: https://github.com/ale84/ALEInlineDatePickerViewController

Evolutionary answered 17/3, 2014 at 9:2 Comment(0)
P
3

I found an answer to a flaw in apple's datecell example where you must have a row below the last datecell or you get an error. In CellForRowAtIndexPath method replace ItemData line with

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

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

After replacing the sample code I can now display display a datePicker cell without having a cell beneath it.

I just joined stackoverflow so if this is in the wrong place or somewhere else I apologize.

Pacificism answered 26/3, 2014 at 2:6 Comment(0)
J
3

The answer from Aaron Bratcher worked except when used with multiple sections. The animations were a bit choppy and it didn't slide the next sections down very well. To fix this I iterated through the next set of sections and translated the rows down the same amount as the date picker's height.

I edited the didSelectRowAtIndexPath to:

// 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];
    }];
}
Jill answered 14/9, 2014 at 22:58 Comment(2)
Thanks @Timmahh, I found the section animation choppy too. I used your solution successfully in my first Swift app! I found that cellForRowAtIndexPath returns nil where cells are off-screen though. To avoid this I added an IB outlet-collection containing all my cells, that came after the picker cell, and iterated through them with your new y_coord. This seems inflexible though as I have to keep the collection up-to-date when I add new cells.Abusive
You're right. In fact, I had fixed the issue but stack overflow wouldn't let me edit the code I put there.Jill
A
3

Adding to the previous answers,

I tried both @datinc and @Aaron Bratcher solutions, both worked great but the animation was not so clean in a grouped static tableView.

After playing with it a little bit I got to this code that works clean and great for me -

- (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];
    }
}

The main change is to use -

        [super tableView:tableView heightForRowAtIndexPath:indexPath];

to update the row, this way the rest of the table sections and cells are not animating.

Hope it helps someone.

Shani

Artel answered 1/12, 2014 at 14:18 Comment(1)
This helped tremendously; thank you! Specifically the [super tableView] aspect, bundled with Aaron Bratcher's post, got me the results I needed on iOS 9.2. Thank you again!Exotoxin
B
2

Adding to the previous answers and @Aaron Bratcher solution...

I was getting choppy animations since iOS 9, and the table was taking a while to load, and enough to be annoying. I narrowed it do to the date pickers being slow to load from the storyboard. Adding the pickers programmatically rather than in the storyboard improved the loading performance, and as a by-product, the animation is smoother.

Remove the date picker from storyboard and have an empty cell, which you set the height as in previous answers, and then call an initialise on 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];
}

Then implement the action e.g.

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

This loads the table much faster than previously. You also remove the animation line from didSelectRowAtIndexPath as it animates smoothly without it (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;
    }
}
Benally answered 27/10, 2015 at 17:57 Comment(0)
G
1

Using this answer without the animation works correctly in iOS 8.1. I have converted it into Swift below:

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()
    }
}
Gaby answered 4/1, 2015 at 22:46 Comment(0)
D
1

Here is another way to solve the problem without static constant numbers. All the cells can be used in static and dynamic table views. This method uses single cell for both title and date picker!

Btw you can have as many date pickers in your table as you wish!

Create a UITableViewCell subclass:

All your table view cells have to be inherited from this class and you must set the cell height manually for every row.

//
//  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

Create a CPDatePickerTableViewCell class from our 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

In your view controller implement these two delegate methods

#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;
}

Example how to set up constraints in Interface builder

Interface builder

Additionally I have written custom cell classes for UITextField and UITextView where tableView:didSelectRowAtIndexPath: is called when cell is selected!

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

Cell height is dynamic and row will grow when text is wrapped to new line!

//
//  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
Declinate answered 27/8, 2015 at 20:24 Comment(0)
I
0

The easiest way to use DateCell in Swift version: Use this example.

  1. Open this example and test it(Make Xcode to Convert to Swift 2)
  2. Drag "DateCellTableViewController.swift" class to your project.

  3. Open "Main.storyboard" and Copy "DateCell" ViewController Object and past it in your storyboard.

Irreverent answered 13/3, 2016 at 13:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.