Wstęp

W tej części samouczka postaram się pokazać jak stworzyć aplikację natywną wykonującą proste obliczenia na liczbach. Będę starał się nie pominąć żadnych ważnych elementów. Jeżeli jednak zdarzyłoby się coś niejasnego dajcie znać w komentarzach.

W tej części poznacie między innymi jak:

  1. Stworzyć nowy projekt – w ramach przypomnienia
  2. Stworzyć widok
  3. Stworzyć kontroler widoku
  4. Obsłużyć zdarzenia
  5. Dodać widok do okna
  6. Połączyć obiekt w widoku z obiektem w kontrolerze

Nowy Projekt

Zacznijmy od otwarcia środowiska XCode i przeskoczeniu ewentualnego ekranu witającego. Tworzymy nowy projekt wybierając z menu File -> New Project i wybraniu Window-Based Application z grupy iPhone OS. Wybieramy nazwę projektu „BaseCalculator” i zapisujemy. Użyta nazwa projektu będzie później wykorzystywana, dlatego jak wybierzesz inną pamiętaj o tym.

Część z plików została stworzona już automatycznie. Jednym z nich jest plik main.m, który posiada tylko jedną metodę, przyjmującą dwa argumenty i zwracający zmienną typu int.

#import 

int main(int argc, char *argv[]) {

	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	int retVal = UIApplicationMain(argc, argv, nil, nil);
	[pool release];
	return retVal;
}
Główna funkcja aplikacji dla iPhona

Główna funkcja aplikacji dla iPhona

Metoda ta będzie miała taki sam kształt, w prawie wszystkich aplikacjach na telefon iPhone.

Kolejnym ważnym plikiem stworzonym automatycznie jest delegator aplikacji, w tym projekcie nazywa on się BaseCalculatorAppDelegate. Definicja klasy jest w pliku o rozszerzeniu .h a implementacja w pliku .m. Tak stworzony projekt można uruchomić kombinacją Cmd + B lub klikając Buil and Go. To co się pokaże to pusty ekran bez specjalnych funkcjonalności.

Warte tu przypomnienia są dwie rzeczy, o których trzeba pamiętać projektując aplikację na iPhona:

  1. Aplikacja posiada tylko jedno okno. W programie tworzymy tylko widoki, które pokazujemy lub ukrywamy
  2. iPhone OS nie wspiera zarządzaniem poprzez garbage collection.

Tworzenie Widoku

W celu stworzenia widoku, musimy stworzyć plik o rozszerzeniu xib. Pliki nib służą do projektowania interfejsu używając programu Interface Builder, dostarczanego wraz z SDK. Jeden taki plik mamy już w projekcie stworzony automatycznie, który opisuje główne okno. To samo, które można obejrzeć po uruchomieniu projektu w symulatorze. By edytować ten plik wystarczy dwa razy kliknąć na MainWindow.xib

MainWindow.xib posiada już cztery obiekty (screenshot ma jeden dodatkowy o którym za chwilę):

Obiekty w Interface Builderze

Obiekty w Interface Builderze

  1. File’s Owner – jest to obiekt reprezentujący aplikację (UIApplication)
  2. First Responder – Jest to obiekt przechwytujący zdarzenia multi-touch (pol. zdarzenia wielodotykowe).
  3. Base Calculator App Delegate – Obiekt odpowiedzialny za wyświetlenie okna i zainicjalizowanie widoku. Jest także odpowiedzialny za przywrócenie aplikacji do poprzedniego stanu, przechwytywanie ostrzeżeń o braku pamięci, odpowiadanie na zmiany orientacji urządzenia. Obiekt ten jest powiązany z klasą BaseCalcultor App Delegate. Można to sprawdzić klikając w menu Tools -> Identity Inspector.
  4. Window – obiekt reprezentujący okno, które widać w aplikacji

W celu stworzenia nowego widoku używając Interface Buildera, klikamy w menu File -> New i dalej View. Następnie pokaże się nowy widok. Po pierwsze musimy zapisać widok w naszym projekcie Xcode. W tym celu klikamy w menu File -> Save, wybieramy katalog naszego projektu, nazywamy widok „BaseCalculatorView” i klikamy save. Jak IB zapyta do jakiego projektu dodać widok, należy wybrać projekt, który właśnie tworzymy.

Wróć do Xcode i przeciągnij stworzony widok do grupy/katalogu resources. Robimy to by zachować porządek ;)

Powróćmy do Interface Buildera. Dodamy teraz parę kontrolek do stworzonego widoku. Najpierw przywołajmy Bibliotekę do środowiska, klikając w menu Tools -> Library. Następnie dodaj dwa pola tekstowe (Text Fields), jedną etykietę (Label) i jeden przycisk (Button). Po tych zabiegach widok powinien wyglądać mniej więcej tak:

Widok po ustawieniu elementów

Widok po ustawieniu elementów

Inspektor atrybutów intefejsu iPhona

Inspektor atrybutów interfejsu iPhona

Ustawmy parę właściwości w użytych kontrolkach. Zaznacz pierwsze pole tekstowe i wybierz Attributes Inspector ( menu: Tools -> Attributes Inspector). Ustaw atrybuty:

  • Placeholder 2 – podpowiedź w polu
  • Phone PadNumber – typ klawiatury
  • Return-keyNext – klawisz zatwierdzający wpisane dane
  • Clear Context before Drawing – wyczyści zawartość pola przed wyświetleniem

Te same ustawienia zastosuj do drugiego pola. Zmień tylko Return-key na Done. Kliknij dwukrotnie na przycisku i wpisz „Dodaj”. Można także wykonać to w Attributes Inspector zmieniając atrybut Title. Zaznacz etykietę i upewnij się, że zaznaczone jest Clear Context before Drawing. Zapisz widok. Tym samym zakończyliśmy projektowanie pierwszego widoku.

Tworzenie kontrolera

Przyszedł czas na wykonanie kontrolera obsługującego naszą aplikację. Odpowiedzialny będzie on za obsługę akcji wykonywanych przez użytkownika, nawigację oraz zarządzanie pamięcią. Każdy widok w aplikacji musi być połączony z takim kontrolerem, który będzie obsługiwał funkcjonalności. Klasą odpowiedzialną w SDK za obsługę widoków jest UIViewController dostarczająca podstawowe funkcjonalności. W celu stworzenia kontrolera w XCode wybieramy File -> New File wybierając jako typ pliku UIViewController subclass. Jako nazwę wybieramy „BaseCalculatorViewController”.
Następnie trzeba stworzyć zmienne dla elementów stworzonych w widoku. Pozwoli to na połączenie widoku z kontrolerem oraz na sterowanie widokiem. W tym celu otwórz nowo utworzony plik BaseCalculatorViewController.h i dodaj kod:

#import 

@interface BaseCalculatorViewController : UIViewController {
    IBOutlet UITextField *firstNumber;
    IBOutlet UITextField *secondNumber;
    IBOutlet UILabel *result;
    NSInteger fNumber, sNumber;
}

@property (nonatomic, retain) UITextField *firstNumber;
@property (nonatomic, retain) UITextField *secondNumber;
@property (nonatomic, retain) UILabel *result;
@property (nonatomic) NSInteger fNumber;
@property (nonatomic) NSInteger sNumber;

-(IBAction)displayMessage:(id)sender;

@end

Słowo IBOutlet mówi kompilatorowi, że dana zmienna ma być widoczna dla Interface Buildera. Linijki kodu poprzedzone @property definiują właściwości klasy. Atrybut retain oznacza, że przy przypisywaniu obiekt jest zachowywany.
Zdefiniowana metoda posłuży przechwyceniu zdarzenia, kliknięcia ma klawiszu.

Obsługa zdarzeń

Wykorzystamy teraz zadeklarowaną w pliku nagłówkowym metodę. Metoda ta jest zadeklarowana ze słówkiem IBAction, które udostępnia ją Interface Builderze. Metoda ta przyjmuje za parametr uniwersalny wskaźnik obiektu, który ją wywołał, a zwraca typ void stowarzyszony ze słówkiem IBAction. Teraz przyszedł czas na zaimplementowanie metody obliczającej sumę oraz na dołożenie setterów/getterów dla właściwości klasy z użyciem słówka @synthesize (tworzy magiczne gettery/ssettery). W tym celu otwórz plik BaseCalculatorViewController.m i dodaj brakujące linie z poniższego listingu.

@implementation BaseCalculatorViewController

@synthesize firstNumber, secondNumber, result, fNumber, sNumber;

-(IBAction)displayMessage:(id)sender
{
    self.fNumber = [firstNumber.text integerValue];
    self.sNumber = [secondNumber.text integerValue];
    NSString *tmpString = nil;
    if(self.fNumber == 0 || self.sNumber == 0) {
         tmpString = [[NSString alloc] initWithFormat:@"Insert some number"];
    } else {
         tmpString = [[NSString alloc] initWithFormat:@"Result: %d",
                            (self.fNumber + self.sNumber)];
    }
    result.text = tmpString;
    [tmpString release];
}

W celach edukacyjnych została tu nadmiarowo stworzona zmienna tmpString. Pokazuje ona jak alokować (alloc), inicjalizować (initWithFormat) i uwalniać pamięć (release). Przy użyciu metody initWithFormat można tworzyć sformatowane ciągi znaków. %@ jest podmieniany na parametr podany dalej. Można też używać innych znaków formatujących np. %s, %d występujące w funkcji printf z języka C. Na końcu metody uwalniam zmienną, którą alokowałem w tej metodzie.

W stworzonym kontrolerze mamy kilka właściwości o atrybucie retain, które powinniśmy uwolnić z pamięci. Robimy to w metodzie dealloc jak poniżej:

- (void)dealloc
{
    [firstNumber release];
    [secondNumber release];
    [result release];
    [super dealloc];
}

Dodanie widoku do okna

Kolejnym krokiem jest dodanie widoku jako podwidoku istniejącego okna. W tym celu otwórz plik BaseCalculatorAppDelegate.h i stwórz zmienną typu BaseCalculatorViewController. Ponieważ używamy typu nieznanego kompilatorowi trzeba zadeklarować ten typ używając słówka @class.

#import 

@class BaseCalculatorViewController;

@interface BaseCalculatorAppDelegate : NSObject  {
    IBOutlet UIWindow *window;
    BaseCalculatorViewController *viewController;
}

@property (nonatomic, retain) UIWindow *window;
@property (nonatomic, retain) BaseCalculatorViewController *viewController;

@end

W pliku implementującym (.m) wykorzystamy metodę applicationDidFinishLaunching w celu przypisania stworzonego widoku do okna. Metoda ta jest wywoływana jak tylko aplikacja zakończy proces uruchamiania się. Powinniśmy wykorzystywać ją do ustawiania pierwszego widoku i przywracania stanu aplikacji.

#import "BaseCalculatorAppDelegate.h"
#import "BaseCalculatorViewController.h"

@implementation BaseCalculatorAppDelegate

@synthesize window, viewController;

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    //załadowanie widoku z bliku nib
    BaseCalculatorViewController *vController = [[BaseCalculatorViewController alloc]
                                       initWithNibName:@"BaseCalculatorView"
                                       bundle:[NSBundle mainBundle]];
    //ustawienie widoku
    self.viewController = vController;

    // uwolnienie zmiennej
    [vController release];

    //dodanie widoku do okna
    [window addSubview:[viewController view]];

    //uwidocznienie widoku
    [window makeKeyAndVisible];
}

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

@end

Połączenie elementów w widoku z obiektami w kontrolerze widoku

Na początku potrzebujemy połączyć elementy widoku z obiektami, które stworzyliśmy. W tym celu dwukrotnie kliknij na BaseCalculatorView.xib, który otworzy Interface Buildera.
Następnie połącz kontroler widoku z obiektem File’s Owner Proxy. W tym celu zaznacz File’s Owner i wybierz Identity Inspector w polu Class wybierz z listy BaseCalculatorViewController. Warto zauważyć, że poniżej są też wyświetlone zmienne zdefiniowane w pliku nagłówkowym.
Teraz połączymy kontroler widoku z widokiem. W tym celu kliknij na Connections Inspector, wyświetli ci się widok podobny do poniższego:

Connection inspector

Connection inspector

Obok zmiennej view jest puste kółko co oznacza, że nie jest połączone. Kliknij kółko przy view i przeciągnij na widok i puść klawisz. Zamalowane kółko będzie informowało o połączeniu. Podobnie postąp z elementami result, firstNumber, secondNumber łącząc je z odpowiednimi elementami na widoku.
Ostatnie połączenie jakie musimy wykonać to połączenie metody wyświetlającej wynik z akcją kliknięcia przycisku. W tym celu zaznacz przycisk i wybierz connection inspector. Znajdź zdarzenie „Touch Up Inside” oznaczające zakończenie kliknięcia (dotyku). Zaznacz puste kółko i przeciągnij je na obiekt File’s Owner. Kiedy zwolnisz klawisz pojawi się metoda, displayMessage, wybierz ją, a stworzysz połączenie.
Wróć do Xcode skompiluj i uruchom Build and Go. Aplikacja powinna już działać, ale pojawi się pewna niedogodność. Mianowicie po wpisaniu liczb klawiatura nie będzie się chowała :/ By to naprawić trzeba dodać protokół do klasy kontrolera.
Deklaracja interfejsu kontrolera widoku powinna wyglądać teraz tak:

@interface BaseCalculatorViewController : UIViewController <UITextFieldDelegate>  {

Kolejnym krokiem jest ustawienie kontrolera widoku jako odbiorcę zdarzeń pól tekstowych oraz zaimplementowanie metody obsługującej ukrywanie klawiatury:

- (void)viewDidLoad {
    firstNumber.delegate = self;
    secondNumber.delegate = self;
}

- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
     if(theTextField == firstNumber || theTextField == secondNumber) {
         [firstNumber resignFirstResponder];
         [secondNumber resignFirstResponder];
     }
     return YES;
}

W metodzie viewDidLoad następuje ustawienie kontrolera jako obiektu odbierającego zdarzenia pól tekstowych. Natomiast w metodzie textFieldShouldReturn sprawdzamy z jaki obiekt wywołał metodę, jeżeli te obiekty mogą powodować ukrycie klawiatury wysyłamy wiadomość resignFirstResponder do obiektów oraz zwracamy YES.

Pozostało jeszcze ustawienie delegacji dla pól tekstowych na File’s Owner (obiekt proxy dla naszego kontrollera). W tym celu w Interface Builderze wybieramy widok i klikamy na pierwszym polu tekstowym i wchodzimy do Connections Inspector. Znajdziemy tam pole delegate. Kliknij na nim i przeciągnij na obiekt File’s Owner i puść. Zrób tak samo z drugim polem.
Zapisz zmiany i uruchom aplikację w Xcode.

Koniec

Tym samym stworzyłeś swoją pierwszą aplikację na iPhona. Jeżeli coś było nie zrozumiałe daj znać w komentarzu, lub napisz na maila kamilmaras [malpeczka] gmail [kropeczka] pl.