Mírná vylepšení v mezích zákona - MujMAC.cz - Apple, Mac OS X, Apple iPod

Odběr fotomagazínu

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:

Seriály

Více seriálů



Software

Mírná vylepšení v mezích zákona

30. srpna 2006, 00.00 | Dnes se ještě k projektu vrátíme s trochou různých vylepšení, na nichž si ukážeme další možnosti, jež nám systém vazeb Cocoa nabízí.

Minule jsme od našeho projektu trošku odbočili, povídajíce si o převodnících hodnot, přepínačích, jež podrobněji určují způsob, kterým se daná vazba bude interpretovat, a o využití zástupných hodnot ("placeholders").

Dnes se ještě k projektu vrátíme s trochou různých vylepšení, na nichž si ukážeme další možnosti, jež nám systém vazeb Cocoa nabízí.

Ukládání a obnovení dat

Ne však hned v prvém odstavci – začneme něčím, co nemá s vazbami zhola nic společného.

Pokud sami průběžně ve vlastním projektu zkoušíte to, o čem si zde povídáme, asi jste si také uvědomili, že ve chvíli kdy díky použití NSArrayControlleru začalo být možné, aby náš model obsahoval libovolně mnoho datových objektů, začalo být po čertech nepříjemné, že s každým ukončením aplikace o všechna vložená data přijdeme.

Škoda, že nepoužíváme objektů z frameworku Core Data; v takovém případě bychom měli persistenci docela a úplně "zadarmo" – jenže se nemůžeme učit příliš mnoho věcí naráz, abychom v tom neměli zmatek, takže datové modelování systému Core Data si ponecháme na později. I tak je ale pomoc velmi snadná: už dávno jsme si v třiatřicátém dílu našeho seriálu ukázali, jaké služby nabízí Foundation Kit pro persistenci objektů; stačí tedy bohatě, přidáme-li do naší modelové třídy dvojici metod

-initWithCoder:(NSCoder*)coder {
    if (!(self=[super init])) return nil;
    [self setWidth:[coder decodeFloatForKey:@"Width"]];
    [self setFullName:[coder decodeObjectForKey:@"FullName"]];
    return self;
}
-(void)encodeWithCoder:(NSCoder*)coder {
    [coder encodeFloat:width forKey:@"Width"];
    [coder encodeObject:[self fullName] forKey:@"FullName"];
}

a hned můžeme model ukládat. Jen... musíme ještě někam přidat službu, jež toto uložení (a znovunačtení) vyvolá; mohli bychom sice změnit strukturu aplikace na práci s dokumenty, kdy bychom opět (nejen) tuto službu dostali "zadarmo", jenže zase – nyní se učíme práci s vazbami, a nikoli práci s třídami NSDocument a spol... takže to uděláme postaru a ručně: datový kontrolér rozšíříme o vlastní služby, konkrétně o ukládání a načítání dat.

Ono je to nakonec dobře, neboť v produkčním kódu velmi často užíváme vlastních datových kontrolérů, jejichž služby jsou tak či onak oproti základním třídám AppKitu obohaceny; alespoň si tedy vyzkoušíme, jak se to dělá:

  • do aplikace přidáme soubor "Controller.h", obsahující rozhraní třídy Controller, jež je dědicem třídy NSArrayController s jednou akcí save:, a soubor "Controller.m", obsahující jeho implementaci (na tu se podíváme za chvilinku);
  • soubor "Controller.h" vhodíme do okna NIBu; Interface Builder se tak dozví o existenci třídy Controller;
  • a hned v Interface Builderu prostřednictvím inspektoru "Custom Class" určíme, že kontrolér bude objektem naší nové třídy:

Zatímco pro zásadní změnu typu kontroléru z třídy NSObjectController na třídu NSArrayController tento inspektor nestačil, právě pro takové věci, jako určení naší vlastní třídy, je určen, a funguje při nich výtečně.

Díky této změně již nyní Interface Builder "ví", že náš kontrolér nabízí akci save:; můžeme tedy na ni hned připojit položku "Save" z hlavní nabídky, a můžeme třeba do okna pro větší pohodlí ještě přidat nové tlačítko "Save" a připojit jej k akci také (jde o staré dobré spojování objektů GUI s akcemi v rámci paradigmatu action/target, jež již dávno důvěrně známe – jakkoli je zajímavé si uvědomit, že jiná vývojové prostředí, kupříkladu ta založená na Javě, dodnes nezvládají ani to!)

Pak již jen vytvoříme vhodnou implementaci – je skutečně jen na pár řádků, a mohla by vypadat třeba takto:

@implementation Controller
-(NSString*)dataFilename {
  return @"/tmp/KVOTest.data";
}
-(IBAction)save:sender {
  [NSKeyedArchiver archiveRootObject:[self content] toFile:[self dataFilename]];
}
-(void)awakeFromNib {
  NSArray *a=[NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilename]];
  if (a) [self setContent:a];
}
@end

Snad to jediné, co stojí za samostatnou zmínku, je použití metody setContent: – my se již o ní zmínili jako o jedné z možností, kterak kontroléru dodat jeho obsah, a přesně to zde také děláme.

Chceme-li, můžeme se také konečně zbavit ošklivé registrace převodníku "MultiplyingTransformer" v jeho metodě load a přenést ji do metody initialize nového kontroléru, kam patří daleko spíše (ačkoli vůbec nejlépe by jí bylo v metodě initialize aplikačního kontroléru – ten však v naší triviální ukázkové aplikaci vůbec nemáme).

Filtrování zobrazených objektů

Poměrně běžná služba, již nabízejí aplikace s "tabulkovým" pohledem na množství datových objektů modelu, je možnost filtrovat zobrazená data – něco jako "ukaž mi pouze objekty, jejichž atribut 'firstname' obsahuje 'Jan'". Pojďme si tuto službu naprogramovat... eh, špatný výraz: díky tomu, že užíváme systém vazeb Cocoa a jeho kontroléry, pojďme si tuto službu během pár minut "namyšovat".

Otevřeme opět soubor "MainMenu.nib" v Interface Builderu, a na vhodné místo do okna vhodíme z palety "Cocoa–Text" vyhledávací textové pole NSSearchField – je tam docela vpravo dole. Pak je navážeme na náš kontrolér, a určíme možné vyhledávací predikáty; zvolíme třeba dva predikáty (stejně jako tomu bylo s vazbami typu "enable..." či s formátovanými textovými řetězci, může jich být libovolně mnoho).

Prvý predikát pojmenujeme "Full Name" a nastavíme jeho obsah na "fullName contains[c] $value" – jinými slovy, právě jsme řekli, že tento predikát má vybrat všechny datové objekty modelu, jejichž atribut "fullName" obsahuje textový řetězec, zapsaný do vyhledávacího pole (to je "$value"), a to bez ohledu na velikost písmen (to je "[c]") – jistě, predikáty jako takové jsme "zatím nebrali", takže to berte jako fakt; všechny možnosti, jež pro takovéto vyhledávání v Cocoa máme, si ukážeme později, až se budeme podrobně zabývat třídou NSPredicate a jejími možnostmi. Určení tohoto predikátu vypadá nějak takto:

Přidáme třeba ještě jeden predikát, nazvaný "Surname" a s nastavením "surname contains[c] $value" – ten nám umožní alternativně vyhledávat jen v samotném příjmení. A... a už nic. Můžeme aplikaci zbuildovat, spustit, a kochat se tím, jak nám NSArrayController pěkně filtruje datové objekty...

Počet objektů

Možná by ale ještě bylo příjemné, kdyby nám aplikace – obzvlášť vzhledem k čerstvě přidané možnosti filtrování – zobrazovala, kolik objektů je vlastně k dispozici, kolik z nich se právě vzhledem k platnému filtru zobrazuje, a třeba také kolik jich máme vybraných.

I to je ale díky vazbám kromobyčejně snadné: pozorní čtenáři možná ještě nezapomněli na padesátý sedmý díl našeho seriálu, v němž jsme se naučili využívat speciální atribut "@count" (a další) pro pole objektů v rámci KVC. Tehdy jsme atributy zadávali přímo v programu; to sice je možné, ale samozřejmě že původně jsou tyto služby určeny právě pro pohodlné nastavení vazby mezi objekty GUI a modelem bez jakéhokoli programování – a to si dnes nakonec ukážeme.

Na vhodné místo do okna přidáme obyčejné needitovatelné textové pole – třeba nad tabulku vedle vyhledávacího NSSearchFieldu – a abychom si vyzkoušeli speciální atributy polí (a zároveň také zástupné hodnoty, o nichž jsme si povídali minule), zobrazíme nejen počet všech, filtrovaných a vybraných objektů, ale také minimální, průměrnou a maximální šířku a výšku ze všech vybraných.

Je to jen chvilinka: pro textové pole samozřejmě použijeme mnohonásobnou vazbu "displayPatternValue"; její formátový řetězec nastavíme na

%{value1}@ selected of %{value2}@ (%{value3}@), width: %{value4}@ ≦ %{value5}@ ≦ %{value6}@, height: %{value7}@ ≦ %{value8}@ ≦ %{value9}@

a odpovídající vazby budou postupně od první po devátou

  • "selectedObjects.@count" – počet právě vybraných objektů (vida, zde se nám právě hodí atribut "selectedObjects" namísto jinak mnohem častěji využívaného atributu "selection");
  • "arrangedObjects.@count" – počet všech zobrazených objektů (v závislosti na právě platném filtrování);
  • "content.@count" – počet všech objektů bez ohledu na filtr;
  • "h" – minimální šířka ze všech vybraných objektů; nastavíme také "placeholdery" (alespoň ten "Null") na nulu – to proto, aby se zobrazilo něco rozumného i v případě, kdy žádný objekt vybraný není. Mimochodem, kdybychom zde užili vazby "h", fungovalo by to také – ale využití "selection" je efektivnější, zvláště v případě, že datových objektů je mnoho;
  • zbývající vazby nastavíme analogicky na "h", @"selection.max.width", "ht", "ht" a "ht".

A teď je to již opravdu vše – aplikace kompletně funguje:

Snad jen drobnost; zjistíme, že pokud spočtený průměr vychází jako desetinné číslo o mnoha platných místech, zobrazí se s mnoha ciframi také v GUI – což patrně není zrovna to, co chceme. Pozorným čtenářům však je řešení jasné: prostě si napíšeme jednoduchý převodník hodnot ("value transformer"), který čísla podle potřeby zaokrouhlí, a v odpovídajících vazbách jej použijeme – to si můžeme nechat jako jednoduché cvičení...

Pokud by se snad někomu nedařilo, hotový projekt obsahující všechny popsané služby nalezne na www.ocs.cz/apps/KVOTest3.zip.

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » Informace  

 » Rubriky  » Agregator  

 » Rubriky  » Software  

 

 

 

Nejčtenější články
Nejlépe hodnocené články
Apple kurzy

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: