Tag Archives: iPhone

Sijaintipalvelut iOS-applikaatioissa

Nykyaikaisista älypuhelimista ja tableteista löytyy välineet käyttäjän sijainnin selvittämiseen. Sijaintia voi käyttää mobiiliapplikaatioissa monella tavalla: käyttäjälle voidaan esimerkiksi tarjota lähiympäristöön liittyvää tietoa – esimerkiksi paikallisuutisia, tai käyttäjää voi muistuttaa hänen saapuessaan tärkeään paikkaan. Liikuntasuorituksia kirjaava applikaatio voi muodostaa juoksureitistä kartan tallennettujen GPS-päivitysten perusteella.
iOS SDK tarjoaa monipuolisen paikannuskirjaston nimeltään CoreLocation. Kirjasto toimii rajapintana koodin ja mobiililaitteen fyysisten mittalaitteiden välillä mahdollistaen mm. seuraavat asiat:
  • laitteen sijainnin paikantaminen, parhaimmillaan alle kymmenen metrin tarkkuudella
  • viestittäminen laitteen sijainnin muuttumisesta
  • määritellyille alueille liikkumisen tarkkailu
  • sijainnin tarkkailu ja siitä tiedottaminen applikaation ollessa taustalla
  • applikaation käynnistäminen sijainnin muuttuessa merkittävästi
Paikannuksen musta laatikko
Mobiililaitteet hoitavat laitteen paikannuksen monella tavalla: GPS-paikantimella, Wifi-tukiasemien avulla ja kolmiomittaamalla puhelinmastoja. CoreLocation valitsee automaattisesti sopivimman paikannusmenetelmän, ja tarvittaessa yhdistelmää useasta menetelmästä. Syy automaattiseen paikannusmenetelmän valintaan on laitteen virrankulutuksen minimointi ja paikannusnopeuden maksimointi.
Vaikka paikannusmenetelmän valinnan läpinäkymättömyys kuulostaa kehittämistä rajoittavalta, helpottaa se kehittäjän työtä. Käytännössä kehittäjän tarvitsee valita haluttu sijainnin tarkkuus ja kirjasto hoitaa kaiken muun.
Paikannusprosessi tapahtuu kahden luokan avulla jotka ovat CLLocationManager ja CLLocation.
  • CLLocationManager paikantaa sijiannin ja lähettää tietoa sijainnin muutoksista CLLocationManagerDelegate-protokollan kautta päivityksiä kuuntelevalle delegaatille. Kaikki paikannukseen liittyvät muutettavat asetukset löytyy tästä luokasta
  • CLLocation kuvaa yhtä paikkatietopäivitystä. Olio sisältää aikaleiman, koordinaatit ja päivityksen tarkkuuden metreinä. Mukana on myös tieto liikkeen suunnasta ja nopeudesta.
Kertakäyttöinen vai jatkuva paikannus?
Moni applikaatio tarvitsee sijaintitietoa vain kerran, esimerkiksi käynnistyessään. Yleensä kerran haettu paikkatieto riittää useimpiin tarpeisin. Silloin on järkevää että CLLocationManager hakee käyttäjän sijaintia vain kun paikkatietoa ei ole haettu, tai viimeisin paikkatietopäivitys on liian vanha tai epätarkka. Yleensä applikaatioihin riittää yksi jaettu CLLocationManager-olio jonka location-propertyä muu applikaatio käyttää. Jaettu CLLocationManager laitetaan silloin hakemaan käyttäjän sijaintia, ja haku päätetään kun tarpeeksi tarkka sijainti on löydetty. Tämän jälkeen jaetussa CLLocationManager-oliossa on tallessa viimeisin haettu sijainti tallennettuna location-propertyyn josta sijaintia tarvitsevat applikaation osat sitä voivat kysyä. On syytä tarkastaa location-propertyn aikaleima, sillä applikaation käynnistyessä tai palatessa taustalta viimeisin sijaintipäivitys voi olla liian vanha kertomaan käyttäjän nykyisen sijainnin.

Paikannus applikaation ollessa taustalla
Sijainnin paikantamista voi jättää päälle kun applikaatio siirtyy taustalle ajoon. Paikannusmenetelmiä on kolme erilaista:
  1. startUpdatingLocation – jatkuva ja tarkka sijainnin päivitys
  2. startMonitoringSignificantLocationChanges – epätarkka ja harvoin päivittyvä sijainnin seuraaminen
  3. startMonitoringForRegion: – tarkkailee liikkuuko laite määritellyn alueen sisälle tai pois alueelta. Alueita voi olla seurannassa yhtäaikaisesti 20 kappaletta.

Kolmesta edellämainitusta menetelmästä ensimmäinen on tarkin, ja sen ollessa käynnissä applikaatio pysyy koko ajan taustalla ajossa. Tämä on paras vaihtoehto jos paikannusta käyttää esimerkiksi liikuntasovellukseen tai reittioppaaseen. Haittapuolena on jatkuva akun kuluminen.

Kaksi jälkimmäistä seurantatapaa kuluttaa vähemmän akkua kuin ensimmäinen, eikä applikaatio jää päälle taustalle. Nämä menetelmät kuitenkin käynnistävät applikaation, ja sijaintipaikannus pysyy päällä vaikka laitteen sammuttaisi ja käynnistäisi uudestaan.

Sijaintipalveluiden käyttö taustalla XCode-projektiss
a
  1. lisää CoreLocation.framework projektiin.
  2. Ota käyttöön Background modes Capabilities-näkymästä. Se löytyy projektin targetista.
  3. Valitse Location updates. Applikaatio saa nyt päivityksiä sijainnista ollessaan taustalla.
CoreLocationin haasteet ja ongelmat
Laitteen sijainnin hakeminen yksittäistä käyttökertaa varten ei ole niin suoraviivaista kuin se voisi olla. Sijainti toimitetaan asynkronisesti CLLocationManagerDelegate-protokollan kautta asynkronisesti. Ei siis ole olemassa suoraa myLocation = getLocation() -tyylistä tapaa hakea sijaintia. Helpoin tapa hakea laitteen sijainti kerra on kutsua CLLocationManager-luokan startUpdatingLocation-metodia. Kutsu käynnistää paikannuksen, ja lähettää normaalisti 3-4 locationManager:didUpdateLocations: -kutsua delegaatille sijainnin päivittymisestä. Paikannus kannattaa lopettaa silloin kun kutsujen mukana tulevien CLLocation-olioiden horizontalAccuracy-attribuutti on tarpeeksi pieni, eli paikannettu sijainti on riittävän tarkka applikaation tarpeisiin.
Haettavan sijainnin halutun tarkkuuden voi määritellä CLLocationManager-olioon, desiredAccuracy-propertyyn. Halutulla sijainnin tarkkuudella on suuri merkitys paikannuksen nopeuteen ja laitteen virran kulutukseen, joten on suositeltavaa että haettavan sijainnin tarkkuus on niin pieni kuin se on välttämätöntä applikaatiolle. Esimerkiksi paikallissäätä tai paikallisuutisia näyttävä applikaatio voi hyvin käyttää kCLLocationAccuracyThreeKilometers-tarkkuutta, kun taas juoksureittiä tallentava applikaatio vaatii tarkimman mahdollisen tarkkuuden, eli kCLLocationAccuracyBest-tarkkuuden.

Lopuksi
Tämä kirjoitus esitteli pintapuolisesti CoreLocation-kirjaston mahdollisuuksia. Moni mielenkiintoinen ja hyödyllinen kirjaston osa, kuten iBeacon-majakat ja CLGeocoder-geokoodauspalvelu, jäivät tässä kirjoituksessa esittelemättä.

Sijaintipalveluiden hyödyntäminen tekee applikaatiosta tilannetietoisen, mutta sijaintipalveluita käyttäessä pitää punnita sijainnista saadut hyödyt ja nopeammin tyhjenevän akun haitat tarkkaan.

Windows Phone -sovelluskehitys Androidin ja iPhonen rinnalla

Windows Phonen astuminen iOS:n ja Androidin rinnalle on saanut aikaan hieman hämmennystä joidenkin loppukäyttäjien ja sovellusten tilaajienkin mielissä. Siinä missä iOS (iPhone/iPad), Android ja jopa poistuva Symbian muistuttavat toisiaan yleisasultaan – kukin tietenkin erottautuen omilla pienillä eroavaisuuksillaan – Windows Phone on tietoisesti suuntautunut erilleen muista omalla Metro-suunnittelukielellään.

Ohje: maassa maan tavalla

Suunnitellessa uutta sovellusta mille tahansa alustalle, yleispätevänä ohjenuorana voitaneen antaa:

Kunkin tyyppisen mobiililaitteen käyttäjä haluaa systemaattisen käyttäjäkokemuksen ja käyttöjärjestelmälle tunnusomaisen sekä varsin yhdenmukaisen ulkoasun sovelluksille. Sovelluksen käyttöönotto on tällöin mahdollisimman luontevaa eikä vaadi syvällistä perehdytystä.

Tämä halu kumpuaa usein niin käyttäjien puolelta kuin myös käyttöjärjestelmien toimittajilta! Jotkut palvelujen kehittävät mieltävät ajattelevansa käyttäjän parasta pyrkimällä mahdollisimman yhdenmukaiseen käyttöliittymään laitteen käyttöjärjestelmästä riippumatta, mutta päätyvät tekemään käyttäjälle karhunpalveluksen. On poikkeustilanne, että ihmiset käyttävät samaa sovellusta eri käyttöjärjestelmillä, mutta varmaa, että he käyttävät sitä muiden saman käyttöjärjestelmän sovellusten lomassa.

Suunniteltaessa sovellusta usealle alustalle täysin samaa konseptia ei siis voi aina käyttää. Vaikka iPhone ja Android-sovellukset voivat muistuttaa toisiaan hämmentävän paljon, niillä on eronsa. WP:llä erot ovat muihin alustoihin verrattuna huomattavat.

Esimerkki

Alla käytännön vertailuesimerkki IMDB-sovelluksesta Applen iPhonelle ja Windows Phonelle.

Kuva 1. iPhone versio IMDB:n sovelluksesta.

Kuva 2. WP-versio IMDB-sovelluksesta.

Edellä esitetyt kuvat osoittavat selvästi, että iPhonen käyttäjät eivät pitäisi WP:n tyylisestä sovelluksesta puhelimessaan (mikäli se edes pääsisi Applen App Storeen), eivätkä vastaavasti Windows Phone -käyttäjät halua iPhone/Android-tyylistä sovellusta laitteilleen. Toisilleen vieraat esitystavat vaativat sovelluksen arkkitehdilta jo konseptin varhaisessa vaiheessa ydinidean hahmottamiskykyä ja näkemystä mitä kukin loppukäyttäjä haluaa siltä.

Microsoftin sivuilta löytyy (englanniksi) esimerkkejä erilaisista sovellustyypeistä Windows Phonella.

Päätelmä

Metro-tyyliohjeita noudattavat Windows Phone -sovellukset ovat niin toimintalogiikaltaan kuin ulkoasultaan selvästi erilaisia kuin iOS/Android-taustaiset sovellukset, vaikka tarjottava toiminnallisuus olisi sama.

Windows Phonen tarjoaman visuaalisesti rikkaan Panorama-näkymän avulla yritysten brändäys ja yleensäkin halutut teemat sovelluksissa voidaan saada muita alustoja vahvemmin esille (kuva 2). Tämä toki vaatii ylimääräisen graafisen aineiston luomista.

Vaikka Windows Phonella vältetään käyttöliittymäelementtien ylimääräisiä koristeita, kuten liukuvärejä, tekstuureita, varjostuksia ja kiiltoefektejä, sisältö pyritään vastaavasti tuomaan esiin niin, että lopputulos voi hyvin olla kauniimpi kuin muilla alustoilla.

 

 

Manuaalinen muistinhallinta “for dummies” iOS-alustalla osa 2

Edellisessä osassa esiteltiin tapa yksinkertaistaa muistinhallintaa tekemällä luokkamuuttujista propertyja. Tällä kertaa syvennytään hieman siihen miten ja milloin Objective-C kutsuu retain ja release metodeja objekteille. Lopuksi vielä muutama huomautus objektien säilömisestä kokoelmien sisällä.

Alla olevassa EsimerkkiYksi.h tiedostossa on esitelty muuttuja minunTeksti, josta on tehty property. Objective-C luo propertyille automaagisesti mutator-metodit eli metodit joilla muuttujan sisältöä voidaan helposti manipuloida.

EsimerkkiYksi.h

@interface EsimerkkiYksi : NSObject {
  NSString *minunTeksti;
}

@property(nonatomic,retain) NSString *minunTeksti;

-(void)esimerkkiMetodi;

@end

Alla olevassa Esimerkki.m tiedostossa on havainnollistettu miten minunTeksti objektia voidaan käsitellä gettereilla ja settereilla.

EsimerkkiYksi.m

@implementation EsimerkkiYksi
@synthesize minunTeksti;

-(void)esimerkkiMetodi {
   //Asettaa minunTeksti muuttujaan tekstin "Mun teksti!"
   self.minunTeksti = [NSString stringWithString: @"Mun teksti!"];

   //Tekee saman asian eri syntaksilla.
   [self setMinunTeksti: [NSString stringWithString: @"Mun teksti!"]];

   NSLog(@"Teksti: %@", minunTeksti);

   NSString *toinenTeksti;

   //Asettaa toinenTeksti muuttujan viittaamaan minunTeksti muuttujaan.
   toinenTeksti = self.minunTeksti;
   NSLog(@"Toinen teksti: %@", toinenTeksti);

   //Sama asia eri syntaksilla.
   toinenTeksti = [self minunTeksti];
   NSLog(@"Toinen teksti: %@", toinenTeksti);
}

@end

Ohjelmoija voi myös korvata muuttujiensa mutator-metodit haluamillaan. Yläpuolella olevaan EsimerkkiYksi luokan tapauksessa metodit voisi kirjoittaa auki seuraavanlaisesti:

-(NSString*)minunTeksti {
   return minunTeksti;
}

-(void)setMinunTeksti:(NSString*)uusiTeksti {
   //Nostaa uusiTeksti muuttujan retainCounttia yhdellä. "Ottaa vastuun objektista"
   [uusiTeksti retain];

   //Vapauttaa minunTeksti muuttujan.
   [minunTeksti release];

   //Asettaa minunTeksti muuttujan.
   minunTeksti = uusiTeksti;
}

Jos kyseiset metodit sisällytetään luokkaan niitä kutsutaan aina, kun minunTeksti muuttujaa pyydetään tai asetetaan self notaation kautta. Mahdollisia ongelmatilanteita syntyy jos setterin sisällä kutsutaan retain ja release metodeja väärissä järjestyksissä. Toinen asia mikä pitää muistaa luodessa omia mutator-metodeja on se, ettei niiden sisällä pidä koskaan käyttää self notaatiota.

Alla esimerkki tilanteesta, jossa ohjelma menee loputtomaan looppiin, koska ohjelmoija käyttää getter-metodin sisällä self notaatiota. Jos olet epävarma on parasta olla muokkaamatta propertyille luotuja mutator-metodeja.

//EI NÄIN!!!!
-(NSString*)minunTeksti {
   return self.minunTeksti;
}

Tapauksissa, joissa objekteja säilötään esimerkiksi NSArray tai NSDictionary kokoelmien sisällä on huomioitavaa, että sisään syötettäville objekteille kutsutaan automaattisesti retain metodia. Muistivuotojen välttämiseksi kokoelmiin tulee syöttää vain autoreleasettuja objekteja, ellei objektin oleteta säilyvän kokoelman tuhoutumisen jälkeen. Alla esimerkki kokoelmien käytöstä.

-(void)populoiArray {
   //EI NÄIN!!!
   NSString *testi = [[NSString alloc] initWithString: @"Testi teksti!"];
   self.testiArray = [NSArray arrayWithObject: testi];

   //testi muuttuja ei ole autoreleasettu. Muistivuoto syntyy, kun testArray tuhotaan.
}

-(void)populoiToinenArray {
   //NÄIN
   NSString *testi = [NSString stringWithString: @"Testi teksti!"];
   self.testiArray = [NSArray arrayWithObject: testi];

   //testi muuttuja on autoreleasettu, testiArray ottaa sen haltuun ja vapauttaa sen, kun testArray tuhotaan.
}

OpenGL ES 2.0 kehityksen aloittaminen

OpenGL ES 2.0 on ollut speksinä olemassa vuodesta 2007 ja tarjolla laitteissakin jo tovin. 3D-grafiikkaan nojaavia mobiileja menestystarinoita ei ihan Angry Birds -mittakaavassa ole näkynyt mutta laitteiden alati parantuessa ja käyttökokemusten karttuessa 3D-renderöinti saattaa hyvinkin nousta keskeisempään rooliin mobiilimaailmassakin. Eivätkä ohjelmoitavan OpenGL ES 2.0 shader pipelinen mahdollisuudet grafiikan piirtämiseen lopu!

Tässä artikkelissa käydään läpi muutama asia jotka on tiedostettava – ja ehkä jopa osattava – aloittaessaan OpenGL ES 2.0 kehitystä. Varsinaisena kohdeyleisönä ovat ne joilla on edes jonkinlainen käsitys 3D-grafiikan periaatteista ja ovat tehneet joitain asioita vanhanmallisella fixed pipeline OpenGL:llä (kuten OpenGL ES 1.1 tai desktop maailman OpenGL 1.x) mutta tarjonnee teksti hieman lohtua aivan vasta-alkajallekin. Ensin käydään läpi uudet asiat jotka tulevat vastaan kun siirrytään fixed function renderöinnistä programmable pipelinen maailmaan ja lopuksi rakennetaan yksinkertainen 3D-softa iOS:lle käyttäen Xcoden OpenGL ES wizardia.

Matriisit

3D-grafiikan kulmakivi, kuvausmatriisit, olivat sisäänrakennettuina vanhassa OpenGL:ssä glPushMatrix()/glPopMatrix()/glRotate() jne myötä. Programmable pipelinessä matriisipinoa ei ole tarjolla ja matriisioperaatiot on toteutettava itse. Käytännössä kaikki alustat tarjoavat omat matriisikirjastonsa tähän (QMatrix4x4 Qt:ssä, android.opengl.Matrix Androidille sekä iOS 5.0 alkaen GLKMatrix4 iPhonelle) tarkoitukseen. Jos affiinit kuvaukset eivät ole tuttuja, aika oppia ne on nyt! Kuvausmatriisien hallinta on käytännössä ehdoton vaatimus modernin OpenGL:n taitamiselle.

Shaderit

Matriisien ohella shaderit ovat suurin muutos. Fixed pipeline hoiti käyttäjän puolesta geometrian transformoinnin ja pikselien piirtämisen; programmable pipelinessä kaikki tehdään itse. Tämä mahdollistaa valtavan paljon enemmän kontrollia siihen mitä ollaan tekemässä: vanha OpenGL käytännössä vaan konfiguroitiin (uploadattiin geometria ja tekstuurit, asetettiin valot ja transformaatiot) ja sen jälkeen OpenGL hoiti loput; lopputulokseen ei voinut vaikuttaa kauheasti. Kaikki tämä on nyt toisin ja ainakin ne, joilla on softwarerenderöintitaustaa, iloinnevat tästä! Shaderin kirjoittajalla on täysi valta (ja toisaalta myös vastuu) piirtää geometria juuri sillä tavalla kuin huvittaa.

Shaderit ovat käytännössä pienia C-kieltä muistuttavia OpenGL Shader Language (“GLSL”) ohjelmia joita ajetaan erittäin tehokkaissa shader prosessoreissa näytönohjaimella. GLSL:n eri versiot ovat keskenään hieman erilaisia; versio 1.20 on yhteensopiva OpenGL ES 2.0:n kanssa. OpenGL ES 2.0:ssa näitä shadereita on kahdenlaisia; ns. Vertex Shaderit sekä Fragment Shaderit. Rutkasti tiivistäen shaderit toimivat seuraavasti: vertex shader -ohjelmaa kutsutaan (ajurin/raudan toimesta) kerran per polygonin vertex. Vertex shader hoitaa geometrian transformoinnin ja voi laskea lisää asioita kuten valaistuksia, distance fogia jne. Vertex shaderin laskutoimitusten lopputulokset (outputit) interpoloidaan yli piirrettävänä olevan polygonin ja tarjoillaan inputteina fragment shaderille “varying” muuttujina. Fragment shader laskee näiden avulla lopullisen piirrettävän pikselin (“fragmentin”) väriarvon. Tämä on fragment shaderin ainoa output. Nyrkkisääntönä, tee kaikki laskenta minkä voit vertex shaderissa ja säilytä fragment shader mahdollisimman yksinkertaisena.

Esimerkki Vertex Shaderista:

// "Attribuutteja" jotka lähetetään shaderille OpenGL:stä
// automaattisesti jahka ne on enabloitu
attribute vec4 position; // transformoimaton "object space" koordinaatti
attribute vec3 normal; // transformoimaton "object space" vertex normaali
attribute vec2 tex_coords; // tekstuurikoordinaatit u,v

// "Uniformeja"; OpenGL:stä manuaalisesti lähetettyjä arvoja jotka ovat
// samat koko piirrettävälle geometrialle
uniform mat4 modelViewProjectionMatrix;
uniform mat3 normalMatrix;

// Fragment shaderin outputit
varying lowp float ndotVarying;
varying mediump vec2 texCoordsVarying;

// Vakio
const vec3 lightPosition = vec3(0.0, 0.0, 1.0);

void main()
{
  // Lasketaan valaistusarvo pinnan normaalin ja valon suunnan välisestä
  // kulmasta kosinilla (dot()) joka rajataan väliin 0..1
  vec3 eyeNormal = normalize(normalMatrix * normal);
  float ndot = max(0.0, dot(eyeNormal, normalize(lightPosition)));

  // Valmistellaan outputit; näiden interpoloidut arvot päätyvät
  // fragment shaderille inputeiksi
  ndotVarying = ndot;
  texCoordsVarying = tex_coords;

  // Geometrian transformointi. gl_Position on varattu sana output
  // muuttujalle johon tämä informaatio kirjoitetaan.
  gl_Position = modelViewProjectionMatrix * position;
}

Esimerkki Fragment Shaderista:

uniform lowp sampler2D texture; // OpenGL:stä passattu "handle" tekstuuriin

// Shaderin inputit; nämä laskettiin fragment shaderissa, interpoloitiin
// raudan toimesta ja ovat nyt tarjolla lopullisen pikselin värin laskemiseksi
varying lowp float ndotVarying;
varying mediump vec2 texCoordsVarying;

void main()
{
  // Tämän shaderin tapauksessa lopullinen pikselin väri lasketaan seuraavasti:
  // sampletaan tekstuurista oikealta kohdalta pikseli ja kerrotaan se
  // valoisuusarvolla (0..1). Lopputulos on kauniisti Lambert/flat shadeutuva
  // pinta jossa valo paistaa katselusuunnasta.
  highp vec4 color = texture2D(texture, texCoordsVarying) * ndotVarying;

  // gl_FragColor on (kuten kaikki gl_ prefiksin GLSL nimet) varattu sana
  // muuttujalle johon lopullinen fragment shaderin output kirjoitetaan.
  gl_FragColor = color;
}

Muut asiat

Koska meillä on shaderit ja niiden myötä valta piirtää miten haluamme, monia vanhan OpenGL:n kutsuja ei tarvita enää mihinkaan. Esimerkkeinä vaikkapa glEnableClientState() ja glEnable(GL_TEXTURE_2D). Lisäksi GLUT-kirjastoa ei ole tarjolla suoraan; matriisikirjaston onkin kyettävä tarjoaamaan toiminnallisuus paitsi rotaatioiden/translaatioiden myös perspektiivimatriisien luomiseksi. Ideaalinen tila tietysti on että kehittäjä tietää miten glFrustum() toimii. Shadereihin liittyvät attributet ja uniformit pähkinänkuoressa: attributet ovat mekanismi tuoda geometriaan liittyvä data vertex shaderille. Niitä hallitaan glBindAttribLocation() / glEnableVertexAttribArray() / glVertexAttribPointer() kutsuilla. Uniformit taas ovat kehittäjän shadereilleen syöttämää dataa jotka ovat yhtenäisiä (tästä nimi..) läpi koko kappaleen (tai sen osan, tai usean kappaleen) geometrian, esimerkiksi transformaatiomatriisit, tekstuurit jne. Uniformeja hallitaan glGetUniformLocation() / glUniform*() kutsuilla.

Toteutus: OpenGL ES 2.0 “Hello, World”

Uusi Xcode ja iOS 5.0 ovat tehneet tämän helpoksi; Xcodesta löytyy projektinluontiwizard jolla syntyy toiminnallisuudeltaan täydellinen OpenGL ES 2.0 sovellus shadereineen kaikkineen. Wizardin luoma toteutus piirtää sinisen ja punaisen laatikon jotka pyörivät toistensa sekä omien keskipisteidensä ympäri. Toinen laatikko piirretään GLSL:llä, toinen GLKitin avulla. GLKit on hyvin laaja ja ominaisuusrikas OpenGL-apukirjasto joka
esiteltiin iOS 5.0 päivityksen myötä. Wizardin/GLKitin käytössä on puolensa; toisaalta pääsee nopeasti liikkeelle toimivalla rungolla, mutta toisaalta se myös tarjoaa kehittäjälle mahdollisuuden olla oikeasti oppimatta mitä konepellin alla tapahtuu.

Tässä harjoituksessa luodaan wizardilla oletusprojekti ja muokataan sita siten
että sininen kuutio piirretään tekstuurimapattynä väritetyn sijaan.
Valitaan Xcode:sta File > New > New Project.. ja valitaan GLKit wizard templaatiksi:

Korvataksemme alkuperäisen kovakoodatun värityksen tekstuurilla, on tehtävä
jokunen asia:

  1. Vertex attribuutteihin lisätään tekstuurikoordinaatit; muuttuja gCubeVertexData[] sisältämään taulukkoon lisätään u,v arvot ja muutetaan sitä käyttäviä osia koodissa sopivasti. (Tähän liittyen, omasta mielestäni on paljon elegantimpaa käyttää C:n structia kuin float-arrayta vertex attributejen kuvaamiseen).
  2. Lisätään attribute ATTRIB_TEXCOORD ja uniform UNIFORM_TEXTURE sekä niiden vaatimat muutokset muualle koodiin.
  3. Ladataan tekstuuri kuvatiedostosta ja uploadataan se OpenGL:n puskureihin;kuvan lataaminen tehdaan UIImage:lla ja byte datan ekstraktoimiseen käytetään seuraavaa apukirjastoa: UIImage-Conversion (Huom., GLKitin mukana voi hyvinkin olla jouhevampikin tapa tehdä tämä).
  4. Muutetaan shaderien koodi tukemaan tekstuuria; katso Shader-esimerkit yllä!

Kuvakaappaukset ennen ja jälkeen muutosten:

Wizardin generoima koodi; kuutiot piirretään pelkin värein
Wizardin generoima koodi; kuutiot piirretään pelkin värein
Muokattu koodi; toinen kuutio piirretään teksturoituna
Muokattu koodi; toinen kuutio piirretään teksturoituna

 

 

 

 

 

 

 

 

 

 

Esimerkin lähdekoodi Xcode 4.2 projektina (598kB)

 

Loppusanat

OpenGL -osaaminen on jossain määrin harvinaista mobiilikehittäjien joukossa, luultavasti lähinnä siksi että sen sovellusalueet ovat olleen rajalliset. Asiaa ei auta korkeahko oppimiskynnys. Sen tarjoamat mahdollisuudet ovat kumminkin valtavat; shadereissa voidaan suorittaa mitä tahansa laskentaa rinnakkain CPUn corejen kanssa – mahdollistaen vaikkapa reaaliaikaisen signaalinkäsittelyn sijoittamisen pois itse suorittimelta.

Tämä artikkeli aloittaa 3D grafiikka / OpenGL ES 2.0 -juttusarjan; seuraavalla kerralla katsotaankin sitten jotain mielekkäämpää kuin ihan perusteita! Kysymyksia, palautetta, muuta sanottavaa? Jätä kommentti!

Lisää luettavaa

WikiBooks: Modern OpenGL Introduction

Lighthouse GLSL Tutorials

 

UIPickerView UIScrollView:n sisällä

UIPickerView inside UIScorllViewApple tarjoaa kattavan dokumentaation iOS-kehitykseen, mutta osa käytettävistä keinoista on kuitenkin jätetty jostain syystä liian pienelle huomiolle. Yksi esimerkki on heikommasta dokumentoinnista on tilanne, jossa UIScrollView-komponentin sisällä on UIPickerView-komponentti, tälläisessä tapauksessa UIScrollView nappaa kaikki kosketukset, eikä näin ollen UIPickerView ole käytettävissä.

Ratkaisu ongelman on varsin yksinkertainen, tai olisi, jos se kerrottaisiin dokumentaatiossa.

Jotta saat UIPickerView:n toimimaan kuten normaalisti, on sinun luotavat oma kustomoitu versio UIScrollView:stä, joka toteuttaa metodin touchesShouldCancelInContentView.

CustomScrollView.h

#import 
@interface CustomScrollView : UIScrollView {
}
@end

CustomScrollView.m

#import "CustomScrollView.h"
@implementation CustomScrollView
- (BOOL)touchesShouldCancelInContentView:(UIView *)view{
//cancels touches to any UIPickerView
//UIPickerView has class description UIPickerTable
if ([[[view class] description] isEqualToString:@"UIPickerTable"]){
return NO;
}
return YES;
}
@end

Tämän lisäksi on CustomScorllView:tä käyttävässä luokassa asettava CustomScrollView:lle seuraavat ominaisuudet ja näille arvot.

CustomScrollView.canCancelContentTouches = YES;
CustomScrollView.delaysContentTouches = NO;

Sopiva paikka ominaisuuksien asettamisille on esimerkiksi CustomScrollView:ta käyttävän luokan ViewDidLoad-metodissa.

iPhone Hei Maailma tutoriaali

T’ällä kerralla käydään läpi yksinkertaisen “Hei Maailma”-sovelluksen tekeminen iPhone:lle. Tutoriaali olettaa, että olet asentanut kehitysympäristön, joten jos et vielä ole, niin katso Artun kirjoittama artikkeli Näin aloitat iOS-kehityksen. Artikkelissa ei mennä vielä Objective-C:hen saloihin, vaikkakin pari riviä tullaan sitäkin copy+pasteamaan.

Avaa Xcode, tee uusi projekti File -> New Project ja valitse View-based Application.

XCode

View-based Applicationit sisältävät yhden näkymän, joka on heti valmiiksi käytettävissä. Monimutkaisemmat monen näkymän sovellutukset on hyvä toteuttaa Window-based Applicationina.

Valitse projektisi tallennuspaikka ja nimeksi HellowWorld (Huomaa HellowWorld, kiiressä luotu projekti sisälsi nimessään yhden kirjaimen liikaa :)). Tallennettuasi uuden projektin, pitäisi XCoden avautua alla olevan kaltaiseen näkymään.

Xcode mainviewProjekti on nyt alustettu ja siihen on liitetty myös yksi näkymä view.  Avaa HellowWorldViewController.h tiedosto editoriin ja korvaa siellä näkyvät lähdekoodi alla olevalla.

#import <UIKit/UIKit.h>

@interface HellowWorldViewController : UIViewController {
    IBOutlet UILabel* heiMaailmaLabel;
}
@property (nonatomic, retain) IBOutlet UILabel *heiMaailmaLabel;

-(IBAction)nappiaPainettu:(id)sender;

@end

Otsikkotiedosto on nyt valmis. Kiinnitä huomioita IBOutlet ja IBAction termeihin. IBOutlet viittaa siihen, että viewcontroller käskyttää näkymää ja IBAction sitä, että näkymä käskyttää viewcontrolleria.

Avaa seuraavaksi HellowWorldViewController.m tiedosto. .m-tiedostot toteuttavat otsikkotiedostossa olevan luokan kuvauksen Class implemantation file.
Tee siihen alla olevat lisäykset.

#import "HellowWorldViewController.h"

@implementation HellowWorldViewController

@synthesize heiMaailmaLabel;

-(IBAction)nappiaPainettu:(id)sender{
	heiMaailmaLabel.text = @"Hei Maailma!";
}
//Monta riviä komentoitua tekstiä
//...
//Ja pari metodia johon ei tarvitse koskea.
- (void)dealloc {
	[heiMaailmaLabel release];
    [super dealloc];
}
@end

Lähdekoodit on nyt kirjoitettu ja voidaan siirtyä näkymän rakentamiseen. Tupla klikkaa HellowWorldViewController.xib tiedostoa ja Interface Builder aukeaa. Alla olevaan näkymään.

Xcode_interface_builder

Valitse Library ikkunasta Round Rect Button – komponentti ja raahaa se harmaaseen View-ikkunaan. Liikuta nappi keskelle alas ja muuta komponentin asetuksista Title-kenttään teksti “Paina Tästä”. Button Attributes – näkymän saa esille, kun nappi on valittu ja painat Command key icon+1.

UIButton_IOSKun nappi on aseteltu, etsi Library-näkymästä Label-komponentti ja raahaa sekin harmaaseen näkymään. Isonna Label-komponenttia vetämällä sen reunoista, tyhjennät Text-kenttä ja valitse Layout -> keskitys. Näkymäsi pitäisi nyt näyttää tältä.

Interfacebuilder_ViewLisätään seuraavaksi nappiin aikaisemmin määrittelemmä IBAction. Paina kerran näkymässä näkyvää Paina Tästä-nappia ja tämän jälkeen Command key icon+2, jolloin saat näkyviin Button Connections-näkymän. Paina ctrl nappi pohjaan ja vedä hiirellä Touch Up Inside pallurasta yhteys File’s Owneriin ja valitse nappiaPainettu:.

Alla oleva kuva havainnoillistanee asiaa.Interfacebuilder_IBActionNäin liitimme aiemmin lähdekoodissa määritelemmä IBAction-tapahtuman tapahtuvaksi, kun sormi nousee ylös Paina Tästä-napista. Huomaa, että hiukan epäintuitiivesti napin painaminen tarkoittaa iOS-laitteissa sitä, että sormi nostetaan pois napilta.

Aiemmin lähdekoodissa määritelemmä IBOutlet yhdistetään nyt näkymään. Valitse File’s Owner, paina ctrl-nappi pohjaan, vedä yhteys Label:iin ja valitse heiMaailmaLabel.intefacebuilder_IBOutlet

Tallenna näkymä Command key icon+s, palaa takaisin XCodeen ja  paina Build & Run .

Simulaattorin pitäisi nyt pyörähtää käyntiin ja näyttää tekemäsi sovellus.

iphone_hello_worldHeipä vain maailma ! Ensi kerralla jotain vähän erikoisempaa.

iPhone-kehityksestä suomeksi

iPhone ja iPad ovat nyt kovassa huudossa. Päivittäin voimme lukea lehtijuttuja, kuinka suomalaisen Rovion Angry Birds valloittaa maailmaa, kuinka Helsingin Sanomat on julkaissut iPad-sovelluksensa tai kuinka Vanjoki hyppäsi Apple-kelkkaan.

Näiden tarinoiden taustalla on satojen tuntien iOS-kehitystyö, joiden tarinat ovat vielä jäännet suomeksi kertomatta.

iPhone-kehitys

Tämän blogin tarkoituksena on kertoa kaikille kiinnostuneille iOS-sovelluskehityksestä, siinä käytettävistä työkaluista, jipoista ja tärkeimmistä alaan liittyvistä uutisista.

Blogin kirjoittajina toimii Qvik Oy:n työntekijät, joilta löytyy kokemusta kymmenistä iOS-sovellutuksista ja yleisesti mobiilikehityksestä. Pyöritämme myös Android-kehityksestä kertovaa blogia osoitteessa androidkehitys.fi.