Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!
Zadejte Vaši e-mailovou adresu:
Kamarád fotí rád?
Přihlas ho k odběru fotomagazínu!
Zadejte e-mailovou adresu kamaráda:
Začínáme s
Programování pro iOS - 4. Varovná hlášení
26. srpna 2010, 00.00 | Začneme aplikaci trochu vylepšovat. Přitom si ukážeme řadu standardních technik, jež se používají při programování aplikací v iOS. Dnes to bude práce s varovnými hlášeními - alerty.
V minulém dílu našeho seriálu jsme sestavili sice jednoduchoučkou, ale přece jen plně funkční aplikaci. Dnes ji začneme trochu vylepšovat a přitom si ukážeme řadu dalších standardních technik, jež se používají při programování aplikací v iOS; dnes si ukážeme kromě jiného práci s varovnými hlášeními ("alerty"), a hned v příštím dílu si řekneme více o řídicích objektech rámců, jež jsou v iOS velmi důležité (mnohem důležitější, než v Mac OS X).
Memoria minuitur, nisi eam exerceas
Prvé zdokonalení, jež do aplikace implementujeme, bude paměť minulých měření. Takový záznam se může hodit, a navíc ji budeme později také moci využít pro vyhodnocení toho, jestli se bouřka přibližuje nebo vzdaluje a jak rychle.
Základní implementace paměti je velmi jednoduchá: využijeme standardních tříd knihovny Foundation; každé měření uložíme do dynamického pole NSMutableArray jako slovník, obsahující dva údaje – časovou značku a naměřenou vzdálenost.
Nejprve si připravíme pole pro ukládání objektů. Nejjednodušší – a v našem případě vcelku i postačující – by ovšem bylo přidat další proměnnou objektu "NSMutableArray *history"; my ale využijeme podporu explicitních objektových atributů, již Objective C nabízí od verze 2. První důvod je didaktický – neuškodí, pokud si práci s atributy procvičíme; kromě toho ale také existuje i důvod praktický, ačkoli konkrétně v tomto případě je velmi slabý: výhodou atributů oproti jednoduchým objektovým proměnným je to, že se nemusíme starat o správu paměti (ta, jak víme, je v iOS ne zcela zanedbatelná, protože zde není k dispozici automatický garbage collector).
Jelikož iOS podporuje všechny výhody moderní architektury Objective C 2 (na rozdíl od dvaatřicetibitových prostředí Mac OS X), stačí pro kompletní podporu nového atributu pouhé dva řádky zdrojového textu; jeden v rozhraní:
@interface MainViewController:UIViewController
<FlipsideViewControllerDelegate> {
...
}
@property (retain) NSMutableArray *history;
...
@end
a jeden v implementaci:
@implementation MainViewController
@synthesize history;
...
Nyní již můžeme implementovat vlastní ukládání záznamů – mohlo by to vypadat kupříkladu takto – zároveň jsme přidali omezení na nejvýše 50 uložených měření, aby nám pole nerostlo nade všechny meze (ačkoli v současné verzi aplikace, kdy o obsah pole přijdeme při jejím ukončení, to samozřejmě není příliš podstatné; za chvilku ale budeme pole ukládat):
@implementation MainViewController
...
-(IBAction)rangeButtonTapped {
...
} else { // thunder
NSTimeInterval diff=
[NSDate timeIntervalSinceReferenceDate]-light;
light=0;
double dist=diff*330.;
range.text=[NSString stringWithFormat:
@"%.1 f km",dist/1000.];
if (!self.history)
self.history=[NSMutableArray array];
[self.history addObject:
[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithDouble:dist],@"dist",
[NSDate date],@"timestamp",
nil]];
while (self.history.count>50)
[self.history removeObjectAtIndex:0];
}
}
...
Povšimněme si několika drobností: předně, pole vytváříme "on demand", teprve ve chvíli, kdy je to skutečně zapotřebí (a ne hned při inicializaci objektu v jeho metodě init). V naprosté většině případů je tento přístup optimální: nejenže šetří paměť a snižuje čas, potřebný ke spuštění aplikace (či k přepnutí do jiného režimu apod.); navíc je spolehlivější a bezpečnější – potenciálními riziky alokace zdrojů v metodě init jsme se zabývali před časem podrobněji.
Dále pak použití cyklu while pro odstranění nadbytečných prvků v poli vypadá na první pohled jako neefektivní – nebylo by lepší odstranit všechny nadbytečné prvky najednou pomocí standardní služby removeObjectsInRange:? Inu, v obecném případě zcela jistě ano; zde je to ale zbytečné, protože přidáváme do pole vždy jen jeden záznam; odstraňovat tedy budeme zase jen jediný.
Inu dobrá, řeknete asi – pak ale proč while a ne pouhý if? To proto, že programujeme defensivně: kdybychom snad přece jen někde náhodou udělali chybu a omylem do pole přidali více prvků, zaručí nám while, že zde bude chyba napravena – a pokud je vše v pořádku, není mezi if a while v tomto případě zhola žádný rozdíl.
Konečně pak, protože nemáme k dispozici automatický garbage collector, musíme při zrušení řídicího objektu paměť záznamů uvolnit (v tomto případě bychom si to mohli "ponechat od cesty", protože řídicí objekt hlavního rámce je vytvořen jednou a nikdy se neuvolňuje; z tréninkových důvodů ale uvolnění implementujeme, ač se v praxi nevyužije):
-(void)dealloc {
self.history=nil;
[super dealloc];
}
Pozn.: mezi programátory v Cocoa existuje dlouhý a prastarý spor o to, zda v metodách init... a dealloc užívat přístupových metod /"accessors"/ nebo ne. Oficiální doporučení firmy Apple je spíše negativní; naopak autor tohoto článku to považuje z mnoha důvodů za správné. Ať tak či onak, v případech, jako je tento, vůbec neexistuje žádný způsob, jak bychom se použití přístupové metody mohli vyhnout! Je tedy zřejmé, že jde o postup správný – prostě proto, že je jediný možný.
Zobrazení historie
Máme-li tedy hotové ukládání historie, pojďme implementovat její zobrazení.
Nejprve do rámce přidáme vhodné tlačítko, do řídicího objektu MainViewController metodu showHistory, a tuto metodu propojíme pomocí mechanizmu akce/cíl s událostí "Touch Up Inside" nového tlačítka; to už všechno umíme, dělali jsme to tak s tlačítkem pro vlastní měření.
Mimochodem, proč má akce showInfo: připravená na základě projektového vzoru argument sender, zatímco naše akce jej nemají? To je tak: v iOS akce může tento argument mít a nemusí; pokud jej použijeme, bude v argumentu předán prvek uživatelského rozhraní, který akci poslal – zde tedy odpovídající tlačítko. Akce by pak mohla rozlišit způsob, jimž byla vyvolána, a podle toho nabízet mírně odlišnou funkčnost. Programátoři Apple argument do projektového vzoru dali proto, že by jej třeba někdy někdo mohl potřebovat; my jej u našich akcí nepoužíváme, protože víme, že jej potřebovat nebudeme.
Prázdná historie
Nejprve si ukážeme možnou implementaci akce showHistory v případě, kdy je historie prázdná; naučíme se přitom zobrazovat varovná hlášení ("alerty"):
-(IBAction)showHistory {
if (self.history.count==0)
[[[[UIAlertView alloc]
initWithTitle:@"No history"
message:@"The history list is empty."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil]
autorelease] show];
}
Vytvoříme instanci standardní knihovní třídy UIAlertView, nastavíme vhodným způsobem její atributy, postaráme se o její automatické uvolnění pomocí zprávy autorelease, a zobrazíme ji zprávou show. iOS zobrazí varovné hlášení a po klepnutí na jeho tlačítko je automaticky zavře; o to se starat nemusíme.
U práce s varovnými hlášeními se chvilku zdržíme, protože se od toho, nač jsme zvyklí z Mac OS X, poněkud liší. Jde o případ, kdy má hlášení více různých tlačítek (ty můžeme přidat tak, že jejich titulky – ukončené hodnotou nil – zapíšeme jako argumenty za otherButtonTitles:), a kdy v kódu je zapotřebí reagovat na to, které z tlačítek uživatel vybral.
V tomto případě musíme udělat následující:
- v rozhraní explicitně deklarovat, že náš řídicí objekt odpovídá protokolu UIAlertViewDelegate (pokud bychom to neudělali, vše by fungovalo bez nejmenších problémů, ale při překladu by se ohlásilo varování);
- namísto delegate:nil předat alertu odkaz na řídicí objekt pomocí delegate:self;
- implementovat metodu alertView:clickedButtonAtIndex: a v ní stisknutí každého tlačítka ošetřit vhodným způsobem.
Jestliže náš řídicí objekt spravuje více varovných hlášení, je to trochu složitější: bohužel, na rozdíl od Mac OS X nelze při sestavování alertu určit zprávu, kterou po stisknutí tlačítka pošle – ta je a zůstává vždy alertView:clickedButtonAtIndex:. Proto musíme buď pro různé alerty použít různých delegátů, nebo – což je obvykle pohodlnější – při sestavování alertu nastavit jeho atributy (ideální je atribut tag, zděděný od nadtřídy UIView) a podle nich jej identifikovat:
-(void)alertView:(UIAlertView*)av
clickedButtonAtIndex:(NSInteger)bi {
switch (av.tag) {
case 1:
...
}
}
-(void)fooBar {
if (...) {
UIAlertView *av=[[UIAlertView alloc]
initWith... ...] autorelease];
av.tag=1;
[av show];
}
}
Pokud píšeme aplikace pouze pro iOS 4 a vyšší, můžeme si samozřejmě snadno doplnit nesrovnatelně pohodlnější podporu práce s bloky zhruba na stejném principu, na jakém jsme si již dříve ukázali její doplnění do Mac OS X.
Naopak zase v iOS 4 musíme při používání varovných hlášení zvážit, co se má stát při "uzavření" aplikace ve chvíli, kdy uživatel stiskne tlačítko "Home". Až do verze 3 včetně stisknutí tohoto tlačítka aplikaci vždy ukončilo; tím samozřejmě také zmizelo varovné hlášení, a po příští aktivaci téže aplikace se nezobrazilo (leda bychom se o to explicitně postarali v kódu). iOS 4 naproti tomu aplikaci ponechá běžet, jen ji přemístí do pozadí a pozastaví její běh; po příští aktivaci tedy varovné hlášení zůstává viditelné. Chceme-li tedy v iOS 4 zachovat stejnou funkčnost jaká byla předtím, musíme si při otvírání alerty zapamatovat, a ve chvíli, kdy je aplikace "uzavřena" (tedy když její delegát dostane systémovou zprávu applicationDidEnterBackground:), všechny aktivní alerty uzavřít zprávou dismissWithClickedButtonIndex:0 animated:NO.
Vypnutí "multitáskingu"
U velmi jednoduchých aplikací, které se načtou a spustí opravdu rychle (a v některých speciálních případech také u složitějších, ale to prozatím ponechme stranou) se může vyplatit vyřešit (nejen) tento problém tím nejjednodušším možným způsobem – pro aplikaci zakázat "multitásking". Ta se pak bude ve všech verzích iOS chovat stejně, jako tomu bylo v iOS 3 a starších: po stisknutí tlačítka "Home" prostě skončí. Stačí do souboru Info.plist přidat položku
<key>UIApplicationExitsOnSuspend</key>
<true/>
Inspektor atributů cíle pro to specialisovaný ovladač nemá; můžeme to ale celkem pohodlně udělat přímo v okně Xcode – otevřeme-li v něm soubor (Jméno projektu)-Info.plist, Xcode zobrazí editor, v němž nejenže máme k dispozici seznam standardních klíčů ze kterého můžeme vybrat; navíc jsou klíče i hodnoty zobrazeny uživatelsky přívětivým způsobem (konkrétně jako text "Application does not run in background" a jako přepínač).
Obsah seriálu (více o seriálu):
- Nastal čas na kakao...
- Tak nejdřív kakao ochutnáme...
- Programovací jazyk C: velmi, velmi stručně
- Objective C: to si vysvětlíme podrobněji
- Co jsme si o Objective C ještě neřekli...
- Nastal čas na kakao - Vznik a zánik objektů
- Nastal čas na kakao - Kopírování objektů
- Nastal čas na kakao - Skryté podtřídy
- Nastal čas na kakao - Základní služby objektů
- Nastal čas na kakao - Jak správně psát v Objective C
- Nastal čas na kakao - Jak správně importovat
- Nastal čas na kakao - Podtřídy, delegáti, vkládání, jak se to rýmuje?
- Nastal čas na kakao - Využití kategorií namísto dědičnosti
- Nastal čas na kakao - Vkládání objektů a přesměrování zpráv
- Nastal čas na kakao - Inicializace a rušení objektů
- Nastal čas na kakao - Metody initWith... a designovaný inicializátor
- Nastal čas na kakao - Inicializace: tipy a triky
- Nastal čas na kakao - Accesory: přístup k proměnným instancí
- Nastal čas na kakao - Šedá je teorie, zelený je strom života...
- Nastal čas na kakao - Více o XCode: inspektory
- Nastal čas na kakao - Aplikace RSS2: datový model
- Nastal čas na kakao - Aplikace RSS: implementace datového modelu
- Nastal čas na kakao - Aplikace RSS: parsování XML
- Nastal čas na kakao - Interface Builder a uživatelské rozhraní
- Nastal čas na kakao - Interface Builder: atributy objektů
- Nastal čas na kakao - Interface Builder: atributy objektů
- Nastal čas na kakao - Druhý kontrolér a dokončení aplikace
- Nastal čas na kakao - Drobná vylepšení a zdokonalení...
- Nastal čas na kakao - Ladění
- Nastal čas na kakao - Třídy Foundation Kitu
- Nastal čas na kakao - Třídy Foundation Kitu (2)
- Nastal čas na kakao - Textové řetězce: NS(Mutable)String
- Nastal čas na kakao - Čísla, binární data a další...
- Nastal čas na kakao - Archivace objektů
- Nastal čas na kakao - Trocha magie, aneb distribuované objekty
- Nastal čas na kakao - Málem bychom zapomněli: NSAutoreleasePool
- Nastal čas na kakao - Zpracování výjimek: NSException
- Nastal čas na kakao - NSInvocation a černá magie
- Nastal čas na kakao - Kakao v Tygrovi
- Nastal čas na kakao - Notifikace: nepřímé předávání zpráv
- Nastal čas na kakao - NSUserDefaults
- Nastal čas na kakao - Co nového ve Foundation Kitu
- Nastal čas na kakao – s Intelem, s Intelem, jedeme do...
- Co nového v Xcode
- Začínáme s AppKitem
- Jak MVC v Kakau vypadá doopravdy?
- Jak MVC v Kakau vypadá doopravdy: dokončení
- Přehled tříd AppKitu
- Nastal čas na kakao - Přehled tříd AppKitu 2
- Přehled tříd AppKitu 3: zbývající třídy GUI
- Přehled tříd AppKitu 4: textový systém
- Nastal čas na kakao - Přehled tříd AppKitu 5: hlavně grafika
- Přehled tříd AppKitu 6: dokumentový systém
- Přehled tříd AppKitu 7: dokončení
- Pojmenované vlastnosti objektů
- Pojmenované vlastnosti objektů: implementace
- Pojmenované vlastnosti objektů: relace 1:N
- Pojmenované vlastnosti objektů: řazení jmen a agregační funkce
- Sledování změn objektů
- Sledování změn objektů – ukázka
- Sledování změn objektů – zdrojový kód
- Sledování změn objektů: kód modelu
- Sledování změn objektů: přímý přístup
- Kontroléry a vazby
- Vázání vazeb
- Další vazby s jednoduchým kontrolérem
- Implementace a použití převodu hodnot
- Validace hodnot
- Validace a chyby, a jedna hezká vazba...
- Práce s polem objektů
- Základní vazby NSArrayControlleru
- Převodníky, přepínače, placeholdery
- Mírná vylepšení v mezích zákona
- Objective C 2.0 - novinky z Leoparda
- NSTreeController
- Programování v Cocoa - Pár tipů a triků
- Programování v Cocoa - Základy kreslení
- Kterak nakreslit modrý obdélník...
- Další služby pro kreslení
- Obrázky a písmenka...
- Události a myš
- Lepší práce s myší
- Události klávesnice
- Input Management
- Příkazy a schránka
- Další události
- Táhni a padni
- Byli jsme na tahu; nyní padneme.
- Zvolme si, jak vhodit
- Drobnosti a chybičky
- Speciální případy tahání či házení
- Kterak táhnout něco, co neexistuje?
- Jak na sítě...
- NSURLConnection
- Safari za minutu
- Služby WebKitu
- Kakao v Leopardu
- Druhé Objective C
- Druhé Objective C: různé drobnosti
- Druhé Objective C: kategorie a protokoly
- Druhé Objective C: nový příkaz cyklu
- Druhé Objective C: atributy a accesory
- Druhé Objective C: atributy a accesory
- 64 je dvakrát 32
- Ubicumque dulce est, ibi et acidum invenies...
- Irbis: že prý žádné novinky?
- Blok sem, blok tam, nám už je to všechno jasné...
- Bloky jsou i v AppKitu
- Irbis a Foundation Kit
- Kde jsou má data?
- Kde jsou má data? V NSCache!
- Soubor, jméno, URL, jak se to rýmuje...
- Další podpora NSURL
- Zabíjení!
- A máme tady i...OS!
- Systémové prvky GUI
- Programování pro iOS 1. díl - Rozdíly mezi "i" a "Mac"
- Programování pro iOS - 2. Začínáme programovat
- Programování pro iOS - 3. základní ovladače a propojení GUI s kódem
- Programování pro iOS - 4. Varovná hlášení
- Programování pro iOS - 5. Rámce a jejich řídicí objekty
- Programování pro iOS - 6. Ukládání dat
- Programování pro iOS - 7. Správa paměti a starý restík
- Programování pro iOS - 8. Dokončení aplikace
- Programování pro iOS - 9. Jak dostat aplikaci do iPhone
- Programování pro iOS - 10. Instalace aplikace do cizího iPhone
- Programování pro iOS - 11. Jak dostat aplikaci do libovolného iPhone
- Programování pro iOS - 12. Touching!
- Programování pro iOS - 13. Kreslíme na iPhone
- Programování pro iOS - 14. Udělejme gesto
- Programování pro iOS - 15. Další gesta
- Programování pro iOS - 16. Více prstů, více zábavy
- Programování pro iOS - 17. Podpora standardních gest
- Programování pro iOS - 18. Recognizery v iOS
- Programování pro iOS - 19. Další standardní recognizery
- Programování pro iOS - 20. Co nového v iOSu
- Programování pro iOS - 21. "Multitasking"
- Programování pro iOS - 22. Nulla est honesta avaritia nisi temporis
- Programování pro iOS - 23. Jak se aktivovat, jsme-li v pozadí
- Programování pro iOS - 24. Zbývající drobnosti
- Programování pro iOS - 25. Řídicí objekty rámců
- Programování pro iOS - 26. Jak se dělá UIViewController
- Programování pro iOS - 27. Kde vzít rámce
- Programování pro iOS - 28. Základní služby
- Programování pro iOS - 29. Práce s rámci
- Programování pro iOS - 30. Rotace zařízení
- Programování pro iOS - 31. Správa paměti v rámcích
- Programování pro iOS - 32. Řídicí objekt pro tabulky
- Programování pro iOS - 33. Řídicí objekt pro strom
- Programování pro iOS - 33. Více o UINavigationControlleru
- Programování pro iOS - 35. Ještě jednou UINavigationController
- Programování pro iOS - 36. Po navigátoru taby
- Programování pro iOS - 37. Více o UITabBarControlleru
- Programování pro iOS - 38. Dokončení UITabBarControlleru
- Programování pro iOS - 39. UIPopoverController
- Programování pro iOS - 40. Další triky UIPopoverControlleru
- Programování pro iOS - 41. Zbývající služby UIPopoverControlleru
- Programování pro iOS - 42. UISplitViewController
- Programujeme v
iTunesXcode 4 - Programování pro iOS - 44. Předvolby Xcode 4
- Programování pro iOS - 45. Práce v Xcode 4
- Xcode 4: projekt a cíle
- Xcode 4: práce s cíli
- Xcode 4: Build Settings
- Xcode 4: Build Phases
- Xcode4: Build Phases podruhé
- Xcode 4: Co jsou to Build Rules?
- Xcode4: taje editoru
- Xcode4: automatické doplňování v editoru
- XIBy chyby
- Více o XIBech
- Editor XIBů
- Inspektory pro XIBy
- Vazby mezi objekty v XIBech
- Vazby mezi objekty v kódu
- Paletky Xcode pro XIBy
- Xcode 4: levý sloupec
- Xcode 4: okno Organizer
- Xcode 4: okno Organizer, část druhá
- Xcode 4: co je to Workspace?
- Xcode 4: základy schémat
- Xcode 4: akční schémata