15.05.2012

Xcode 4, iPhone, Cocoa Touch, iOS4, iOS5

Tutorial: UITableView mit NavigationController

In diesem Tutorial wird erklärt, wie eine UITableView erstellt wird, die beim Klick auf eine Tabellenzeile eine neue View öffnet. Von der View kann über einen UINavigationController wieder zurück in die UITableView navigiert werden.

Dieses Tutorial setzt auf dem Beispiel aus Tutorial UITableViewController mit Sections auf.

Die folgenden beiden Screenshots zeigen die fertige iPhone-App:

Als erstes wird eine Detailansicht mit ⌘+N angelegt. In dem Fenster wählt man unter iOS den Eintrag Cocoa Touch aus. Als Template wird Objective-C class selektiert und mit Next bestätigt.

Create New Project

Als Namen für die Class wählt man DetailViewController und klickt auf Next.

Create New Project

Dann wird mit Create die Klasse erzeugt.

Create New Project

Nun muß in der Interface-Datei DetailViewController.h noch festgelegt werden, daß es sich um einen ViewController handelt. Dazu wird einfach UIViewController ergänzt, so daß der Inhalt folgendermaßen aussieht:

//
//  DetailViewController.h
//  TableViewApp
//
//  Created by Jörn Hameister on 15.05.12.
//  Copyright (c) 2012 http://www.hameister.org. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface DetailViewController : UIViewController

@end

Dieser Schritt ist wichtig, da sonst im nächsten Schritt keine Verbindung zwischen File's Owner und DetailViewController hergestellt werden kann.

Als nächstes wird eine neue XIB-Datei für den DetailViewController mit ⌘+N angelegt. In dem Fenster wählt man unter iOS den Eintrag User Interface aus. Als Template wird Empty selektiert und mit Next bestätigt.

Create New Project

Als Device Family läßt man unverändert iPhone stehen und bestätigt den Dialog mit Next.

Create New Project

Unter Save As: wird den Name der XIB-Datei eingetragen. Da ViewController und zugehörige XIB-Datei den gleichen Namen haben sollten, wählt man DetailViewController und schließt den Dialog mit Create

Create New Project

Nun öffnet sich der Interface Builder mit einer leeren Ansicht. Dort sucht man in der Object Library nach UIView und zieht diese nach links in Oberfläche.

Create New Project

Nun wird File's Owner selektiert und im Identity inspector als Class der Name des ViewControllers DetailViewController eingetragen. Abschließend wird mit einem ctrl-Klick auf den File's Owner die Verbindung zwischen dem Outlet view und der grünen View hergestellt.

Create New Project

Als erstes wechseln wir in den Editor Assistent mit ⌥⌘↩ und sorgen dafür, daß links DetailViewController.xib und rechts DetailViewController.h angezeigt wird.

In der Object Library sucht man nun nach UILabel und zieht das Label nach links in UIView und paßt die Größe an. Anschließend kann die Ausrichtung (Alignment) des Labels angepaßt werden, so daß der Text mittig angezeigt wird.

Da das Label vom DetailViewController aus gefüllt werden soll, muß ein Outlet dafür erstellt werden. Dazu selektiert man das Label und zieht mit ctrl und gedrückter Mousetaste eine Linie in die Header-Datei auf der rechten Seite.

Create New Project

In dem Dialog trägt man als Name den Wert labelFontType ein.

Create New Project

Was jetzt im DetailViewController noch fehlt, ist eine Property zum Setzen des Font-Namen. Deshalb wird in der Header-Datei die folgende Zeile ergänzt.

@property (nonatomic, strong) NSString* fontName;

Außerdem muß in der Datei DetailViewController.m der Aufruf @synthesize fontName; hinzugefügt werden. Nun kann vom ViewController aus der fontName gesetzt werden.

Als letztes muß noch die Methode viewWillAppear: ergänzt werden. Diese Methode wird aufgerufen, wenn die View angezeigt wird. Das ist eine gute Stelle um in dem Label den Font-Namen zu setzen, der angezeigt werden soll.

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    labelFontType.font = [UIFont fontWithName:self.fontName size:30.0];
    labelFontType.text = self.fontName;

}

Für das Label wird hier der Font-Type anhand des übergebenen Namens gesetzt. Außerdem wird als text der Font-Name in das Label geschrieben.

Jetzt ist der DetailViewController fertig. Damit er benutzt werden kann, müssen folgende Dinge ergänzt werden:

  • Für die UITableViewCells muß ein AccessoryType zum Navigieren in die DetailView hinzugefügt werden.
  • Damit auf den Klick in die Tabellenzelle reagiert wird, muß der ViewController eine Methode des UITableViewDelegate implementieren.
  • Der UINavigationController muß als RootViewController gesetzt werden.

Der AccessorType wird in der Methode tableView:cellForRowAtIndexPath: gesetzt indem das Property accessoryType von cell mit einem der folgenden Werte belegt wird:

  • UITableViewCellAccessoryNone
  • UITableViewCellAccessoryDisclosureIndicator
  • UITableViewCellAccessoryDetailDisclosureButton
  • UITableViewCellAccessoryCheckmark

Standardmäßig ist der erste Werte gesetzt. Wir verwenden in dem Beispiel den zweiten Wert, wie in folgender Methode zu sehen ist.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
    if(!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"];

        //Details button
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }

        [[cell textLabel] setText:[self.model fontNameAtIndex:[indexPath row] inFamily:[indexPath section]]];
    return cell;
}

Anschließen wird in der Datei ViewController.m folgende Methode ergänzt:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    DetailViewController* detailsViewController = [[DetailViewController alloc]init];
    [detailsViewController setFontName:[self.model fontNameAtIndex:[indexPath row] inFamily:[indexPath section]]];
    [[self navigationController] pushViewController:detailsViewController animated:YES];
}

In ihr wird der DetailViewController initialisiert und der fontName wird gesetzt. Außerdem wird der detailViewController dem navigationController übergeben.

Da die Methode Teil des UITableViewDelegate ist, würde man normalerweise erwarten, daß man die Header-Datei um diesen Delegate erweitern muß, damit die Methode aufgerufen wird. Dies ist aber nicht der Fall. Grund dafür ist, daß der ViewController keine Nib/Xib-Datei besetzt. In diesem Fall wird der Delegate einfach auf self gesetzt.

Was jetzt noch fehlt, ist das Setzen des UINavigationController als RootController. Das muß in der Datei AppDelegate.m gemacht werden. Also wird die Methode application:didFinishLaunchingWithOptions: so angepaßt, daß sie folgendermaßen aussieht:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.


    ViewController* vc = [[ViewController alloc]init];
    UINavigationController* nc = [[UINavigationController alloc]initWithRootViewController:vc];
    [[self window]setRootViewController:nc];

    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

Prinzipiell ist die App nun lauffähig, allerdings stört auf der ersten Seite noch die leere Navigation-Bar oben. Bei dieser App wird sie nicht benötigt und soll ausgeblendet werden. Außerdem steht in der Navigation-Bar auf der zweiten Seite in den Zurück-Button Back. Es wäre schöner, wenn dort Zurück stehen würde.

Also wird beim Erscheinen der UITableView einfach die UINavigationBar ausgeblendet. Dazu ergänzt man folgende Methode in der Datei ViewController.m:

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.navigationController setNavigationBarHidden:YES];

}

Nun muß man dafür sorgen, daß sie in der DetailView wieder eingeblendet wird. Dazu ergänzt man in der Datei DetailViewController.m die dritte Zeile:

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.navigationController setNavigationBarHidden:NO];

    labelFontType.font = [UIFont fontWithName:self.fontName size:30.0];
    labelFontType.text = self.fontName;
}

Zum Ändern des Titels des Back-Button muß die init-Methode in der Datei ViewController.m angepaßt werden.

- (id)init {
    self = [super initWithStyle:UITableViewStyleGrouped];
    if (self) {
        UINavigationItem *n = [self navigationItem];
        n.title = @"Zurück";
    }
    return self;
}

Es wird einfach das UINavigationItem vom NavigationController abgefragt und der title angepaßt.

Nach dem Kompilieren und Starten mit ⌘+R öffnet sich der Simulator und zeigt die App an.

Als kleine Fingerübung kann man den DetailViewController um einen UISlider, ein UILabel und ein UITextView erweitern, so daß die Detailansicht folgendermaßen aussieht:

Apps, die diese Funktionalität haben, findet man im AppStore. Unglaublich, aber wahr!