Category Archives: Uncategorized

Sijaintipalvelut iOS-applikaatioissa 2 – Geokoodaus

Edellinen kirjoitukseni oli pintaraapaisu iOS SDK:n CoreLocation-kirjastoon. Kävin läpi kuinka laitteen sijainti saatiin haettua CLLocationManager-luokan avulla leveys- ja pituuspiirin koordinaatit sisältävän CLLocation-olion muodossa. Kuitenkaan pelkät koordinaatit eivät sellaisenaan ole kovin kiinnostavaa tai hyödynnettävää tietoa ja sen takia  koordinaatteihin liittyy usein kysymyksiä kuten “missä tämä paikka on?” ja “mitä täällä paikassa on?”. Vastauksen näihin tarjoaa geokoodaus.

Geokoodaus on osoitteen muuttamista koordinaateiksi, ja käänteinen geokoodaus koordinaattien muuttamista osoitteeksi. CoreLocation tarjoaa kummankin palvelun CLGeocoder-luokassa. Geokoodauksen käyttö ei edellytä paikkatietojen käytön hyväksyntää käyttäjältä. Luokka sisältää kolme metodia geokoodaukseen ja yhden metodin käänteiseen geokoodaukseen.

  • geocodeAddressString:completionHandler: Ottaa parametrinä vapaamuotoisen osoitteen, esimerkiksi “Urho Kekkosen katu 5, 00100 Helsinki”
  • geocodeAddressString:inRegion:completionHandler: Tekee samaa kuin edellinen, mutta toisena parametrina voi antaa aluetta kuvaavan CLRegion-olion jonka sisällä olevia kohteita suositaan tuloksia järjestellessä.
  • geocodeAddressDictionary:completionHandler: Parametrina annetaan NSDictionary johon voi määritellä tarkemmin haettavan alueen esim. kaupungin tai maan perusteella. Esimerkiksi parametrinä annettu NSDictionary @{(id)kABPersonAddressCityKey : @”St. Petersburg”, (id)kABPersonAddressCountryKey : @”United States”} palauttaisi Floridassa olevan St. Petersburgin kaupungin. Dictionaryn kABPersonAddressCountryKey-avaimen arvon ollessa @”Russia“, palauttaa geokoodaaja itänaapurin entistä pääkaupunkia kuvaavan vastauksen.
  • reverseGeocodeLocation:completionHandler: palauttaa listan koordinaatteja vastaavia CLPlacemark-olioita. Parametrina metodi ottaa CLLocation-olion. CLLocation-olion voi luoda ja alustaa CLLocation-oliolla.
Kaikilla neljällä metodilla on samanlainen completion handler. Se sisältää listan CLPlacemark-olioita (joita normaalisti on yksi kappale listassa) ja NSError-olion. CLGeocoding-oliolla on myös metodit isGeocoding ja cancelGeocode. Geokoodauksen kumoaminen saa aikaan kCLErrorGeocodeCanceled-tyyppisen virheilmoituksen.

Mitä täällä on?
CLPlacemark on luokka paikkatietojen esitystä varten. Oliosta löytyy alueesta riippuen mm. seuraavia tietoja:
  • paikan koordinaatit (CLLocation-olion muodossa)
  • Listan alueen maamerkeistä (esim. Golden Gate -silta San Franciscossa), listana NSString-olioita
  • paikan osoitetiodot Address Book -yhteensopivina avain-arvopareina
  • paikan nimi
  • kaupunki ja kaupunginosa
  • mahdollistet läänit ja maakunnat joiden osa paikka on
  • maa, johon paikka kuuluu, sekä maakoodi
  • vesistö tai valtameri jossa paikka sijaitsee

 Vaihtoehto Applen geokoodauspalvelulle
Applen tarjoaman geokoodauspalvelun lisäksi Googlella on Geokoodaus-API. Googlen paikkatietokanta on epäilemättä paljon kattavampi ja tarkempi kuin Applen oma, mutta Applen valmiit CLGeocoding-luokat tarjoavat hyvin nopean tavan muuttaa koordinaatteja informaatioksi ja toisin päin. Yleensä Applen palvelu riittää katuosoitteiden ja kaupunkien nimien muuttamisen koordinaateiksi ja toisin päin. Googlen palvelussa kehittäjä joutuisi itse parsimaan JSON- tai XML-muotoisen vastauksen itse. Mielestäni Applen geokoodauspalvelun suurin puute on maamerkkien, kauppojen, ravintoloiden yms. kiinnostavien kohteiden täydellinen puute. Siitä huolimatta CLGeocoder on riittänyt useimpiin kehittämiini applikaatioihin.

Push-viestit ja push-palvelin – mitä ne ovat?

Viimeisen kahden vuoden aikana push-viestien osuus mobiiliviestinnässä on kasvanut räjähdysmäisesti uusien älykkäämpien palvelujen myötä. Seuraavassa pyrimme avaamaan mitä push-viestit ovat sekä niiden hyötyjä ja mahdollisuuksia. Tulemme julkaisemaan myöhemmin myös push-viestintäteknologiaa ja -arkkitehtuuria tarkemmin käsittelevän artikkelin.

Push-viestejä voitaisiin kuvata esimerkiksi seuraavalla virkkeellä:

Push-viestit ovat viestejä, jotka laitteeseen saapuessaan lähes poikkeuksetta herättävät käyttäjän huomion. Käyttäjä voi viestin otsikosta riippuen joko unohtaa sen tai perehtyä siihen ja saada mahdollisesti lisää tietoa asiasta.

Kuvauksen mukaisesti push-viestejä voitaisiin verrata tekstiviesteihin, sillä lisäyksellä, että ne aina liittyvät puhelimelle ladattuun sovellukseen.

Mainonnan ja markkinoinnin näkökulmasta push-viestit eroavat merkittävästi tekstiviesteistä tai muista digitaalisista viestintämuodoista. Digitaalinen mainonta tekstiviestien avulla saattaa monien mielestä olla tunkeilevaa ja sähköpostin välityksellä tapahtuvana persoonatonta sekä harvoin tunteita herättävää. Push-viestit sen sijaan saapuvat aina käyttäjän sallimana, näkyvät reaaliaikaisesti laitteessa ja voivat sisältää monipuolista interaktiivista sisältöä.

Henkilökohtainen merkityksellisyys

Yksilön profilointi on perinteisessä mainonnassa usein pulmallista ja sen toteutus sekä järkevästi että käytännöllisesti on hankalaa, sillä käyttäjien taustat pitää kartoittaa ja sen perusteella jakaa käyttäjät lokeroihin. Käyttäjien profiilitietojen muuttuminen puolestaan ei välttämättä koskaan saavuta palveluiden tarjoaa. Tällainen tilanne saattaa olla kaikille vahingollista. Mainostaja tekee turhaa työtä ja tuhlaa resurssejaan. Viestin saajan osalta roskaviestinnäksi muuttuneen yhteydenottojen käsittely voidaan kokea jopa ärsyttäväksi, mikä saattaa herättää negatiivisia tunteita palvelun tarjoajaa kohtaan.

Push-viestien vastaanottamisen sallimisen mahdollisuus mobiilisovelluksessa estää turhien viestien saapumisen loppukäyttäjille. Viestien lähettämisen salliminen myös ennakoi vastaanottavampaa suhtautumista asiasisältöön. Mobiilisovellus, joka on yhteydessä push-palveluun, voi kerätä ja pitää ajantasalla käyttäjien palvelutietoja, listaa kiinnostuksen kohteista ja sekä esimerkiksi paikkatietoja – käyttäjän sen salliessa. Erityisesti paikkatietojen hyväksikäyttö on vielä vähän käytetty “se suuri” mahdollisuus; paikkatietoisuus yhdistettynä käyttäjien kiinnostuksien kohteisiin tuo merkityksellisyyttä ja korostaa palvelun tarjoajan kunnioitusta yksilöä kohtaan.

Push-viestit saavuttavat kohdeyleisön välittömästi

Ihmisillä on nykyään hyvin usein monia sähköpostitilejä: todennäköisesti tietty tili varattuna henkilökohtaiseen viestintään ja jokin toinen tai toiset tilit yleisempään viestintään, kuten rekisteröitymisiin ja muihin harvemmin käytettyihin palveluihin. Viestin ja loppukäyttäjän kohtaaminen saattaa tapahtua vasta päivien tai viikkojen kuluttua itse lähetystapahtumasta, koska käyttäjät eivät välttämättä seuraa aktiivisesti kaikkia tilejään.

Push-viestit saavuttavat vastaanottajan viimeistään muutaman minuutin sisällä lähetyksestä. Käyttäjien voidaan olettaa saaneen ja huomioineen viestin lähes välittömästi, sillä puhelin värisee, soittaa mahdollisesti merkkiäänen ja ruudulla tapahtuu visuaalinen päivitys.

Pushin avulla sisältöä mobiiliviestintään

Niin massaviestit kuin käyttäjien profiloinnin kautta yksilöidyt viestit kannattaa muotoilla huolellisesti ja sisällyttää niihin jotain, joka oikeuttaa välittömän yhteydenoton käyttäjiin.

Pelkkä tekstitieto,esimerkiksi vaikkapa “Liikkeemme on auki tänäänkin klo 9-18” voi olla paikallaan sillöin tällöin muistuttamassa palvelun olemassaolosta. Suositeltavampaa olisi kuitenkin antaa käyttäjille arvokkaampaa informaatiota, jota epush-viestipalvelua käyttämättömät eivät tietäisi. Esimerkiksi “Muotisuunnittelijamme on tänään liikkeessämme klo 18 asti antamassa henkilökohtaisia vinkkejä. Skumppaa 50 ensimmäiselle!” -tyyppinen viesti tuo lisäarvoa viestiin ja on myös käyttäjille syy olla liittyneenä viestipalveluun. Varsinkin lyhytaikaiset kaikille tarjottavat tiedotteet ja edut houkuttelevat palvelusta kiinnostuneet liittymään palveluun saadakseen tiedon tapahtumista ensimmäisten joukossa.

Push-viestivaihtoehdot

Push-viestit ovat perusidealtaan samankaltaisia kaikissa laitteissa: viesti saapuu, se mahdollisesti avataan ja siihen sidottu mobiilisovellus käynnistyy ja näyttää lisätietoa asiasta.

Applen iOS-laitteilla push-viestit tulevat laitteen ruudulle ja käyttäjä voi reagoida siihen välittömästi tai palata asiaan myöhemmin uudelleen viestikeskuksen kautta. Näkyvän viestisisällön lisäksi viestiin liitetään usein myös käyttäjälle näkymätöntä tietoa, kuten mikä merkkiääni soitetaan viestin saapuessa tai mitä dataa palveluun halutaan välittää sisällönpäivittämisen mahdollistamiseksi.

Windows Phone -laitteille on tarjolla puolestaan usean tyyppisiä push-viestejä, joista perusvaihtoehto (“toast”) on samankaltaisin muiden alustojen push-viesteihin verrattuna. Windows Phone -laitteelle on mahdollisuus lähettää myös raw- ja tile-viestejä. Raw-viestit tarjoavat mahdollisuuden palvelulle syöttää tietoa (esimerkiksi kuponki) mobiilisovellukseen taustalla, ilman että sovelluksen pitää hakea tietoa erikseen.

Tile-viesteissä puolestaan palvelu voi syöttää sovellustiileen (vastaa muilla alustoilla suurin piirtein ikonia työpöydällä) taustakuvia, tekstejä ja numerotietoa. Tämän avulla voidaan toteuttaa esimerkiksi käyttäjän työpöydän uutissovelluksen ikonin sisällönmuutos tai vastaava tilapäivitys.

Android-laitteilla mahdollisuuksia on useita. Käytännössä kehittäjä voi päättää mitä tehdään push-viestin tullessa. Yleisin käytäntö kuitenkin on, että käyttäjälle näytetään notifikaatti, jonka hän voi avata silloin kun on siihen sopiva hetki. Voidaan kuitenkin tehdä monimutkaisempiakin toteutuksia — esimerkiksi käsketään laite lataamaan jokin iso tiedosto ja vasta sen jälkeen ilmoitetaan käyttäjälle notifikaatilla. Käyttäjälle ei ole pakko ilmoittaa push-viestistä mitään, vaan se voi pelkästään olla taustalla tapahtuvaa logiikkaa.

Toimintaympäristö

Push-viestipalvelun käynnistämisen jälkeineen tapahtuva viestien lähetys voi parhaimmillaan olla varsin yksinkertaista ja suoraviivaista. Palvelun tarjoaja luo uuden push-viestin syöttämällä lähettävässä palvelussa lomakkeeseen viestin tiedot. Lähetysnapin painamisen jälkeen tiedot siirtyvät push-viestipalvelimelle, joka lähettää viestin edelleen Applen, Googlen ja Microsoftin kautta mobiililaitteille. Edellä kuvattu toimintatapa vaatii kuitenkin sen, että palvelun kehittäjä on joko itse kehittänyt push-palvelimen viestien välittämiseen tai käyttää tarjolla olevia kaupallisia vaihtoehtoja.

Qvik on toteuttanut push-palvelimen ja tarjoaa sitä mielellään kaikkien asiakkaiden käyttöön. Jatkamme Push-teknologiasta ja viestintäarkkitehtuurista tarkemmin seuraavassa artikkelissa.

 

Kuva 1. Push -viestipalvelin lähettää viestit mobiilikäyttäjille

 

iPad Mini -arvostelu

Ensimmäinen iPad julkaistiin tammikuussa 2010. Aikaa on siis kulunut vasta vähän vajaat kolme vuotta, mutta julkaisusta alkaneen tablettivallankumouksen takia aika tuntuu huomattavasti pidemmältä ja paljon on vettä virrannut julkaisun jälkeen. Suurin muutos tablettimarkkinoilla iPadin julkaisun jälkeen ovat olleet halvat minitabletit Amazon Kindle Fire ja Google Nexus 7. Nyt Apple lähtee mukaan minitablettitaistoon ei-niin-halvalla iPad  Minillä.

Otin viikonlopun ajaksi testiin iPad Minin 16-gigaisen WiFi-version ja testasin, onko uutukaisesta mihin ja mihinkään.

Onko minun käteni todella iso, vai onko iPad Mini?

Ensi tuntuma

Ensi tuntuma iPad Miniin on melkein maaginen: se on kevyt, erittäin ohut ja tuntuma on “arvokas”. Samanlaista tunnetta ei välitä iPad Minin Android vastine Google Nexus 7, joka on pienestä koostaan huolimatta varsin pulska ja painava. Myös sen ulkokuori natisee kun laitetta käsittelee. Tuo arvokkuuden tuntuma on todennäköisesti syy, miksi Apple on hinnoitellut iPad Minin melkein 100 euroa kalliimmaksi kuin mitä Google on laittanut hintalapuksi Google Nexus 7:lle.

Kannettavuus on iPad Minissä huippuluokkaa: se sujahtaa talvitakin povitaskuun näppärästi, eikä sitä edes huomaa kantavansa mukanaan.

Näyttö

Retina iPadiin tottuneelle iPad Minin pienempi näytöntarkkuus on heti huomattavissa, ja teksti näyttää hiukan sumuiselta. Latasin iBooksista testin vuoksi pari kirjaa ja yhden sarjakuvan. Kirjoissa näyttö ei varsinaisesti haitannut, mutta sarjakuvissa tekstien näkemin vaati huomattavaa keskittymistä. Zoomailuun ei kuitenkaan tarvinnut turvautua. Verkkosivuilla huomasin zoomailevani huomattavasti enemmän kuin Retina iPadilla.

Käyttö

Kolmannen sukupolven Retina iPad (eli se ensimmäinen Retina), kärsii hiukan nopeusongelmista, johtuen näytön suuresta koosta. Nuo nopeusongelmat ovat poissa iPad Minissä: kuten myös samalla suunnilleen samalla raudalla toimivassa iPad 2:ssa, applikaatiot avautuvat sukkelaan ja isotkin pdf-tiedostot ovat selattavissa nopeasti iBooksissa.

Peleistä testasin Suomalaisen Remedyn Death Rally -peliä, jota olen aiemmin testannut iPhone 4:lla ja kaikilla aiemmilla iPadeilla. iPad Minillä pelattuna pelikokemus oli tähän astisista laitteita paras. Näyttö oli tarpeeksi iso, mutta laite on niin kevyt, että sitä jaksaa kannatella pidemmänkin aikaan. Voidaan siis sanoa, että homma pelittää.

Pähkinänkuoressa

Olin ennen testirupeaa hiukan skeptinen minitablettien tarpeellisuudesta, testi ei aivan kokonaan saannut mieltäni muutettua, mutta ei minitabletit aivan turhia ole. iPad Mini on rahansa arvoinen laite ja varsinkin sisäisellä verkkoyhteydellä varustettuna laite olisi varsin kätevä paljon tienpäällä oleville.

Retina iPadin omistajille iPad Mini ei tuota mainittavaa lisäarvoa, mutta esimerkiksi iPod Touch tai iPad 1 olisi jo varsin perusteltua korvata iPad Minillä. Muihin minitabletteihin verrattuna iPad Minin iso näyttö, mutta pieni koko antaa mielesätni sellaisia etuja, joista kannattaa maksaa muutama euro enemmän.

 

Ehta modaalidialogi iOS:lla


Muista kehitysympäristöistä iOS-maailmaan tulevasta voi tuntua oudolta Cocoa Touchin modaalidialogien puute. Vaikkakin on mahdollista rakentaa kaiken muun päälle piirtyvä dialogi joka oletukselta (ja tämä on ohitettavissa) saa kaiken syötteen, dialogin näyttävä kutsu ei blokkaa vaan palaa välittömästi ja dialogin sulkeutuminen on otettava kiinni asynkronisella callbackilla (‘delegate’). Edes perustyökalu UIAlertView ei käyttäydy kuin muiden alustojen – vaikkapa MFC, Javascript, Symbian Series60/Series80, Java AWT/Swing, Qt, Android, GTK, jne.. – stock komponentit MessageBoxit ja InputDialogit.

Tämänkaltaista UI paradigmaa voi kuitenkin paitsi kaivata, myös tarvita; oletetaan etta ollaan rakentamassa iOS -toteutusta cross-platform sovellukseen jossa täytyy toteuttaa metodi vaikkapa seuraavasti:

std::string AskUserInput()
{
  // Semantiikka: kysy käyttäjältä syöte popup dialogilla 
  //   ja palauta annettu teksti
}

Miten tämä toteutetaan iOS:lla? Nostetaan kädet pystyyn, otetaan projektin arkkitehti Skypen päähän ja anotaan API:in muutosta joka mahdollistaisi syötteen kyselyn asynkronisen käsittelyn? Miksei, mutta jos yo. mekanismi on jo toteutettu viidelle muulle alustalle, on edessa iso muutostyö ja paljon hampaita kiristeleviä ihmisiä. Turha asynkronia näin yksinkertaisessa asiassa hankaloittaa sekä pirstaloittaa arkkitehtuuria.

Ongelman ratkaisu lähtee UI frameworkin toiminnan ymmärtämisestä. Käytännössä kaikissa moderneissa käyttöliittymäkirjastoissa on 1-N UI säiettä jotka palvelevat käyttöliittymää ns. event loopin [tapahtumakieriö?! -toim.] kautta. Event loop ottaa inputteja komponenteilta, timereilta tai vaikkapa muilta säikeiltä alustakohtaisten viestinvälitysmekanismien välityksellä ja tarjoilee niitä kiinnostuneille kuuntelijoille. Käytännössä jokaisella säikeellä on tasan yksi (tai nolla, jos kyseessä ei ole UI:ta palveleva säie) event loop, ja yleisesti mobiilikäyttöjärjestelmissä UI:ta palvelee vain yksi säie kerrallaan. Käyttöliittymä kumminkin toimii jouhevasti niin kauan kuin tätä säiettä ei varata kerralla liian pitkäksi aikaa; pitkäkestoinen laskenta täytyy joka jakaa pieniin osiin tai tehdä muissa säikeissä.

Event loopiin perustuvat järjestelmät sallivat yleensä event looppien sisäkkäisen ajamisen (nesting); tällöin vanha event loop aktivaatio pysähtyy eikä jatka ennenkuin sisempi loop aktivaatio loppuu. Tätä voidaan hyväksikäyttää asynkronian poistamiseksi; API-metodin sisällä ajetaan uusi aktivaatio, kuunnellaan asynkronista vastausta ja lopetataan aktivaatio kun vastaus on saatu. Tällöin APIa kutsuva metodi blokkaa koko suorituksen ajan. On kuitenkin erotettava tämä semaforin odottamisesta; säiehän ei ole idlena vaan suorittaa sisempää event loop aktivaatiota.

Rakennetaan esimerkkitoteutus iOS:lle. Halutaan tehda seuraavanlainen kutsu:

-(void) requestAndLogInput {
  InputDialog* dlg = [InputDialog dialog];
  [dlg showModal];
  NSLog(@"Dialog input was = %@", [dlg getInput]);
}

Aloitetaan dialogin luomisesta. Tähän käytetään normaalia Cocoa Touch boilerplatea:

+(InputDialog*) dialog {
    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"InputDialog"
                                                      owner:nil
                                                    options:nil];
    InputDialog* dlg = [[[nibViews objectAtIndex:0] retain] autorelease];

    return dlg;
}

Määritellään metodi jolla dialogi näytetään modaalisti – se asettuu päällimmäiseksi ja blokkaa kunnes dialog on suljettu. iOS:lla event loop tunnetaan nimellä Run loop, ja sitä vastaavat APIt ovat NSRunLoop sekä Core Foundationin CFRunLoop*:

-(void) showModal {
    // Lisätään dialogimme keywindow'n ylimmän lapsen ylimmäksi lapseksi,
    // jolloin dialogi päätyy päällimmäiseksi view stackiin
    UIWindow* keyWindow = [[UIApplication sharedApplication] keyWindow];
    UIView* topmostView = [keyWindow.subviews objectAtIndex:0];
    [topmostView addSubview:self];

    // Sijoitetaan dialogi suunnilleen keskelle ruutua    
    CGSize s = topmostView.bounds.size;
    self.center = CGPointMake(s.width / 2, (s.height / 2) - 70);

    // Ajetaan nested run loop aktivaatio jolloin showModal kutsu blokkaa
    CFRunLoopRun();
}

Lopuksi määritellään dialogin sulkeva IBAction:

-(IBAction) okPressed {
    // Poistetaan dialogi näkyvistä
    [self removeFromSuperview];

    // Pysäytetään sisempi run loop aktivaatio; kontrolli palaa 
    // ulommalle ja showModal -kutsu palaa
    CFRunLoopStop(CFRunLoopGetCurrent());
}

Valmista! Ja koska kyseessä  ovat erilliset run loop aktivaatiot eivätkä erilliset run loopit, esimerkiksi timerit jatkavat eloaan ja autorelease pool on jaettu aktivaatioiden välillä.

 Esimerkkiprojekti Xcode 4.5 -projektina. 40kB zip

 

Android-tablettien osuus hurjassa kasvussa.

Pew Reseach Center’s Project for Excellence in Journalism on julkaissut tutkimustilastoja Yhdysvaltojen aikuisväestön tablet-tietokoneiden käytöstä ja laitteiden osuuksista.

Hulppea 25% amerikkalaisista aikuisista omistaa tablet-tietokoneen. Luku on valtava ottaen huomioon, että ensimmäinen nykyaikainen tablet iPad ilmestyi vuonna 2010.

Applen hallitsemat tablet-markkinat ovat myös mullistuneet vuoden aikana. Siinä missä vielä vuosi sitten Applen iPadit olivat 81% koko markkinasta, on tämän vuoden luku 52% ja Androideilla 48%. Androidin markkinaosuudesta 21% menee Kindle Firellä, joka käyttää Amazonin omaa versiota Androidista. Kehittäjille tämä on sinäänsä huono uutinen, koska tämä lisää entisestään fragmentaatiota Android-alustalla (nyt kauppoja onkin 2).

Markkinaosuuksien kasvusta huolimatta iPad on edelleen tablettien webbiselaustilastoissa ykkösenä hurjalla 85% osuudella.

Yllä olevat luvut kerättiin ennen Kindle Fire HD:n ja Google Nexus 7:n kauppoihin tuloa, joten Android-laitteet ovat jo todennäköisesti ohittaneet iPadit Yhdysvalloissa. Laitteista jälkimmäinen on vihdoin myös rantautumassa Suomeenkin ja hinnaksi näyttää asettuvan 269 euroa.

Applen vastaiskua tablettien parissa odotellessa.

Tarkemmat kuvat Retina-näytöille

Artikkelissa Mobiiliselaimen tunnistaminen CSS:llä esittelin css-ominaisuutta min-device-pixel-ratio näin:

min-device-pixel-ratio tarkoittaa näytön “pikselitiheyttä”. Tyypillisen tietokoneen näytön ja peruspuhelimen arvo on 1, mutta esimerkiksi iPhone 4:llä ja iPhone 4S:llä tämä arvo on 2. Selitys tälle on ns. retina-näytössä: näytön koko on iPhone 3G:ssä ja iPhone 4:ssä on sama, mutta näytön leveys ja korkeus on kaksinkertainen. Tällöin sivustolla olevat kuvat näkyvät iPhone 4:n tarkemmalla näytöllä pikselöityneenä ja epätarkkoina, sillä käytetyn grafiikan resoluutio on liian pieni. Tämän säännön avulla voidaankin saada käyttöön tarkemmat grafiikat siihen pystyville näytöille.”

Tässä artikkelissä käsittellään pikselitiheyttä ja sitä, mitä se tarkoittaa web- kehittäjän kannalta.

Pikselitiheys ja retina-näytöt

Pikselitiheyden (engl. ppi, pixel per inch) käsitettä käytetään silloin, kun halutaan kertoa näytön pikseleiden määrä suhteutettuna näytön kokoon. Pikselitiheyden käsite voidaan liittää siis monenlaisiin laitteisiin: televisioihin, puhelimiin, digitaalisiin kameroihin jne. Esimerkiksi Suomen kodeissa tyypillisen, 42″ Full HD -television pikselitiheys on 52ppi ja tietokoneen 23″ Full HD -monitorin pikselitiheys on 98ppi. Matkapuhelinten pikselitiheys on tyypillisesti huomattavasti korkeampi kuin tietokoneen näytöillä ja televisioissa – esimerkiksi iPhone 4 ja iPhone 4S -laitteiden pikselitiheys on 326ppi ja Sony Xperia S:llä peräti 342ppi.

Retina on Applen iPhone 4:n myötä käyttöön ottama käyttämä markkinointinimi erityisen tarkoille näytöille. iPhone 4:n laitteen pikselimäärä on kaksinkertainen edeltäjäänsä nähden: iPhone 3GS:llä ja sen edeltäjillä näytön koko oli 3.5″ ja resoluutio 320x480px (163ppi). iPhone 4 piti näytön koon ennallaan, mutta resoluutio kaksinkertaistui 640x960px (326ppi) kokoiseksi.

Koska näytön fyysinen koko on sama, kehittäjät saivat sovelluksensa muutettua tarkemmaksi tuplaamalla sovelluksen kuvien resoluution ja laittamalla tiedoston nimen loppuun tekstin @2x. Tästä syntyi pikselitiheyden käsite: normaalinäytön pikselitiheys on 1, kun taas retina-näytön pikselitiheydeksi tulee 2.

Myös muut valmistajat ovat tuoneet korkeamman pikselitiheyden laitteita: Esimerkiksi Samsung Galaxy S II -laitteessa pikselitiheys on 1.5, ja Samsung Galaxy S III:ssa pikselitiheys on 2. Apple toi myös Retina-näytöllisen MacBook Pro -kannettavan tietokoneen, jonka pikselitiheys on 2 ja resoluutio peräti 2880×1800 (15″, 220ppi). Myös vuoden 2012 iPad-tablettitietokone tuo mukanaan retina-näytön 2048×1536 -resoluutiolla (9.7″, 264ppi).

CSS ja korkeamman pikselitiheyden näytöt

Selaimen ja verkkosivujen kannalta retina-näyttö asettaa tarpeen tuoda korkearesoluutioisia kuvia ja grafiikoita näille laitteille, sillä verkkosivujen kuvat on suunniteltu pääasiassa vanhemmille, huomattavasti epätarkemmille näytöille. CSS3-standardiin tuli onneksi nopeasti mukaan mahdollisuus määrittää CSS-säännöt pikselitiheyden perusteella.

Tässä esimerkki css-tiedoston valinnasta pixel densityn perusteella:

 <link rel="stylesheet" href="retina.css" media="only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2)" /> 

Huomaa että sääntö määritellään tässä yhteydessä neljään kertaan, sillä kaikki selaimet eivät vielä osaa tulkita min-device-pixel-ratio-määritettä sellaisenaan. Yllä oleva koodinpätkä ottaa sivulla käyttöön retina.css-tiedoston laitteilla, joiden pikselitiheys on 2 tai enemmän. Alla esimerkki taustakuvan asettamisesta niin, että se toimii oikein pikselitiheyden arvoilla 1, 1.5 ja 2. Huomaa css-sääntöjen järjestys: Alempana oleva sääntö on pätevämpi, jolloin järjestyksen täytyy olla pienemmästä pikselitiheydestä suurempaan.

 .logo { background: url('logo.png'); } @media only screen and (min-device-pixel-ratio: 1.5) { .logo { background: url('logo@1.5x.png'); } } @media only screen and (min-device-pixel-ratio: 2) { .logo { background: url('logo@2x.png'); } } 

Selkeyden vuoksi esimerkissä on jätetty pois webkit-, mozilla- ja opera -versiot.

HTML ja korkeamman pikselitiheyden näytöt

HTML-dokumentin sisällä käytetään usein kuvia <img>-tägien sisällä. Kuvien määrittäminen pikselitiheyden perusteella ei kuitenkaan onnistu suoraan tägin sisällä, sillä HTML-standardit eivät vielä sisällä siihen keinoja.

Apuun tulee tässä vaiheessa JavaScript, jolle on valmiiksi tehty kirjastoja retina-kuvien käsittelyyn. Retina.js-kirjasto toimii niin, että se etsii @2x-loppuvat tiedostot ja näyttää ne korkearesoluutioisella näytöllä. jQuery Retina Display Plugin on jQuery-plugin, joka toimii vastaavalla tavalla – kuvien nimikäytäntö on tosin hieman erilainen, sillä retina-kuvan nimen perässä täytyy lukea -2x.

Jos JavaScriptissä halutaan suorittaa erilaista logiikkaa eri pikselitiheyden näytöille, näytön pikselitiheyden saa selville julkisesta muuttujasta window.devicePixelRatio.

Jatkoluettavaa

Optimising for High Pixel Density Displays, Menacing Cloudin artikkeli aiheesta vähän eri näkökulmasta (englanniksi)
Resoluutio (Kuvatekniikka) – Wikipedian lyhyehkö artikkeli resoluutiosta, joka sisältää myös vähän kuvausta pikselitiheydestä.
List of displays by pixel density – Wikipedian kattava lista tämän hetken näytöistä ja niiden pikselitiheyksistä (englanniksi)

Windows Phone – Listbox animaatiot

Taustaa

Animaatiot ja siirtymät (transitiot) ovat olennainen osa Windows Phone sovellusten maailmaa. Liikkeen ja vasteen lisääminen näytössä oleviin kohteisiin on sinänsä yksinkertaista, kunhan vain hallitsee työkalut ja menetelmät. Listauksiin (Listbox) kajoaminen on kuitenkin hiukan monimutkaisempaa kuin yksittäisten kohteiden, kuten esimerkiksi kuvan liikuttelu. Alla perehdytään Listbox-listausvalikon elävyttämiseen.

Rakennetaan ohjelman runko

Tarvitsemme Listbox-esimerkin, jota voimme animoida. Kohteena voi käyttää omaa Listbox:ia tai alla olevaa esimerkkiä. MVVM:ää ei ole käytetty esimerkissä yksinkertaisuuden vuoksi.

  • Luo uusi projekti (Silverlight for Windows Phone)
  • Kopioi alta koodikatkelmat uuden projektin MainPage.xaml- ja MainPage.xaml.cs-tiedostoihin.

MainPage.xaml:

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="Listaus" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="Kaupunkeja" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <ListBox Margin="45,151,33,43" x:Name="lista">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <StackPanel Margin="8,8,1,0">
                                <StackPanel Background="White" Width="380">
                                    <TextBlock Margin="8,8,1,0" Foreground="Black"  Text="{Binding}"/>
                                </StackPanel>
                            </StackPanel>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>

MainPage.xaml.cs:

List<string> nameList;

// Constructor
public MainPage()
{
InitializeComponent();
nameList = new List<string>();
nameList.Add("Helsinki");
nameList.Add("Oulu");
nameList.Add("Turku");
nameList.Add("Joensuu");
nameList.Add("Lahti");
nameList.Add("Tampere");
lista.ItemsSource = nameList;
}

Suorita ohjelma. Näkymän pitäisi olla alla olevan kuvan mukainen:

Kuva 1. Alkutilanne.

Lisätään vastetta listauksen kohteisiin – osa 1

Listauksen kohteiden koskettaminen ei anna erikoisempaa visuaalista vastetta kuvan 1 tilanteessa. Lisätään “tilt”-efekti erittäin vähällä vaivalla.

  • Lataa ja asenna Silverlight toolkit
  • Lisää projektiin viittaus (reference) Visual studiossa “Microsoft.Phone.Control.Toolkit.dll”-tiedostoon, joka ladattiin edellisessä kohdassa.
  • Lisää koodit:

MainPage.xaml-sivun alkuun:

xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"

sekä toolkit:TiltEffect.IsTiltEnabled=”True”- lisäys ListBox-määrittelyyn:

<Grid>
<StackPanel Margin="8,8,1,0" toolkit:TiltEffect.IsTiltEnabled="True">
<StackPanel Background="White" Width="380">
<TextBlock Margin="8,8,1,0" Foreground="Black"  Text="{Binding}"/>
</StackPanel>
</StackPanel>
</Grid>

Suorita ohjelma. Kosketa ListBoxin kohteita. Voidaan huomata, että listaus on muuttunut paljon elävämmäksi, koska listauskohta “antaa myöten” kosketukselle.

Lisätään vastetta listauksen kohteisiin – osa 2

Osa 1:ssä luotiin pieni kosketusvaste listauskohteisiin olemassa olevan työkalusetin (toolkit) avulla, mutta entäpä oman animaation lisääminen tai määrittely? Se onnistuu seuraavasti:

  • Avaa Expression Blend 4 (klikkaa hiiren oikeaa nappia MainPage.xaml-tiedoston kohdalta Visual Studiossa: “Open in Expression Blend”).
  • Valikosta (kuva 2) “Objects and Timeline” valitse “lista”, eli määritetty ListBox

Kuva 2. ListBox Blendissä.

  • Klikkaa oikeaa hiiren nappia lista:n kohdalla ja valitse “Edit Additional Templates” ja “Edit Generated Item Container (ItemContainerStyle)”, sekä viimeiseksi “Edit a Copy”.
  • “Create Style Resource”-ikkuna avautuu. Voit nimetä tyylin haluamaksesi. Muuta “Define in -> This document”-kohta “ListBox: lista”-vaihtoedoksi ja paina OK.
  • Valitse “ContentContainer” “Objects and Timeline”-lehdykästä.
  • Properties valikko ilmaantuu näkyviin (alavalikkoja: Brushes, Appearance, Layout, Common properties, Transform etc).
  • Viereisestä lehdykästä “States” valitse “Selection States”-kohdasta “Selected”.
  • Valitse jälleen “Objects and Timeline”-lehdykkä.
  • Paina “Show Timeline”-nappia ja aikanäkymä aukeaa. Siirrä keltaista pystyviivaa vetämällä sitä oikealla haluamasi määrä. Valitaan tällä kertaa 0:00,500.

  • Aukaise Transform-valikko Properties-ikkunasta painamalla kolmiota otsikon vieressä. Aseta Projection kohdasta Y:n arvoksi 360.

  • “Object and Timeline”-lehdykässä voit kokeilla saatua efektiä painamalla “Play”-kolmiota aikajanan yläkulmassa.
  • Tallenna työ: File/Save all.
  • Avaa Visual Studio. Työkalu huomauttaa, että tiedostoa on muutettu. Hyväksy muutokset (Yes to all).

Vilkaisemalla MainPage.xaml-tiedostoa voidaan huomata, että koodin määrä on lisääntynyt huomattavasti. Blend-työkalu on generoinut animaatiotoiminnallisuuden automaattisesti.

Suorita ohjelma. Klikkaamalla listauksen yhtä kohdetta voidaan huomata, että valittu kohde pyörähtää akselinsa ympäri ja palautuu alkupaikkaansa. Uudelleen samaa kohdetta painettaessa ei tapahdu mitään, koska valitsemisaktivointi on Selection-tilatyyppiä. Erilaisia aktivointi-triggereitä ja animointisekvenssejä voidaan määritellä helposti soveltamalla yllä annettua ohjetta.