iOS 7 - Wie wird eine Datumsauswahl in einer Tabellenansicht angezeigt?


121

In WWDC 2013-Video schlägt Apple vor, die Auswahl in einer Tabellenansicht in iOS 7 anzuzeigen. Wie wird eine Ansicht zwischen Zellen der Tabellenansicht eingefügt und animiert?

So aus der Apple Kalender-App:

Datumsauswahl vor Ort


Welches WWDC-Video ist das? Ich versuche zu verstehen, warum dies iOS7-spezifisch ist und ob es einen Grund gibt, warum dies in früheren Versionen nicht möglich war.
Clafou

4
Gefunden, es ist "Was ist neu in iOS User Interface Design" (danke ASCIIwwdc). Das Grundprinzip ist, dass das alte Styling nicht richtig inline aussah, aber das neue flache Aussehen.
Clafou

Antworten:


102

Mit iOS7 hat Apple den Beispielcode veröffentlicht DateCell.

Geben Sie hier die Bildbeschreibung ein

Demonstriert die formatierte Anzeige von Datumsobjekten in Tabellenzellen und die Verwendung von UIDatePicker zum Bearbeiten dieser Werte. Als Delegat dieser Tabelle verwendet das Beispiel die Methode "didSelectRowAtIndexPath", um das UIDatePicker-Steuerelement zu öffnen.

Für iOS 6.x und frühere Versionen wird UIViewAnimation verwendet, um den UIDatePicker auf dem Bildschirm nach oben und außerhalb des Bildschirms nach unten zu schieben. Für iOS 7.x wird der UIDatePicker der Tabellenansicht inline hinzugefügt.

Die Aktionsmethode des UIDatePicker legt die NSDate-Eigenschaft der benutzerdefinierten Tabellenzelle direkt fest. Darüber hinaus zeigt dieses Beispiel, wie die NSDateFormatter-Klasse verwendet wird, um das datumsformatierte Erscheinungsbild der benutzerdefinierten Zelle zu erzielen.

Geben Sie hier die Bildbeschreibung ein

Sie können den Beispielcode hier herunterladen: DateCell .


Apples Code hat Probleme. Siehe meine Antwort unten, wenn Sie Ihre statische Tabelle behalten möchtenView
Aaron Bratcher

1
@AaronBratcher Ich füge Datecell einfach in meine Antwort ein und du erwartest nie, dass du genau den Code bekommst, den du willst. Sie müssen nach Ihren Wünschen modifizieren und ändern. Ihre Lösung ist auch gut, aber denken Sie daran, dass Sie nie genau wissen, was Sie von anderem Code benötigen.
Nitin Gohel

3
schrecklichster Beispielcode. Sie könnten einige Ratschläge
gebrauchen,

2
Wow, der Apple-Code ist ... nun, sagen wir, der Fokus liegt auf dem Effekt der Tabellenansicht. Ich hoffe, niemand wird von ihrem Code in diesem Beispielprojekt inspiriert: s
Daniel

84

Sie können die Antwort verwenden, die ich zuvor unten gegeben habe, oder diese neue Klasse in Swift verwenden, die ich erstellt habe, um diese Aufgabe viel einfacher und sauberer zu gestalten: https://github.com/AaronBratcher/TableViewHelper


Ich finde den von Apple bereitgestellten Code in vielerlei Hinsicht problematisch:

  • Sie können keine statische tableView haben, da sie die tableView: cellForRowAtIndexPath-Methode verwenden
  • Der Code stürzt ab, wenn Sie keine zusätzlichen Zeilen unterhalb der Zelle für die Auswahl des letzten Datums haben

Bei statischen Zelltabellen definiere ich meine Datumsauswahlzelle unter meiner Datumsanzeigezelle und habe ein Flag, das angibt, ob ich sie bearbeite. Wenn ja, gebe ich eine angemessene Zellenhöhe zurück, andernfalls gebe ich eine Zellenhöhe von Null zurück.

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

Wenn auf die Zeile mit dem Datum geklickt wird, ändere ich das Flag und führe die Aktualisierungsanimation durch, um die Auswahl anzuzeigen.

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

Wenn ich mehrere Datums- / Zeitauswahlelemente in derselben Tabelle habe, setze ich die Flags beim Klicken entsprechend und lade die entsprechenden Zeilen neu. Ich habe festgestellt, dass ich meine statische Tabelle behalten, viel weniger Code verwenden und leichter verstehen kann, was passiert.


Dies hat bei mir funktioniert, außer dass es einen Fehler in UITableView zu geben scheint. Die Zeilenhöhe wurde in meiner Methode korrekt zurückgegeben, aber es wurde tatsächlich die entgegengesetzte Höhe umgeschaltet, die ich wollte. Wenn ich die Zeilen zweimal neu geladen habe, hat es funktioniert. (Ich habe tableView reloadData nicht verwendet, weil ich das Ganze nicht neu laden wollte). Auch der Animationsblock war nicht notwendig, er scheint von alleine ganz gut zu animieren.
Chris Garrett

Hat jemand das Beispiel verwendet, um eine Textauswahl zu erstellen? Wenn das so ist, wie? Danke
TheGeezer

1
Sie möchten eine Zeile anzeigen, damit der Benutzer Text eingeben kann, anstatt ein Datum auszuwählen? Wenn ja, ist der Code der gleiche. Nur der Inhalt der aufgedeckten Zeile ist unterschiedlich.
Aaron Bratcher

22
Dort haben wir einen Fehler mit iOS7.1, der Objekte außerhalb der Zellengrenzen anzeigt - wie das mit der Höhe 0 und dem Picker im Inneren. Die Lösung besteht darin, einen Ausgang für die Zelle zu erhalten und cell.clipsToBounds = YES;
EmilDo

1
@Dunken - Eine Lösung wird im obigen Kommentar beschrieben: stackoverflow.com/questions/18973573/… Obwohl die Zeile eine Höhe von Null hat, wird der Inhalt standardmäßig nicht abgeschnitten, sodass Sie sie unter dem anzeigen können folgende Reihe.
Chris Nolet

18

Mit dem Storyboard und einer statischen Tabelle konnte ich mit dem folgenden Code das gleiche Ergebnis erzielen. Dies ist eine großartige Lösung, denn wenn Sie viele ungewöhnlich geformte Zellen haben oder mehrere Zellen haben möchten, die dynamisch angezeigt / ausgeblendet werden, funktioniert dieser Code weiterhin.

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

Fehlt hier noch etwas? Die Höhe der Zellen ändert sich nicht :)
DevC

2
Seit iOS 7.1 müssen Sie auch "Clip to SubViews" im Attributes Inspector auswählen - das hat mir gefehlt :)
DevC

Auch die Zeile "self.dateOpen =! Self.isDateOpen;" sollte zu Beginn "self.isDateOpen =" lauten, nicht "self.dateOpen ="
Peter Johnson

2
Der [tableView reloadData];Anruf ist nicht erforderlich. Derzeit deaktiviert es die Zeilenauswahl, aber es ist besser, die Auswahl der Zeile wie [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
Barry Haanstra

Ich hatte seltsame Animationseffekte, als ob die Datumszelle nach dem "Öffnen" leer wäre, aber die Verwendung der Anfangs- und Enddaten hat das gelöst. Schön und glatt jetzt. Danke datinc!
nh32rg

5

Ich habe die DateCell-Quelle von Apple übernommen und die Storyboard-Datei entfernt.

Wenn Sie eines ohne Storyboard wünschen, schauen Sie unter: https://github.com/ajaygautam/DateCellWithoutStoryboard


Dank @Ajay, habe ich Ihren geänderten Code und ersetzen möchten UIDatePickermit UIPickerView. Ich habe einige Dinge ausprobiert und konnte sie pickerViewauf jeder Zelle anzeigen, aber sie wird nicht mit jeder Zelle aktualisiert. Ich habe meine Frage und meinen Code hier gepostet . Bitte schauen Sie mal rein und es wäre sehr nett, wenn Sie mir eine Lösung vorschlagen könnten.
Mughees Musaddiq

Ich habe Ihre Frage gesehen ... sie enthält zu viele Informationen und erklärt nicht wirklich viel. Vielleicht kannst du deinen Code hochladen / irgendwo komprimieren, damit ich ihn mir ansehen kann. Ich kann es debuggen - wenn ich den Code ausführen kann ...
Ajay Gautam

Danke, ich schaffte es irgendwie zu ersetzen UIDatePickermit UIPickerViewaber mit paar Bugs. 1. Es stürzt ab, wenn Sie UIPickerViewdie Tabelle öffnen und scrollen. 2. Der Wert wird automatisch UILabelDetail in den unteren Zeilen der Tabelle zugewiesen, wenn Werte der Beschriftung Details in den oberen Zeilen zugewiesen werden. Hier ist mein Code
Mughees Musaddiq

Können Sie das Testprojekt finden? Haben Sie Glück?
Mughees Musaddiq

Entschuldigung, ich war ein bisschen beschäftigt. Benötigen Sie noch Hilfe dabei?
Ajay Gautam

5

Eines der besten Tutorials dazu ist iOS 7 Inline UIDatePicker - Teil 2 . Grundsätzlich verwende ich hier statische Tabellenansichtszellen und implementiere einige zusätzliche Methoden. Ich habe dafür Xamarin und C # verwendet:

Du musst aktiv sein Clip Subviews.

Höhe einstellen:

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

    return base.GetHeightForRow(tableView,indexPath);
}

Als eine Klassenvariable: private bool datePickerIsShowing = false;

Datumsauswahl anzeigen:

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

Datumsauswahl ausblenden:

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

Und diese Funktionen aufrufen:

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

    this.TableView.DeselectRow (indexPath, true);
}

2
Wenn Sie ablehnen, können Sie vielleicht erklären, warum Sie ablehnen. Anstatt Links zu Websites zu veröffentlichen, habe ich auch Code eingefügt. In meinem Fall ist es C #. Ich weiß nicht, was daran falsch ist.
Testen

3

Ich habe meinen eigenen benutzerdefinierten Ansichts-Controller erstellt, um das Hinzufügen einer Inline-Auswahl in einer Tabellenansicht zu vereinfachen. Sie unterteilen es einfach und befolgen einige einfache Regeln. Es behandelt die Präsentation der Datumsauswahl.

Sie finden es hier zusammen mit einem Beispielprojekt, das die Verwendung demonstriert: https://github.com/ale84/ALEInlineDatePickerViewController


3

Ich habe eine Antwort auf einen Fehler im Beispiel für die Datumszelle von Apple gefunden, bei dem eine Zeile unter der letzten Datumszelle stehen muss, sonst wird eine Fehlermeldung angezeigt. In der CellForRowAtIndexPath-Methode ersetzen Sie die ItemData-Zeile durch

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

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

Nach dem Ersetzen des Beispielcodes kann ich jetzt eine datePicker-Zelle anzeigen, ohne dass sich eine Zelle darunter befindet.

Ich bin gerade zu stackoverflow gekommen. Wenn dies also am falschen Ort oder woanders ist, entschuldige ich mich.


3

Die Antwort von Aaron Bratcher funktionierte nur in mehreren Abschnitten. Die Animationen waren etwas abgehackt und die nächsten Abschnitte liefen nicht sehr gut nach unten. Um dies zu beheben, habe ich die nächsten Abschnitte durchlaufen und die Zeilen um den gleichen Betrag wie die Höhe der Datumsauswahl nach unten verschoben.

Ich habe den didSelectRowAtIndexPath wie folgt bearbeitet:

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

Danke @Timmahh, ich fand die Abschnittsanimation auch abgehackt. Ich habe Ihre Lösung erfolgreich in meiner ersten Swift-App verwendet! Ich fand heraus, dass cellForRowAtIndexPath null zurückgibt, wenn sich Zellen außerhalb des Bildschirms befinden. Um dies zu vermeiden, habe ich eine IB-Outlet-Sammlung hinzugefügt, die alle meine Zellen enthält, die nach der Auswahlzelle kamen, und sie mit Ihrem neuen y_coord durchlaufen. Dies scheint jedoch unflexibel, da ich die Sammlung auf dem neuesten Stand halten muss, wenn ich neue Zellen hinzufüge.
Josh

Du hast recht. Tatsächlich hatte ich das Problem behoben, aber durch einen Stapelüberlauf konnte ich den dort eingegebenen Code nicht bearbeiten.
Timothy Logan

3

Hinzufügen zu den vorherigen Antworten,

Ich habe sowohl @datinc- als auch @Aaron Bratcher-Lösungen ausprobiert. Beide haben hervorragend funktioniert, aber die Animation war in einer gruppierten statischen Tabellenansicht nicht so sauber.

Nachdem ich ein bisschen damit gespielt hatte, kam ich zu diesem Code, der sauber und großartig für mich funktioniert -

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

Die Hauptänderung ist zu verwenden -

        [super tableView:tableView heightForRowAtIndexPath:indexPath];

Um die Zeile zu aktualisieren, werden auf diese Weise die restlichen Tabellenabschnitte und Zellen nicht animiert.

Hoffe es hilft jemandem.

Shani


Das hat enorm geholfen; Danke! Insbesondere der Aspekt [super tableView], der mit Aaron Bratchers Beitrag gebündelt wurde, brachte mir die Ergebnisse, die ich unter iOS 9.2 benötigte. Danke nochmal!
Terrance Shaw

2

Hinzufügen zu den vorherigen Antworten und @ Aaron Bratcher-Lösung ...

Seit iOS 9 bekam ich abgehackte Animationen, und das Laden der Tabelle dauerte eine Weile und war nervig genug. Ich habe es auf die Datumsauswahl beschränkt, die nur langsam vom Storyboard geladen wird. Das programmgesteuerte Hinzufügen der Picker anstelle des Storyboards verbesserte die Ladeleistung, und als Nebenprodukt ist die Animation flüssiger.

Entfernen Sie die Datumsauswahl aus dem Storyboard und haben Sie eine leere Zelle, in der Sie die Höhe wie in den vorherigen Antworten festlegen. Rufen Sie dann eine Initialisierung für viewDidLoad auf:

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

Dann implementieren Sie die Aktion zB

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

Dadurch wird die Tabelle viel schneller als zuvor geladen. Sie entfernen auch die Animationslinie aus, didSelectRowAtIndexPathda sie ohne sie reibungslos animiert (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;
    }
}

1

Die Verwendung dieser Antwort ohne Animation funktioniert in iOS 8.1 ordnungsgemäß. Ich habe es unten in Swift umgewandelt:

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

1

Hier ist eine andere Möglichkeit, das Problem ohne statische konstante Zahlen zu lösen. Alle Zellen können in statischen und dynamischen Tabellenansichten verwendet werden. Diese Methode verwendet eine einzelne Zelle sowohl für die Titel- als auch für die Datumsauswahl!

Übrigens können Sie so viele Dattelwähler in Ihrem Tisch haben, wie Sie möchten!

Erstellen Sie eine UITableViewCell- Unterklasse:

Alle Ihre Tabellenansichtszellen müssen von dieser Klasse geerbt werden und Sie müssen die Zellenhöhe für jede Zeile manuell festlegen.

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

Erstellen Sie eine CPDatePickerTableViewCell- Klasse aus unserer 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

Implementieren Sie in Ihrem View Controller diese beiden Delegate-Methoden

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

Beispiel zum Einrichten von Einschränkungen im Interface Builder

Interface Builder

Zusätzlich habe ich benutzerdefinierte Zellklassen für UITextField und UITextView geschrieben, wobei tableView: didSelectRowAtIndexPath: aufgerufen wird, wenn cell ausgewählt ist!

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

Die Zellenhöhe ist dynamisch und die Zeile wächst, wenn der Text in eine neue Zeile eingeschlossen wird!

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

Der einfachste Weg, DateCell in der Swift-Version zu verwenden: Verwenden Sie dieses Beispiel .

  1. Öffnen Sie dieses Beispiel und testen Sie es (Xcode für die Konvertierung in Swift 2 erstellen)
  2. Ziehen Sie die Klasse " DateCellTableViewController.swift " in Ihr Projekt.

  3. Öffnen Sie "Main.storyboard" und kopieren Sie das " DateCell " ViewController-Objekt und fügen Sie es in Ihr Storyboard ein.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.