Grunderna i Cocoa Touch
Nu fortsätter 99macs guideserie i IOS-utveckling med en grundligare titt på Apples grafiska gränssnitt för applayout och ramverket bakom: Cocoa Touch.
I del ett gick vi igenom de absoluta grunderna i programmering för Iphone och i del två tittade vi på grunderna i Objective C.
Alla ”vanliga” appar för IOS, och många enklare spel med för den delen, byggs ihop av komponenter ur något som Apple kallar Cocoa Touch. Detta är en stor samling #Objective C-klasser som alla har med det grafiska gränssnittet i IOS att göra – klasser som hanterar bilder och bildvisning, klasser som hanterar navigering, klasser som hanterar animation och klasser som tar hand om användarens pekningar och andra gester.
Att bygga ihop en sådan app är numera oftast en kombination av layoutarbete med det Apple kallar Storyboards, och vanlig kod som skrivs i Objective C och använder Apples klasser och deras metoder. Om begreppen känns lite luddiga kan du ta en titt i förra delen av artikelserien för en liten uppfräschare.
Storyboards är ett grafiskt gränssnitt och den grafiska representationen av en massa kod som du annars hade behövt knacka för hand – du kan visserligen göra appar helt utan, men det sparar mycket tid att slippa placera alla grafiska element för hand och som nybörjare kommer du ha nog med kod att lära känna som det är.
Börja med en app som enkel listar dina Apple-prylar
För att visa hur du kommer igång med Storyboards och att bygga enklare appar utgår vi från modellen lista/detaljer – det vill säga en lista över objekt där du kan trycka på varje objekt för att visa en sida med detaljer om just det objektet. Detta är, i olika former, en mycket vanlig modell och används som en del i många appar som även har andra funktioner – Mail till exempel, men även fotoappen i IOS har en variant av denna som dock har en mer avancerad lista.
Skapa ett nytt projekt Välj New -> Project från Arkivmenyn och klicka på Master-Detail Application. Välj ett passande namn, låt Use Storyboards och Use Automatic Reference Counting vara ifyllda, och spara projektet.
Testa sedan att köra det direkt i Iphonesimulatorn, så att du vet att det fungerar och ser vad det är vi ska utgå ifrån. Som du ser har Apple lagt till en standardfunktion för plusknappen längst upp till höger, som vi sedan kan ändra.
Klicka på .storyboard-filen i listan till vänster, dra upp fönstret så att det täcker hela skärmen, och göm både höger- och vänsterkolumnerna med cmd-0 (noll) och alt-cmd-0 (noll igen).
Då kommer det grafiska layoutgränssnittet visas stort och tydligt så att du kan titta ordentligt. Det du har framför dig är en grafisk representation av appens struktur. Längst till vänster ligger en UINavigationController, en standardklass som används som bas för att kunna navigera mellan olika vyer. Även en så enkel app som den här, med två vyer (en tabell och en detaljvy), behöver en NavigationController för att fungera – åtminstone när du bygger den med hjälp av Storyboards.
Data
Kommer du ihåg begreppet Model-View-Controller (MVC)? Det är programmeringsfilosofin som ligger till grund för vanliga IOS-appar som går ut på att hålla isär programmets komponenter utifrån deras funktion – model är klasser som hanterar data, view är klasser som visar data och controller är klasser som styr de andra typerna.
I exemplet som följer med när du skapar Master-Detail-projektet hamnar data i en array som hör till XXXMasterViewController.m (XXX är prefixet du skrev in när du skapade projektet). Detta är ett fusk som gör det lättare med en så här enkel app, men som gör det mycket krångligare om du senare bygger ut appen till att ha mer funktionalitet och inte följer MVC-tänket.
Istället bygger vi en egen, enkel, klass för att hantera data. Välj New -> File (cmd-N), klicka på Objective-C class och fyll i ett passande namn, vi väljer ApplePryl. Välj NSObject från listan under Subclass of och spara. Din datahanterare behöver hålla koll på en rad olika saker. I vårt exempel gör vi en enkel ”databas” över dina Apple-prylar, så till att börja med bör vi lägga till namn, modell, datum när du köpte den och – i förberedande syfte – en bild.
Vi kommer inte börja lägga till bilder i den här artikeln, men det är lika bra att vi lägger till möjligheten att spara en bild redan nu.
Öppna ApplePryl.h och lägg till följande kod mellan @interface- och @end-raderna:
@property (nonatomic, copy) NSString *namn; @property (nonatomic, copy) NSString *modell; @property (nonatomic, copy) NSDate *datum; @property (nonatomic, strong) UIImage *bild; -(id)initWithName:(NSString *)name model:(NSString *)model date:(NSDate *)date;
Det sista är en ny metod för att skapa ett ApplePryl-objekt med namn, modell och inköpsdatum redan bestämda (vi lägger till en vy för att skapa nya prylar i senare steg, där denna metod används för att lägga till prylen i listan).
I ApplePryl.m lägger du sedan till den så kallade implementationen av denna metod, efter @implementation-raden:
-(id)initWithName:(NSString *)name model:(NSString *)model date:(NSDate *)date { self = [super init]; if (self) { _namn = name; _modell = model; _datum = date; _bild = nil; return self; } return nil; }
I en mer avancerad app skulle vi nu skapa en klass som hanterar alla dataobjekt – lägger till, tar bort och ger info om enskilda objekt till andra objekt i programmet – men i enklare appar är det lättare att låta din controller-klass (MasterViewController) hantera data såväl som vyer.
Ta bort det onödiga
Exempelkoden är nu i vägen, så vi tar bort den. Öppna MasterViewController.m och radera följande:
- (void)insertNewObject:(id)sender { … (allt där emellan) } UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)]; self.navigationItem.rightBarButtonItem = addButton;
Ändra sedan _objects till _prylar överallt i koden, så att vi vet att vi har att göra med en lista över prylar. Om du testkör nu bör du ha en tom app utan plusknapp där Edit-knappen inte gör något alls.
Skapa ny pryl-vy
Nu ska vi lägga till en ny plusknapp som leder till en vy där du matar in data för en ny pryl.
Öppna din Storyboard igen, och dra en Bar Button Item från listan nere till höger och placera den i menyraden i din MasterViewController (den mellersta).
Byt till fliken med ett litet reglage uppe till höger och byt Identifier till ”Add” så får den automatiskt plustecknet. Dra sedan in en ViewController och placera bredvid Detail View Controller till höger.
Skapa en ny klass (cmd-n) med UIViewController under Subclass of, och döp den till AddNewViewController. Gå sedan tillbaka till Storyboarden, markera det nya ViewControll-objektet, klicka på identitetsfliken uppe till höger (tredje från vänster) och skriv in AddNewViewController längst upp under Custom Class. Ctrl/höger-dra sedan från plusknappen till den nya vyn och välj Modal från den lilla svarta rutan som dyker upp.
Markera den nya vyn och välj Editor -> Embed In -> Navigation Controller. Detta gör att du får en menyrad längst upp där du kan placera knappar för att spara och avbryta.
Testkör appen – du ska nu kunna trycka på plusknappen och byta till en helt tom vy så när som på en bakåtknapp som automatiskt lades till.
För att bygga själva vyn drar du sedan in ett gäng Labels, ett par Text Fields och en Date Picker från listan nere till höger, så att resultatet blir ungefär som på bilden ovan. Dra även in två Bar Button Items och placera uppe i hörnen, och välj som för plusknappen, men istället Cancel på den vänstra och Save på den högra.
Dags att koda
Nu måste vi skriva kod som använder den data du matar in via den nya vyn och skapar ett nytt ApplePryl-objekt som sedan kan visas i listan.
Börja med att lägga till så kallade outlets för textfälten och datumväljaren. Markera varje i tur och ordning och ctrl/höger-dra till AddNewViewController.h, välj Outlet i den lilla menyn som dyker upp och namnge i tur och ordning: namnText, modellText respektive datum. Följande kod ska då ha lagts till automatiskt:
@property (weak, nonatomic) IBOutlet UITextField *namnText; @property (weak, nonatomic) IBOutlet UITextField *modellText; @property (weak, nonatomic) IBOutlet UIDatePicker *datum;
Dessa använder vi sedan för att skicka data till MasterViewController som skapar det nya ApplePryl-objektet. För att hantera textfält och datumväljare måste vi lägga till lite mer kod i AddNewViewController.h:
#import "ApplePryl.h" @interface NNMAddNewViewController : UIViewController <UITextFieldDelegate> @property (nonatomic, strong) ApplePryl *pryl;
Notera att vi lade till #import ”ApplePryl.h” och <UITextFieldDelegate> – detta gör att #Xcode förstår att vår klass kan hantera inmatningar i textfält, och gör att vi kan hantera prylobjekt, vilket kommer till nytta i ett senare steg.
I AddNewViewController.m lägger vi sedan till följande:
- (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return NO; }
Öppna sedan MasterViewController.h och lägg till två nya metoder:
- (IBAction)done:(UIStoryboardSegue *)segue; - (IBAction)cancel:(UIStoryboardSegue *)segue;
Dessa specificerar vi sedan i MasterViewController.m så här:
- (IBAction)done:(UIStoryboardSegue *)segue { if ([[segue identifier] isEqualToString:@"Spara"]) { NNMAddNewViewController *addController = [segue sourceViewController]; if (addController.pryl) { if (!_prylar) { _prylar = [[NSMutableArray alloc] init]; } [_prylar addObject:addController.pryl]; [[self tableView] reloadData]; } [self dismissViewControllerAnimated:YES completion:NULL]; } } - (IBAction)cancel:(UIStoryboardSegue *)segue { if ([[segue identifier] isEqualToString:@"Avbryt"]) { [self dismissViewControllerAnimated:YES completion:NULL]; } }
Dessa metoder gör att vår MasterViewController är mottaglig för en typ av metod som Apple kallar segue, och är övergångar mellan olika vyer.
Byt tillbaka till Storyboard och ctrl/höger-dra från först Cancel och sedan Done till den lilla gröna ”utgångs”-knappen med texten Exit som ligger i listan under Master View Controller.
Där ska metoderna cancel: och done: ligga, välj den som passar knappen du drar från.
Markera sedan Unwind segue from Done to Exit och skriv in Spara under Identifier i högerkolumnen. Gör samma sak för Cancel to Exit och skriv in Avbryt.
Nu har du kopplat ihop dessa knappar med metoderna ovan och gett dem identifierande namn. Testkör appen igen och se att den funkar – du ska nu kunna gå tillbaka till listvyn. Några data som du matat in kommer naturligtvis ännu inte att sparas, men det fixar vi i nästa steg.
Skicka data
Öppna AddNewViewController.m och lägg till följande metod längst ner innan @end:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@"Spara"]) { if ([self.namnText.text length] || [self.modellText.text length]) { self.pryl = [[ApplePryl alloc] initWithName:self.namnText.text model:self.modellText.text date:self.datum.date]; } } }
Det här är en standardmetod för ViewController-klasser som du själv kan lägga till för att skicka data från en klass till en annan. Den kallas precis innan övergången mellan vyerna. Här testar vi först att åtminstone ett av textfälten är ifyllt och skapar sedan ett ApplePryl-objekt med den ifyllda datan.
Denna kommer sedan hämtas av MasterViewController i metoden done: som vi skrev in i ett tidigare steg.
Visa dina prylar
För att något nytt ska visas måste vi ändra både i listvyn och detaljvyn. Vi börjar med listan. Öppna MasterViewController.m och ändra i metoden
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; ApplePryl *pryl = _prylar[indexPath.row]; cell.textLabel.text = pryl.namn; return cell; }
Den här metoden används för att ladda rätt data till rätt cell i listan. Här lägger vi helt enkelt till namnet på prylen som ska ligga där som en label. Därefter måste du även ändra metoden prepareForSegue (samma som i AddNewViewController tidigare). Denna gång så här:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@"showDetail"]) { NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; ApplePryl *pryl = [_prylar objectAtIndex:indexPath.row]; [[segue destinationViewController] setDetailItem:pryl]; } }
Vad vi gör är att vi tar rätt pryl från listan över prylar, utifrån den rad i listan som du tryckte på – indexPathForSelectedRow ger den raden och [_prylar objectAtIndex:indexPath.row] väljer objektet i listan på den positionen.
Öppna nu din Storyboard igen och designa detaljvyn som på bilden ovan. Ta först bort den label som redan ligger i mitten. Ctrl/höger-dra sedan från de två labels som ska hålla de faktiska uppgifterna till DetailViewController.h och skapa två Outlets som du döper till modellEtikett och datumEtikett.
Dra sedan in en Navigation Item från listan nere till höger och lägg i menyradan. Ctrl/höger-dra från denna på samma sätt för att skapa en Outlet, som du döper till titel.
Lägg till #import ”ApplePryl.h” längst upp i DetailViewController.h, och ändra detailItem så här:
@property (strong, nonatomic) ApplePryl *detailItem;
Öppna sedan DetailViewController.m och ändra configureView-metoden till följande:
- (void)configureView { // Update the user interface for the detail item. if (self.detailItem) { self.titel.title = self.detailItem.namn; self.modellEtikett.text = self.detailItem.modell; NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; [dateFormat setDateFormat:@"yyyy-MM-dd"]; NSString *datumText = [dateFormat stringFromDate:self.detailItem.datum]; self.datumEtikett.text = datumText; } }
Och därefter setDetailItem så här:
- (void)setDetailItem:(ApplePryl *)newDetailItem { … (lämna orört) }
Det enda vi gör är att ändra den klass som objektet detailItem förväntas ha till vår dataklass, ApplePryl. Detta är strikt taget inte nödvändigt, men kan ibland underlätta om du stöter på buggar i dina egna program. Det gör helt enkelt att inga andra klasser ”duger” för denna metod och inte av misstag kan lägga till ett objekt som inte fungerar med hur vår vy visas.
Kör din app igen – om allt har gått som det ska kan du nu lägga till nya prylar som sedan hamnar i listan. Det är fortfarande långt ifrån perfekt, men du har åtminstone sett hur du kan skapa en navigationshierarki och lägga till helt nya objekt som sedan visas i appen.
Nästa steg blir att se till att tangentbordet kan försvinna, att datumväljaren blir lite vettigare (varför ska du behöva ställa in tid till exempel?) och framför allt: Att utsmycka appen med bilder och bakgrunder.
Vi fortsätter med denna app i nästa avsnitt av artikelserien genom att knyta ihop lösa trådar och börja fixa så att den blir lite snyggare. IOS-utveckling handlar nästan mer än någon annan #utveckling om finslipning och att verkligen göra appen helt klar.
Litteraturlista
Your Second IOS App – en liknande app som Apple visar dig hur du gör, men något mer avancerad och som förutsätter att du kan betydligt mer. Bra referens när du kommit igång lite mer, men vill kolla upp hur du hanterar segues och annat i en Navigation Controller-styrd app. PDF hos Apple.
IOS Human Interface Guidelines – det här är Guiden med stort G för allt vad gränssnittsdesign för IOS heter. Apple hade en Human Interface Guideline redan på den ursprungliga Macintoshens tid, och koncepted är detsamma nu som då: Visa utvecklare hur en riktig IOS-app är uppbyggd och varför. PDF hos Apple.
Start Developing iOS Apps Today – Apples motsvarighet till den här artikelserien. Apple har många andra dokument och guider, men dessa är några av de som kanske passar just nu i din utveckling. PDF hos Apple.
Beginning IOS 6 Development: Exploring the IOS SDK – David Mark, Jack Nutting, Jeff LaMarche och Fredrik Olsson skriver en av de mest lättlästa introduktionsböckerna till IOS-programmering, som kan ta dig från gröngöling till nästan mogen att göra en riktig app på egen hand. Välskriven och enkel, men utan att kringgå viktiga ämnen som du behöver i mer avancerad utveckling. Rekommenderas! Pris cirka 150 kronor.