Zpět Obsah Další

GUI a InterfaceBuilder — doplňky


Minule jsme si ukázali základní principy práce InterfaceBuilderu; zopakujme si je velmi stručně:

Standardní třída NSTableView např. obsahuje "outlet" dataSource; kterýkoli objekt, se kterým tento outlet propojíme v InterfaceBuilderu, bude pro tabulku sloužit jako zdroj dat. Standardní třída NSTextView obsahuje "akci" copy:; jestliže ji připojíme např. k tlačítku, vyvolá stisknutí toho tlačítka vždy uložení označeného textu do schránky.

To už známe z minulého dílu našeho volného seriálu. Dnes se podíváme na některé speciální služby, a příště si ukážeme praktický příklad.

File's Owner

Součástí každého NIBu jsou kromě objektů, jež jsme do něj "naházeli" z palety, a objektů, jež jsme sami vytvořili službou "Instantiate" (viz minulý díl našeho seriálu) také dva zvláštní "objekty": File's Owner, a First Responder. Podívejte se na obrázek; ikony, které je representují, vidíme hned zkraje prvé řady, na rozdíl od "normálních" objektů jako je menu nebo instance objektu MyClass mají šedé titulky:

Nejprve se podíváme na "File's Owner" (pro zjednodušení mu nadále budeme říkat FO): to je ve skutečnosti zástupce libovolného objektu, který není součástí NIBu, avšak účastní se jeho zavádění a nastavování outletů a akcí. Můžeme tedy v InterfaceBuilderu navázat akce i outlety pro FO; když se NIB zavádí do aplikace, programátor určí který objekt je FO — a jeho proměnné (outlety) se pak změní (budou do nich uloženy odkazy na objekty, ne které byly "nadrátovány"), a jeho metody (akce) budou volány když v uživatelském rozhraní dojde k odpovídající události (jež byla "nadrátována" na akci FO).

Účelem této na první pohled snad trochu podívné služby je umožňit vazbu mezi objekty z NIBu a objekty, jež jsou vytvořeny jiným způsobem — např. programově, nebo při zavedení jiného NIBu. Vzpomeňme si, jak jsme v minulém dílu vytvořili objekt třídy MyClass: prostě jsme InterfaceBuilderu dali na vědomí jeho specifikaci (prostřednictvím hlavičkového souboru), a pak jsme prostě službou Instantiate vytvořili instanci. Co kdybychom však chtěli, aby tento objekt řídil dva NIBy, např. tak, aby se po provedení akce login: objevilo jméno uživatele v textovém poli v okně, které je uloženo ve zcela jiném NIBu?

Právě k tomu slouží FO. Hlavičkový soubor třídy MyClass bychom upravili takto:

 @interface MyClass:...
 {
   id text;
   IBOutlet NSWindow *window;
   IBOutlet NSTextField *userName; // nový outlet
   ...
 }
 ...

a zavedli bychom jej v InterfaceBuilderu i do druhého NIBu. V něm bychom pak prostřednictvím inspektoru určili, že FO je třídy MyClass, a pak už docela standardním způsobem "přidrátujeme" outlet k odpovídajícímu objektu (podobně jako na obrázku, jen namísto instance MyClass, již v druhém NIBu mít nebudeme, použijeme FO). Pak již jen změníme implementaci takto:

 @implementation MyClass
 ...
 -(void)login:sender {
   user=[text stringValue];
   [userName setStringValue:user]; // nastavení pole v jiném NIBu
   [window performClose:self];
 }
 ...
 @end

a jsme prakticky hotovi. Jen nesmíme zapomenout při zavádění druhého NIBu určit, že jeho vlastníkem je naše instance třídy MyClass; kdybychom druhý NIB zaváděli z některé z metod třídy MyClass, vypadal by odpovídající příkaz takto:

 [NSBundle loadNibNamed:@"DruhyNib" owner:self];

A to je opravdu vše.

First Responder

Druhým zástupným objektem je "First Responder"; v tomto odstavci pro něj budeme používat zkratku FR. Podobně jako "File's Owner", je i FR pouze zástupcem, representujícím nějaký jiný objekt — který to je se zjistí až za běhu aplikace. Je zde však jeden zásadní rozdíl — zatímco "File's Owner" odpovídá jedinému objektu (tomu, který byl určen jako vlastník NIBu při jeho zavádění), může FR odpovídat mnoha různým objektům v různých chvílích: representuje totiž "aktivní" objekt uživatelského rozhraní. To je, s trochou zjednodušení, vždy ten objekt, ve kterém je právě textový kursor. Jakmile (např. klepnutím myší) přemístíme kursor do jiného objektu (do jiného okna, do jiného textového pole apod.), změní se odpovídajícím způsobem i FR.

Z toho už by mělo být do značné míry i jasné, jaký smysl FR má: představte si, že chcete např. umístit do nabídky položku menu "Copy", a "nadrátovat" ji na takovou akci, aby vše korektně fungovalo. Pokud bychom měli triviální editor, který obsahuje jen jediné okno, mohli bychom jakž takž použít "normální drátování", se kterým jsme se seznámili minule: připojili bychom akci copy: jediného textového okna k odpovídající položce menu.

Jakmile však aplikace obsahuje více různých textových polí (a tak tomu je vlastně pro každou aplikaci), je třeba nějak zajistit, aby položka menu vyvolala kopírování právě v tom textovém objektu, se kterým uživatel zrovna pracuje: tj. v tom, ve kterém je právě textový kursor — jinými slovy, ve FR.

Samozřejmým důsledkem výše uvedeného mechanismu je to, že FR se chová malinko jinak, než všechny ostatní "objekty" v okně InterfaceBuilderu. Především, nemá žádné outlety: samozřejmě, protože representuje "každou chvíli jiný objekt", takže nelze uvažovat o tom, aby se do jeho outletů ukládaly odkazy na jiné objekty. Naopak ale má řadu akcí: jde vlastně o všechny standardní služby všech objektů, jež se mohou stát FR: kromě práce se schránkou (cut:, copy: a paste:) jsou zde služby pro formátování, práce s písmy, volba barvy, kontrola pravopisu, a mnoho dalších... Chceme-li, můžeme navíc prostřednictvím panelu "Classes" kdykoli přidat další.

Mimochodem, není třeba se bát toho, že by některý objekt, který se náhodou stane FR, nebyl schopen zpracovat některou ze zpráv, jež jsou prostřednictvím různých NIBů (nebo zcela programově) FR zasílány, a že by to vedlo k pádu aplikace. Výše uvedené tvrzení, že "FR je ten objekt uživatelského rozhraní, který právě obsahuje textový kursor", je totiž jen velmi hrubá aproximace. Knihovny Cocoa totiž pro práci s FR nabízejí poměrně komplikovaný a nesmírně precizně propracovaný systém, který zajišťuje, že zpráva dojde ke správnému cíli, a pokud náhodou takový neexistuje — není vůbec odeslána. My se s tímto mechanismem podrobněji seznámíme časem, až si budeme ukazovat standardní třídu NSResponder. Prozatím si řekneme jen tolik, že pokud "objekt s kursorem" nedokáže zpracovat zprávu, poslanou FR, systém ji nabídne několika dalším logickým příjemcům (mezi něž patří mj. okno, které obsahuje "objekt s kursorem"); a jestliže zprávu nezpracuje nikdo, nic se nestane.

V praxi tedy nevadí, jestliže se dočasně FR stane objekt, který není schopen pracovat se schránkou: položka menu "Copy", připojená na akci copy: FR v takovém případě ověří, zda náhodou není práce se schránkou implementována na úrovni okna, jež zmíněný objekt obsahuje — a pokud ne, neudělá nic. Cocoa dokonce obsahuje mechanismus — s nímž se seznámíme až se budeme zabývat standardními třídami, jež representují menu --, který zajistí, že v tomto posledním případě, kdy odpovídající zprávu nedokáže zpracovat nikdo, je položka menu automaticky zobrazena šedivě.

To je pro dnešek všechno...

Základy InterfaceBuilderu jsme již probrali; příště si ukážeme slíbený komplexnější příklad.


Zpět Obsah Další

Copyright © Chip, O. Čada 2000-2003