Feb 28 2009

iPhone Development: Nav Bar Mini HowTo

Nachdem sich das letzte Mini HowTo it dem “Root”-Navigationselement, der Tab Bar beschäftigt hat, will ich hier die Verwendung der Navigation Bar zeigen. Wenn keine Tab Bar verwendet wird, dann kann die Navigation Bar das “höchste” Navigationselement der Anwendung sein, ansonsten muss es in ein View der Tab Bar eingebaut werden.

Beispiele für die Navigation Bar finden sich in vielen Applikationen, so wird dies zum Beispiel bei den Kontakten verwendet, um einen neuen Kontakt anzulegen, oder um von einem Kontakt wieder auf die Kontaktliste zurückzukommen.

Wie bereits zuvor erwähnt verwende ich für sämtliche Applikationen die Window-Based Application als Vorlage. Äquivalent zur Anwendung der Tab Bar wird die Datei MainWindow.xib im Interface Builder geöffnet und ein Navigation Controller in das Fenster gezogen. Das Resultat sollte dann wie folgt aussehen:

tutorialnavigationbar-bild-1 tutorialnavigationbar-bild-2

Anschließend wird dieser Controller im AppDelegate in XCode wieder als IBOutlet hinzugefügt:

TutorialNavigationBarAppDelegate.h:

1
2
3
4
5
6
7
8
9
10
11
#import <UIKit/UIKit.h>

@interface TutorialNavigationBarAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow                *window;
    UINavigationController  *navController;                                     // LINE ADDED
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController   *navController; // LINE ADDED

@end

TutorialNavigationBarAppDelegate.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#import "TutorialNavigationBarAppDelegate.h"

@implementation TutorialNavigationBarAppDelegate

@synthesize window;
@synthesize navController;                                                  // LINE ADDED

- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    [window addSubview:[navController view]];                               // LINE ADDED

    // Override point for customization after application launch
    [window makeKeyAndVisible];
}

- (void)dealloc {
    [window release];
    [super dealloc];
}

@end

Im Anschluss werden die Outlets im Interface Builder verbunden, d.h. der Navigation Controller wird rechts angeklickt und der Kreis des “New Referencing Outlet” auf das Tutorial Navigation Bar App Delegate gezogen. Das Resultat sollte dann wie folgt aussehen:

tutorialnavigationbar-bild-3

Die Applikation sollte jetzt bereits laufen und eine leere View mit einer Navigation Bar anzeigen:

tutorialnavigationbar-bild-4

Um die Navigation Bar vernünftig verwenden zu können, wird einerseits ein Eingabe-Element benötigt, mit dem auf eine andere View umgeschaltet werden kann, andererseits auch noch diese View, auf die umgeschaltet werden soll.

Im XCode erstelle ich daher eine neue Datei vom Typ “User Interfaces” “View XIB”, die ich View2.xib taufe.

tutorialnavigationbar-bild-5 tutorialnavigationbar-bild-6

Diese Datei wird nun mit dem Interface Builder geöffnet. Damit diese hier auch der richtigen Größe entspricht, kann man über “View Attributes” als Top Bar eine Navigation Bar simulieren.

tutorialnavigationbar-bild-7

Diese View bekommt ein Label, welches ich “displayText” taufe, damit man zumindest irgendetwas erkennen kann.

tutorialnavigationbar-bild-8 tutorialnavigationbar-bild-9

Weiters muss für diese View auch wieder ein View Controller erstellt werden. Dazu erstellt man im XCode eine neue Datei vom Typ “UIViewController subclass”, die ich View2ViewController taufe.

tutorialnavigationbar-bild-10 tutorialnavigationbar-bild-11

In diesen beiden Dateien erstelle ich ein IBOutlet für das Label, damit man den Text auch modifizieren kann. Die beiden Dateien sehen danach wie folgt aus:

View2ViewController.h:

1
2
3
4
5
6
7
8
9
#import <UIKit/UIKit.h>

@interface View2ViewController : UIViewController {
    UILabel                 *displayText;                                       // LINE ADDED
}

@property (nonatomic, retain) IBOutlet UILabel  *displayText;                   // LINE ADDED

@end

View2ViewController.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#import "View2ViewController.h"

@implementation View2ViewController

@synthesize displayText;                                                    // LINE ADDED

/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
}
*/


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

- (void)dealloc {
    [super dealloc];
}

@end

View Controller und View müssen im Interface Builder wieder miteinander verbunden werden. Als Class für den “File’s Owner” verwendet man den eben erstellten View2ViewController:

tutorialnavigationbar-bild-12

Und das “New Referencing Outlet” des displayText Labels mit dem displayText Attribut des “File’s Owner”. Das Resultat sollte dann wie folgt aussehen:

tutorialnavigationbar-bild-13

Außerdem muss noch das View Attribut des “File’s Owner” mit der View verbunden werden. Dazu wird der “File’s Owner” rechts angeklickt, und der Kreis neben “view” auf die View gezogen. Das Ergebnis sollte so aussehen:

tutorialnavigationbar-bild-14

Die Umschaltung auf diese View muss nun irgendwie ausgelöst werden, daher füge ich in der MainWindow.xib einen Button hinzu (im Normalfall wird dies vermutlich eher aus einer TableView geschehen), den ich detailsButton taufe. Das sieht im Interface Builder dann wie folgt aus:

tutorialnavigationbar-bild-15

Wie man an diesem Bild erkennen kann, wird die View durch den Button ersetzt. Wenn man eine eigene View einsetzen will (was ich durchaus als sinnvoll erachte), so sollte man dem View Controller hier eine eigene XIB Datei angeben (allerdings bin ich mir nicht ganz sicher ob das funktioniert, weil es beim Tab Bar Controller ja nicht geklappt hat).

Anschließend füge ich die Outlets und die Action im AppDelegate ein (eine eigene View hätte hier den Vorteil, das die App Delegate nicht so überfüllt ist). Zustätzlich muss auch die neue View hier eingetragen werden. Die neuen App Delegate Dateien sehen dann so aus:

TutorialNavBarAppDelegate.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#import <UIKit/UIKit.h>

#import "View2ViewController.h"

@interface TutorialNavigationBarAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow                *window;
    UINavigationController  *navController;

    UIButton                *detailsButton;                                         // LINE ADDED

    View2ViewController     *view2Controller;                                       // LINE ADDED
}

@property (nonatomic, retain) IBOutlet UIWindow                 *window;
@property (nonatomic, retain) IBOutlet UINavigationController   *navController;

@property (nonatomic, retain) IBOutlet UIButton                 *detailsButton;     // LINE ADDED

@property (nonatomic, retain) View2ViewController               *view2Controller;   // LINE ADDED

- (IBAction) buttonPressed:(id)sender;                                              // LINE ADDED

@end

TutorialNavBarAppDelegate.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#import "TutorialNavigationBarAppDelegate.h"

@implementation TutorialNavigationBarAppDelegate

@synthesize window;
@synthesize navController;
@synthesize detailsButton;                              // LINE ADDED

@synthesize view2Controller;                            // LINE ADDED

- (IBAction) buttonPressed:(id)sender {                 // BLOCK ADDED
    if ( view2Controller == nil ) {
        view2Controller = [[View2ViewController alloc] initWithNibName:@"View2" bundle:nil];
        [view2Controller displayText].text = @"Details";
    }

    [navController pushViewController:view2Controller animated:YES];
}

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    [window addSubview:[navController view]];

    // Override point for customization after application launch
    [window makeKeyAndVisible];
}

- (void)dealloc {
    [window release];
    [super dealloc];
}

@end

Die buttonPressed Methode erzeugt eine neue View auf Basis des View2ViewControllers mit der View2 XIB als Resource, wobei der Text des Labels mit “Details” beschriftet wird. Anschließend wird der Navigation Controller aufgefordert, die View entsprechend umzuschalten.

Im Anschluss werden die ganzen Dinge wieder im Interface Builder verbunden, d.h. das “New Referencing Outlet” des detailsButton mit dem Tutorial Nav Bar App Delegate displayButton Attribut und das Touch Down Event des detailsButton mit der buttonPressed Methode des Tutorial Nav Bar App Delegates. Das Resultat ist hier dargestellt:

tutorialnavigationbar-bild-16

Das fertige Resultat sollte nun so aussehen:

tutorialnavigationbar-bild-17 tutorialnavigationbar-bild-18

Falls man einen Add Button einsetzen will, muss man dies in der viewDidLoad Methode des entsprechenden ViewControllers hinzufügen (bei diesem Beispiel funktioniert dies daher nur im View2ViewController, da die Startseite keine eigene View besitzt). Der folgende Code erledigt dies:

1
2
3
4
5
6
7
8
9
- (IBAction) addButtonPressed:(id)sender {
    // TODO Add Implementation
}

- (void)viewDidLoad {
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addButtonPressed:)];

    [super viewDidLoad];
}

Hier ist nun wieder der komplette Code des Beispiels: TutorialNavBar (ZIP-Archiv)


Feb 27 2009

iPhone Development: Tab Bar Mini HowTo

Das Interface des iPhones ist in mehrere verschiedene Komponenten unterteilt. Die Tab Bar ist dabei jene Komponenten, die am unteren Rand des Displays angezeigt wird, und zwischen verschiedenen Darstellungen umschaltet.

Beim Telefon ist dies beispielswiese Favoriten, Anrufliste, Kontakte, Ziffernblock und Voicemail; bei der Uhr Weltuhr, Wecker, Stoppuhr sowie Timer.

Eine Tab Bar ist das “höchste” Navigationselement am iPhone. D.h. wenn eine Tab Bar verwendet werden soll, so sind alle anderen Ansichten dieser untergeordnet. Daher muss bei einer Applikation mit Tab Bar basierter Navigation diese Tab Bar als erstes Element implementiert werden.

Für sämtliche Applikationen am iPhone verwende ich die Window-Based Application als Vorlage. Diese generiert nur eine AppDelegate Klasse für die Anwendung sowie eine MainWindow.xib Datei, die das Interface definiert:

bild-1-new-project-template bild-2-new-project-name bild-3-initial-project

Um eine Tab Bar zu verwenden, wird zuerst die Datei MainWindow.xib im Interface Builder geöffnet und ein Tab Bar Controller in das Fenster gezogen. Das Resultat sieht wie folgt aus:

bild-4-ib-mainwindow-xib bild-5-ib-tab-bar-controller

Anschließend wird im XCode der Controller als IBOutlet hinzugefügt, d.h. die Datei TutorialTabBarAppDelegate.h wie folgt geändert:

1
2
3
4
5
6
7
8
9
10
11
#import <UIKit/UIKit.h>

@interface TutorialTabBarAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow            *window;
    UITabBarController  *tabBarController;                                      // LINE ADDED
}

@property (nonatomic, retain) IBOutlet UIWindow             *window;
@property (nonatomic, retain) IBOutlet UITabBarController   *tabBarController;  // LINE ADDED

@end

Anschließend wird der Tab Bar Controller als View der Anwendung initialisiert, d.h. die Datei TutorialTabBarAppDelegate.m wie folgt modifiziert:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#import "TutorialTabBarAppDelegate.h"

@implementation TutorialTabBarAppDelegate

@synthesize window;
@synthesize tabBarController;                                               // LINE ADDED

- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    [window addSubview:[tabBarController view]];                            // LINE ADDED

    // Override point for customization after application launch
    [window makeKeyAndVisible];
}

- (void)dealloc {
    [window release];
    [super dealloc];
}

@end

Anschließend geht es wieder zurück zum Interface Builder. Hier wird der Tab Bar Controller rechts angeklickt. Daraufhin erscheint ein Popup-Fenster, in dem man den Kreis neben “New Referencing Outlet” auf das Tutorial Tab Bar App Delegate Objekt dragt, wo dann wieder ein Popup mit dem Inhalt “tabBarController” erscheint. Dieser tabBarController entspricht der IBOutlet-Variablen, die wir zuvor gerade angelegt haben. Das Resultat sollte wie folgt aussehen:

bild-6-ib-referencing-outlet

Die Applikation sollte jetzt bereits laufen und eine funktionierende Tab Bar darstellen:

bild-7-tutorial-tab-bar-v1

Damit die ganze Anwendung überhaupt erst Sinn ergibt müssen für die einzelnen Tabs noch entsprechende Inhalte generiert werden. Dazu wird für jedes Tab eine eigene View angelegt.

Dazu wird im XCode eine neue Datei vom Typ UIViewController subclass angelegt, die den Namen “Tab1ViewController” erhält:

bild-8-new-uiviewcontroller-template bild-9-new-uiviewcontroller-name

Die dadurch erstellten Dateien lasse ich vorerst unangetastet. Weiterhin wird eine Interface Builder Datei für diesen Controller erstellt, d.h. eine neue Datei vom Typ “View XIB” mit dem Namen “Tab1View” zum Projekt hinzugefügt:

bild-10-new-uiview-xib-template bild-11-new-uiview-xib-name

Die Datei Tab1View.xib muss nun mit dem Interface Builder geöffnet werden. Für den “File’s Owner” wird im Identity Inspector als Class das NSObject durch Tab1ViewController ersetzt:

bild-12-tab1view-identity-inspector

Anschließend muss das “New Referencing Outlet” der View mit dem Attribut view des “File’s Owner” verbunden werden. Dazu dient wieder ein Rechtsklick auf View und ein anschließendes Draggen des Krieses auf “File’s Owner”.

Um die View in der Tutorial-Applikation auch identifizieren zu können, wird ein entsprechendes Label in die View gezogen. Das Resultat sieht im Interface Builder dann so aus:

bild-13-tab1view

Das View muss nun noch entsprechend in der Tab Bar eingebunden werden. Die Idee dies direkt im Interface Builder zu machen ist zwar naheliegend und funktioniert auch prinzipiell – zumindest solange bis man dann versucht das Programm auszuführen, dann endet es mit einer Exception. Also muss man doch wieder zum Code greifen. Daher muss die App Delegate wir folgt angepasst werden:

TutorialTabBarAppDelegate.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import <UIKit/UIKit.h>

#import "Tab1ViewController.h"                                                  // LINE ADDED

@interface TutorialTabBarAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow            *window;
    UITabBarController  *tabBarController;

    Tab1ViewController  *tab1ViewController;                                    // LINE ADDED
}

@property (nonatomic, retain) IBOutlet UIWindow             *window;
@property (nonatomic, retain) IBOutlet UITabBarController   *tabBarController;

@property (nonatomic, retain) Tab1ViewController            *tab1ViewController;// LINE ADDED

@end

TutorialTabBarAppDelegate.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#import "TutorialTabBarAppDelegate.h"

@implementation TutorialTabBarAppDelegate

@synthesize window;
@synthesize tabBarController;

@synthesize tab1ViewController;                                             // LINE ADDED

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    tab1ViewController =                                                    // LINES ADDED
        [[Tab1ViewController alloc] initWithNibName:@"Tab1View" bundle:nil];

    [[[[tabBarController viewControllers] objectAtIndex:0] view] addSubview:[tab1ViewController view]];

    [window addSubview:[tabBarController view]];

    // Override point for customization after application launch
    [window makeKeyAndVisible];
}

- (void)dealloc {
    [window release];
    [super dealloc];
}

@end

Äquivalent wird ein Tab2ViewController sowie eine Tab2View angelegt sowie im AppDelegate eingebaut. Das fertige Programm sieht dann wie folgt aus:

bild-14-finished-tab-1 bild-15-finished-tab-2

Der fertige Sourcecode des Programms ist hier als Download verfügbar: TutorialTabBar (ZIP-Archiv)

Viel Erfolg beim Programmieren!