One thing that I love about Obj-C and Cocoa is how seamlessly Apple is introducing funcionalities to take advantage of multicore devices.
One of the most common things that a dev does by hourly basis is enumerate a collection.
In Obj-C we can takle this with different approaches:
1 – old plain C for/while loops
2 – fast enumeration protocol (the for..in)
3 – Create an NSEnumerator object from the collection
4 – Use enumeration methods declared in the object interface

The 4th approach is present in almost each kind of collections and one of its methods is the

- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(id obj, BOOL *stop))block

The opts parameter is a bit mask where we can pass this really interesting option:

NSEnumerationConcurrent

As the name states this makes the enumeration process a concurrent task and it’s a pleasure to see in instruments the 2 cores working all togheter. There is one caveat as stated by Apple engineers “the code of the block must be safe against concurrent invocation”.

One other stuff that I really love is the way we can help thread safety using blocks.
Recently for a social-instagram-like app I needed to use extensively the AFNetworking library.
To take trace of the networking operation created by each view controller I created a sort of register of the living connection.
The problem in this scenario is the async nature of the process related to the networking tasks. After adding an operation to the register, is impossible to know when it will be  finished (and consequently removed from the register) and this could happen while adding another one the register. We need to put things in order.

To help us we can use a thread locking mechanism,  I’ve found really uselfull the dispatch_barrier mechanism that has less overhead compared to an exclusive lock.

First we need to create our concurrent queue.
In the reading method we create a synch block that waits for the reading to end, if more cores are avalaible more reading request can occur, in the writing method we use a barrier to be sure that no other code has access until the writing finishes. Since GCD queue are executed in first in – first out order we can guarantee that the block would be executed in the same order we have requested.
As you can see from the picture (taken from the book Effective Obj-C 2.0) while the writing block is executing no reading (or writing) can happen.

Read-Write process

Read-Write process

In code is pretty much something like that.

First we create a our concurrent queue

syncQueue = dispatch_queue_create("it.shootshare.registeroperation", DISPATCH_QUEUE_CONCURRENT);

Then we override our properties:

- (NSString*) name {
  NSString * __block aName = nil;
  dispatch_sync(syncQueue, ^{
     aName = _name;
  });
  return aName;
}

- (void) setName:(NSString*)aName {
   dispatch_barrier_async(syncQueue, ^{
     _name = aName;
  });
}

I don’t want to start explaining what is “the internet of things” because it’s a really huge topic, but I’m here to share my first experience into connect an iOS device to a micro controller called Pinoccio and what that means.

What is Pinoccio? am I missing an “H”?

Is really hard to describe it, I’m really proud about being a funder of this project on indiegogo.

Pinoccio at a first first look seems to be like a pumped Arduino with a WiFi shield and mesh radio communication. If you are wondering.. yes is compatible with the Arduino IDE and you can compile your own custom firmware, but do we really need that? Building a firmware with the Arduino IDE that include a network library, shield communication can be really frustrating, if you are not familiar with micro-controllers, is really a long hard road. For instance the memory is a big problem into creating a sort of web-server that can run into a micro-controller.
One of my first desire when I ‘ve got my first iPod Touch was to connect my device on everything and when I started to build my own applications using the iphone sdk, I always tried to figure out how to do that, but I’ve found huge obstacles that I wasn’t still prepared to tackle.
In the earlier realeses of iOS version wasn’t possible to connect the iphone to a sort of prototype board, well not exactly but I have should enroll to the made for iphone/ipod program and the process wasn’t really easy. The accessory framework had very few documentation available to the public. Now we can use the Bluetooth LE to connect with an accessory but it seems to be locked for 1:1 connection (but I’m not totally sure about it, for instance the multipeer connectivity can connect up to 8 devices).
Then I started to use Arduino a little, but the problem was still the connection between the 2 devices, the only option was using the ethernet shield, build something like a webserver and access Arduino pins with a sort of web services. Not so easy and also with a lot of limits about memory, bottlenecks etc in my mind I couldn’t find a way to do something that could be ready for production.

Later Redspark started to sell a cable (in compliance with the made for iphone program). With this cable is possible using the serial communication to make an Arduino talk with an ios device, but under current Apple policy this cable may not be used with apps sold on the App Store.

Then I’ve found ElectricImp, I was really interested into that project but when I heard that I would always need to stay bounded to their cloud platform I gave up, but the Imp is a really cool device if you accept that limitation (or feature).
On Febraury 2013 during an Indiegogo campaign I heard about Pinocc.io it was love at first sight.

Pinoccio image

Pinoccio with all its features

Why Pinoccio changes everything?

  • It is a really small and cool prototyping board.
  • They are making an FCC certified version for production of a size of a nail.
  • You can build you own firmware in the old Arduino way.
  • You can use a scripting language to communicate with it from a web terminal (and via REST).
  • You can log it and launch those script commands using REST webservices.
  • You are not bound to use their platform.
  • You can create your own mesh of pinocc.ios and only one of them need to comminicate WiFi.
  • You can create your own custom script (maybe because you need to communicate in a specific way with a sensor or peripheral)
  • It’s all wireless
Pinocc.io Web interface

Pinocc.io Web interface

It’s pretty clear that you can do almost everything you want.

The idea here is to send all your request to their (or yours) platform and read or write properties with simple calls on the the platform, and this is a reallu smart idea, because most probably the platform manages the traffic on the board, maybe also caching some responses, avoiding bottle necks or massive requests.

To make a simple example about how this board connected to you iOS device can be powerful, I’ve made an easy app that asks the on-board temperature of the two scouts.
We have a lead scout (Optimus Prime) with the Wi-Fi backpack on it and the other scout (Bumble Bee) that communicates through the lead scout.
The lead scout is installed in my home and the other outside – of course the on board temperature is not the real environment temperature because is due to the load of the CPU and the environment temperature itself-.

Pinoccio temperature app

A video can be found here.

This is a simple example but let me try to figure out something bigger.

Let’s imagine that each scout has a Bluetooth LE backpack and a simple OLED display installed in a shoes shop. Each shoes has its own scout (like a price tag) with the display that shows the like/reviews numbers.
Each scout sends an iBeacon signal to identify a specific product close to them. When a user get close to the product using the shop application connects to that scout/ibeacon and get the product details, there is also the possibility to “like” it, once the use press like on the phone the like number on the display is incremented almost in real-time.
The other interesting example could be to measure the energy consumption of our house integrating a hall effect sensor on each scout and display data in a cool web page or into your mobile phone.

Arduino in my opinion created a revolution opening the market to all those boards, these is just the beginning because the revolution of the DIY is cultural and educational.
I hope to have more time to play around using Pinocc.io and this is the first time that I feel I have something “ready for production” and not just a new cool R&D gadget.

I’ m working on a photo app with a lot of technical challenges to tackle, but I hate when you spend a lot of times on stupid things mostly due to the lack of documentation. EXIF orientation flag is one of them, so let’s try to explain it.

An image can contain additional information, those info can be stored inside the EXIF format specification.

EXIF Tags (or metadata) cover a lot of different aspects:

- timestamp information

- camera settings (such as ISO,white balance, orintation)

- a thumb image

- geographical coordination

- other descriptions and copyright info

Orientation flag is based on the rotation of the camera respect to the ground (and probably the scan directions of the image).

iOS cameras follow the same rules as new digital camera, so the EXIF orientation flag is a reality, the problem is that conflicts somehow with the UIKit defined enumeration for UIImage orientations.

typedef enum {
 UIImageOrientationUp =            0, // 0 deg rotation exif 1
 UIImageOrientationDown =          1, // 180 deg rotation exif 3
 UIImageOrientationLeft =          2, // 90 deg CCW exif 6
 UIImageOrientationRight =         3, // 90 deg CW exif 8
 UIImageOrientationUpMirrored =    4, // horizontal flip exif 2
 UIImageOrientationDownMirrored =  5, // horizontal flip exif 4
 UIImageOrientationLeftMirrored =  6, // vertical flip exif 5
 UIImageOrientationRightMirrored = 7, // vertical flip exif 7
} UIImageOrientation;

Let’s see how orientations behave during 3 different kinds of image acquisition.

We will start with UIImagePickerController. Doing some experiment shooting and rotating the device we obtain this table.

Using back camera:

DeviceOrientation UIImageOrientation EXIF
Portrait UIImageOrientationRight 6
LandRight UIImageOrientationUp 1(default)
LandLeft UIImageOrientationDown 3
PotraitUpsideDown UIImageOrientationLeft 8

With front camera:

DeviceOrientation UIImageOrientation EXIF
Portrait UIImageOrientationRight 6
LandRight UIImageOrientationDown 3
LandLeft UIImageOrientationUp 1
PotraitUpsideDown UIImageOrientationLeft 8

To obtain those data you just need to add those few lines of code inside the delgate method:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
 // ...
 UIImage * originalImage = (UIImage *) [info objectForKey:  UIImagePickerControllerOriginalImage];
 NSDictionary *i mageMetadata = [info objectForKey: UIImagePickerControllerMediaMetadata];
 NSLog(@"Metadata Exif Orientation: %@   UIImageOrientation %d", imageMetadata[(NSString*)kCGImagePropertyOrientation],   originalImage.imageOrientation);
 //...
}

Using AVStillImageOutput things change. Avoiding the orientation correction that we could apply using -videoOutputOrientation on AVCaptureConnection we will obtain always the same number, the exif orientation number 6

 [[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:[[self stillImageOutput] connectionWithMediaType:AVMediaTypeVideo]
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
 CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate);
 NSLog(@" Metadata %@", (__bridge NSDictionary*)attachments);
 CFRelease(attachments);
 // ...
}];

The same happens when we play with AVCaptureVideoDataOutput delegate method. Each frame is returned with orientation number 1, that means that we need to fix that programmatically, and that is what Apple does in the SquareCam sample code. They get the device orientation and convert it into EXIF.

 /* kCGImagePropertyOrientation values
 The intended display orientation of the image. If present, this key is a CFNumber value with the same value as defined
 by the TIFF and EXIF specifications -- see enumeration of integer constants.
 The value specified where the origin (0,0) of the image is located. If not present, a value of 1 is assumed.
 used when calling featuresInImage: options: The value for this key is an integer NSNumber from 1..8 as found in kCGImagePropertyOrientation.
 If present, the detection will be done based on that orientation but the coordinates in the returned features will still be based on those of the image. */
 enum {
  PHOTOS_EXIF_0ROW_TOP_0COL_LEFT          = 1, //   1  =  0th row is at the top, and 0th column is on the left (THE DEFAULT).
  PHOTOS_EXIF_0ROW_TOP_0COL_RIGHT         = 2, //   2  =  0th row is at the top, and 0th column is on the right.
  PHOTOS_EXIF_0ROW_BOTTOM_0COL_RIGHT      = 3, //   3  =  0th row is at the bottom, and 0th column is on the right.
  PHOTOS_EXIF_0ROW_BOTTOM_0COL_LEFT       = 4, //   4  =  0th row is at the bottom, and 0th column is on the left.
  PHOTOS_EXIF_0ROW_LEFT_0COL_TOP          = 5, //   5  =  0th row is on the left, and 0th column is the top.
  PHOTOS_EXIF_0ROW_RIGHT_0COL_TOP         = 6, //   6  =  0th row is on the right, and 0th column is the top.
  PHOTOS_EXIF_0ROW_RIGHT_0COL_BOTTOM      = 7, //   7  =  0th row is on the right, and 0th column is the bottom.
  PHOTOS_EXIF_0ROW_LEFT_0COL_BOTTOM       = 8  //   8  =  0th row is on the left, and 0th column is the bottom.
 };
 switch (curDeviceOrientation) {
  case UIDeviceOrientationPortraitUpsideDown:  // Device oriented vertically, home button on the top
   exifOrientation = PHOTOS_EXIF_0ROW_LEFT_0COL_BOTTOM;
  break;
  case UIDeviceOrientationLandscapeLeft:       // Device oriented horizontally, home button on the right
   if (isUsingFrontFacingCamera)
    exifOrientation = PHOTOS_EXIF_0ROW_BOTTOM_0COL_RIGHT;
   else
    exifOrientation = PHOTOS_EXIF_0ROW_TOP_0COL_LEFT;
  break;
  case UIDeviceOrientationLandscapeRight:      // Device oriented horizontally, home button on the left
   if (isUsingFrontFacingCamera)
    exifOrientation = PHOTOS_EXIF_0ROW_TOP_0COL_LEFT;
   else
    exifOrientation = PHOTOS_EXIF_0ROW_BOTTOM_0COL_RIGHT;
  break;
  case UIDeviceOrientationPortrait:            // Device oriented vertically, home button on the bottom
  default:
   exifOrientation = PHOTOS_EXIF_0ROW_RIGHT_0COL_TOP;
  break;
}

EXIF dictionary is not supported by all image formats, for instance PNG doesn’t support it. When we create a UIImage from a source (disk, remote) and the image raw data contains this flag is automatically converted into a UIImageOrientation value. This is really important also when we save an image, if we don’t bring it during the saving operation, the saved image will be shown in a wrong way.

Erika Sadun has made a very helpful pack of  methods and functions to use the camera in the right way also dealing with orientations.

Probably the idea behind UIImageOrientation is that it refers to the difference in degrees to apply if we want a match between the current device orientation and the EXIF flag related to a correct image orientation.

Di recente ho dovuto “smanettare” per un bel po’ di tempo sulle UITableView  con contenuto statico.

Cosa sono le UITableView statiche?

Dal punto di visto dell’utilità sono eccezionali perché in pochissimo tempo, tramite l’editor degli storyboard consentono di creare una lista di celle fissa, statica, nel senso che non può cambiare a runtime (anche se non è completamente esatto perché certi comportamenti possono essere sovrascritti). Ciò comporta il non dover implementare i metodi di data source (UITableViewDataSource).

Lo svantaggio è che non sono celle riutilizzabili, quindi un uso massiccio impatta in maniera negativa sulle performance. Devono essere, pertanto, usate quando il numero di elementi in una UITableView è basso.

Si pensi, ad esempio, di dover comporre una lista di attributi che descrivono l’articolo di un negozio, come:

  • data inserimento articolo
  • pezzi a magazzino
  • costo articolo
  • etc…

Per un requisito del genere una table view statica è un ottimo candidato per creare un’interfaccia di questo tipo. Essa apporta maggiori vantaggi rispetto ad una classica view con label e campi di testo.

Perché ? :

  1. le UITableView sono un elemento estremamente “mantenibile”: se volessi aggiungere una voce mi basterebbe aggiungere un’altra row
  2. le UITableView ereditano dalle  UIScrollView posso anche non preoccuparmi delle diverse dimensioni di schermo
  3. non c’è bisogno di impazzire dietro ad autolayout
  4. non è necessario implementare i metodi di datasource
  5. se l’ interfaccia presenta dei campi da occultare in alcuni casi posso semplicemente dire alla tableview che a quell’index path per quel specifico caso l’altezza è 0 (solo per UITableViewController)

Mi occuperò, dunque, proprio dell’altezza delle UITableViewCell. Uno degli ostacoli forse più difficili da superare nell’utilizzo di queste è riuscire a creare delle celle di altezza variabile che si adattino al contenuto. Per capirci, è sufficiente pensare a quelle di what’s app o dell’applicazione messaggi.

L’altezza delle UITableViewCell viene infatti calcolata prima che queste vengano create e renderizzate a schermo perché le altezze sono necessarie per calcolare la contentSize della UIScrollView; una soluzione spesso adottata è quella di creare un modello di altezze, ovvero si precalcolano le altezze prima di visualizzare le celle per poi passare questo dato nel metodo – tableView:heightForRowAtIndexPath: . Un’operazione che poteva diventare ancora più complessa nel qual caso le celle fossero state di diverso tipo. Ciò comportava la creazione delle celle nella fase di precalcolo in maniera da realizzare tutti i calcoli necessari e fornire un’altezza corretta.

Si era di fronte al classico “collo di bottiglia”. Su table view, in presenza di un grande numero di righe, si poteva  giungere ad una situazione molto fastidiosa: un blocco momentaneo con un ritardo sul caricamento dell’interfaccia.

iOS7 ha introdotto un nuovo metodo – tableView:estimatedHeightForRowAtIndexPath: che rimanda al momento della visualizzazione il richiamo del metodo – tableView:heightForRowAtIndexPath:.

Sostanzialmente richiede una stima che consenta alla UITableView di potersi dare una contentSize di settare le altezze delle scrollbar etc.

Anche in questo caso ci troviamo di fronte ad uno strumento che dovrebbe essere usato solo in certe occasioni. Infatti, il calcolo dell’altezza rimandato durante il caricamento della cella può creare un lag durante lo scrolling percettibile.

La stima da fornire può essere un numero fisso, magari basato su una media ipotetica dei diversi valori che l’altezza può assumere, oppure può essere un calcolo approssimato molto più veloce di quello più preciso.

Un suggerimento che vale la pena menzionare è il caching dei valori già calcolati delle altezze. Se le celle non subiscono modifiche dopo la loro visualizzazione è inutile ricalcolarne l’altezza ogni volta, basta restituire quella in cache.

 

Visto che l’argomento iBeacon sembra particolarmente interessante ho deciso di fare un video di 13 min con due piccole demo.

Spero possiate trovarlo interessante e perdonate le papere, ma andavo a braccio!!!!

Un altro articolo interessante sullo stesso argomento: iBeacon, behind the scene

 

La pirateria – e forse dico una cosa scontata – è un grosso problema anche per iOS.

Nel mondo del Jailbreak esistono AppStore fittizi dove è possibile scaricare gratuitamente copie di app che normalmente si trovano a pagamento.

Il tipo di utenza che usufruisce di questo servizio è divisibile in due macro-categorie:

a. persone che non compreranno mai la tua app

b. persone intenzionate all’acquisto, ma che desiderano provarla prima di investire i loro soldi nel tuo prodotto 

Riguardo al successivo pagamento da parte degli utenti tipo b. non bisogna farsi molte illusioni: sono davvero pochi coloro che regolarizzano la loro posizione andando sullo store virtuale di Apple.

Lo scenario è, dunque, tutt’altro che roseo. Un’ulteriore complicazione viene anche dalla natura stessa delle applicazioni. Questo genere di prodotti ha, infatti, una vita brevissima di utilizzo-medio, con il tangibile rischio che la ”prova” gratuita coincida perfettamente con il termine di utilizzo. Detto questo le app hanno anche costi rappresentativi del loro effettivo utilizzo. Apple, inoltre, non fornisce alcun periodo di trial sui prodotti distribuiti attraverso l’AppStore.

Come accade per altri settori, anche le tecniche per evitare la pirateria sulle app sono molto complesse, facilmente aggirabili e dall’elevata possibilità di creare un danno anche a chi l’applicazione l’ha regolarmente acquistata. Questo a causa di falsi positivi che possono essere causati anche per una semplice sostituzione del device. Non dimentichiamoci, poi, che Apple non vincola l’applicazione al dispositivo sul quale è stata scaricata, ma all’account dell’utente che l’ha acquistata.

Esistono soluzioni al problema? Una tecnica ottimale sarebbe quella di verificare se effettivamente l’utente ha effettuato l’acquisto, richiedendo una sorta di ricevuta digitale. Con iOS7 è stato introdotto un set di nuove API che consentono di ottenerla (anche se – a dire il vero – il sistema era comunque disponibile già con Lion). La classe è in oggetto SKReceiptRefreshRequest , interessante vero? Peccato che la verifica dello scontrino digitale sia ancora una procedura particolarmente complessa.

La ricevuta digitale sarebbe anche utilizzabile nel passaggio da applicazione a pagamento ad applicazione con modello freemium, in questo modo sarebbe possibile fornire a chi ha già pagato un set superiore di risorse.

La speranza è che venga rilasciata una qualche utility che renda l’analisi più snella (esiste già una lib su GitHub, che comunque richiede diverse conoscenze nell’ambito di certificati etc). O il danno economico diventerà sempre meno gestibile.

IOS7 è uscito, lo sappiamo e molti di noi lo hanno già installato. Il nuovo sistema operativo rappresenta un cambiamento radicale rispetto al 6, un cambiamento necessario per svecchiare un prodotto che rischiava di essere visto come “datato”, specie se confrontato con quelli proposti da concorrenti che, con il trascorrere degli anni, hanno saputo produrre sistemi operativi molto accattivanti.

Non parlerò delle funzioni di iOS7, delle quali è stato detto già abbastanza. Voglio guardare “il dietro le quinte” e ripercorrere le trasformazioni che iOS7 ha subito negli ultimi mesi, dalla developer preview 1 fino al rilascio ufficiale odierno. Un’altra maniera per capire il modus operandi di Apple, fra tentativi, certezze e modifiche dell’ultima ora.

Per noi sviluppatori iOS7 rappresenta “un problema” anzi, due problemi. Il primo è il missmatch creativo che si è creato tra sistema operativo e applicazioni “vecchie” che per lo più utilizzavano lo scheumorfismo.

Il secondo sono le API e il nuovo modo di “vedere” le applicazioni.

La questione ha due risvolti, entrambi da considerare.

Senza dubbio per innovare, è necessario guardare avanti e, nel caso, prendere decisioni unilaterali, di forza, in maniera che tutti vi si adeguino senza negoziare. Lasciando troppa libertà si corre il rischio che ognuno continui sulla sua vecchia strada, implementando API vecchie. Una nota personale: è da tempo che mi riprometto di imparare bene il linguaggio “Ruby”, ma ad ogni rilascio di sistema operativo mi vedo costretto a ricominciare a studiare tutto daccapo.

Apple è decisamente coerente con quello che dice e fa; alcuni elementi dell’interfaccia grafica standard sono stati macellati da diversi sviluppatori (me compreso) per accontentare le richieste di clienti a cui non piaceva il pop up bluetto perché non in linea con i colori aziendali. Da Cupertino, infatti, non si smetteva di sconsigliare modifiche a tali componenti, avvertendo che release future avrebbero potuto generare cambiamenti, con il rischio di dare vita a comportamenti inaspettati con l’applicazione di questi hack… Insomma tutti ne erano consapevoli tanto ieri come oggi.

 

Tuttavia, se la filosofia che sta dietro a determinate scelte è comprensibile, le vie che propone per renderla operativa sono talvolta meno limpide.

Parliamo del minimalismo dell’interfaccia grafica di iOS7. È chiaro che questa scelta non è fine a sé stessa come su altri sistemi (una pura questione estetica, di gusto), ma propedeutica al contenuto e al contesto presentato, animazioni incluse.

Meno chiaro, invece, la maniera attraverso la quale trasformare il concetto in qualcosa di reale, attraverso la programmazione. Infatti, benché iOS7 preveda che tutte le app compilate debbano utilizzare come area lo schermo intero, ad oggi sembra non esistere un modo semplice per cambiare questa azione e renderla simile ad iOS6. Una scelta che ha fatto letteralmente impazzire un certo numero di sviluppatori. Capita che le “vecchie” applicazioni ricompilate vengano visualizzate con un layout diverso e con i contenuti che “scivolano” sotto la status bar. Un problema non da poco per chi come me sta lavorando per implementare animazioni di transizione diverse da quelle standard tra una sezione e un’altra senza usare gli strumenti forniti da Apple.

Non c’è dunque da stupirsi se il forum ufficiale della Mela morsicata è animato da discussioni visitate da migliaia di persone. In pratica, con questa scelta, si obbliga lo sviluppatore da una parte a “ridisegnare” gran parte del layout e dall’altra a tenere sempre presente la bicompatibilità con il vecchio sistema.

Gli stessi ingegneri Apple si sono dimostrati non esattamente all’altezza: le loro risposte ancora latitano e quando arrivano, non sono del tutto esaustive, soffermandosi su un solo modo di programmare , talvolta utilizzato da una percentuale minima dei programmatori. Intanto anch’io mi sono cimentato, proponendo una soluzione che riduca l’impatto sulle ore di lavoro e mantenga la retrocompatibilità con il 6. La speranza è che funzioni.

 

Il confronto con i programmatori ha portato Apple ha fare alcuni importanti correttivi. Dai primi giorni di settembre è possibile scaricare anche le versioni vecchie per quelle app che hanno interrotto la compatibilità con i precedenti sistemi operativi.

Nel nostro caso stiamo sviluppando un update per un nostro prodotto, iEtiquette che sarà compatibile solo con iOS7. Mentre per chi non avesse o potesse aggiornare sarà comunque possibile scaricare la versione precedente. Questa è una scelta di importanza enorme perché favorisce sia sviluppatori che utenti.

Il cambiamento, come dicevo all’inizio di questo post è radicale. Sicuramente qualcuno si lamenterà. Negli ultimi giorni ho letto di diverse polemiche riguardo ad aggiornamenti  di applicazioni a pagamento che – con il pretesto del passaggio al sistema 7 – dovranno essere pagate di nuovo dagli utenti che le avevano a suo tempo scaricate. Un caso eclatante – notizia di oggi – riguarda il task manager CLEAR che nella nuova versione iOS7 compatibile richiede ben 3 euro.

L’effort per rendere una app compatibile con iOS7 è molto elevato e per applicazioni complicate (CLEAR non è tra queste) è abbastanza normale che gli sviluppatori richiedano un pagamento. Il problema è che non è possibile fissare un prezzo di update, o gratis o tutto. D’altra parte ritengo che sia normale che un’applicazione supporti gratuitamente un solo sistema operativo. È una situazione che nel mondo desktop viviamo tutti i giorni (è probabile che qualcuno se ne approfitti, avvelenando il mercato).

 

Altre sorprese passate in sordina sono la possibilità di scaricare app in 3G fino a 100MB (prima il limite era di 50MB) e che un’applicazione per iPhone su iPad userà le risorse per retina display e verrà visualizzata a quasi tutto schermo, con un miglioramento sull’usabilità davvero notevole.

 

I video di iOS7 hanno sorpreso tutti quanti per l’interessantissimo effetto di parallasse. Ma come funziona? E come è possibile implementarlo anche su versioni precedenti di iOS?

Potete osservarne il funzionamento in questo video dei ragazzi di iDownloadBlog.com

Partiamo dalle basi, spiegando cos’è un effetto di parallasse. La definizione su wikipedia è: “fenomeno per cui un oggetto sembra spostarsi rispetto allo sfondo se si cambia il punto di osservazione”. Nulla da aggiungere, lo sperimentiamo ogni giorno con i nostri occhi. Il nostro cervello per dare aspetto di tridimensionalità a quello che guardiamo grazie alla visione binoculare riesce a comporre immagini che fanno sembrare gli oggetti più vicini più grandi di quelli lontani, fornendo quindi una certa profondità all’immagine composta.

Un esempio lampante è quando si viaggia in treno, lo sfondo sembra composto da più strati che si muovono a velocità diverse.

Su iOS questo effetto ovviamente ottenuto tramite una simulazione con l’aiuto del giroscopio. Credo che ben pochi non siano al corrente che sui dispositivi Apple (da iPhone 4 in su e iPad 2 in su) sia installato un giroscopio oltre al classico accelerometro. Con l’accelerometro si familiarizza più facilmente: è un dispositivo che misura le accelerazioni sui 3 assi cartesiani. Il giroscopio, invece, misura accelerazioni angolari. È un dispositivo molto interessante che consente in alcune applicazioni di fornire un’esplorazione panoramica di una foto (o universo di un gioco) ruotando sul nostro asse. Si tratta dello stesso dispositivo che ci consente di creare le foto panoramiche.

È un tipo di tecnologia proveniente dall’aeronautica e dai lanci spaziali… davvero interessante!

Salvo smentite, sugli ultimi dispositivi, il chip MEMS (se non sapete cos’è un MEMS leggete qua, non posso dirvi tutto io) del giroscopio è fabbricato da una certa STMicroelectronics azienda italo-francese, ma con origini italiane (solo un nome: Adriano Olivetti).

Grazie ai giroscopi è possibile ottenere gli angoli di rotazione del dispositivo. Angoli che hanno il nome di imbardata, beccheggio e rollio, nomi che derivano dalla navigazione marina e aeronautica.

Giroscopes, courtesy Apple Inc.

Giroscopio

 

Su iOS gli accelerometri sia assiali che angolari sono gestiti da un framework chiamato CoreMotion. Esistono tre classi all’interno di questo framework che consentono l’ottenimento dei dati:

  1. CMAccelerometerData: fornisce le accelerazioni assiali
  2. CMGyroData: fornisce le accelerazioni angolari
  3. CMDeviceMotion: incapsula le misurazioni combinate come l’”assetto” del dispositivo

Istanze di CMMotionManager danno l’accesso a questo tipo di informazioni. I dati possono essere ottenuti in due modi:

  •  push: specificando un intervallo di tempo si fornisce un block per gestire i dati. Ad ogni intervallo il block verrà inserito all’interno di una specifica coda di esecuzione in attesa di essere processato
  • pull: è un tipo di richiesta effettuato direttamente dall’app, che in determinati momenti richiede lo stato del dispositivo

Ottenere le informazioni dagli accelerometri è un’operazione che richiede diversa energia e che quindi tende a consumare molta batteria, per questo motivo generalmente è meglio richiedere le informazioni in modalità pull.

CMDeviceMotion è la classe che ci interessa, perché non ci fornisce il dato diretto dagli accelerometri, ma lo rielabora in modo, ad esempio, da eliminare l’accelerazione gravitazionale. Une delle sue proprietà è l’attitude, cioè l’asseto del dispositivo. La simulazione del parallasse funziona leggendo la posizione del dispositivo e modificando la posizione di quello che stiamo guardando sullo schermo, in pratica se prima eravamo noi a muoverci adesso è ciò che viene visualizzato che si muove.

Questa è solo una prima parte. Per dare un effetto di tridimensionalità non è sufficiente che l’immagine si muova, è necessario anche fornirle una prospettiva. In questo ci viene in aiuto il CATransformLayer e la sua proprietà -sublayerTransform.

CATransformLayer è una classe particolare che non appiattisce le trasformazioni all’interno della gerarchia dei sub-layer, ma riesce a mantenerne la tridimensionalità. -sublayerTransform è invece una proprietà di tutti i CALayer che consente di applicare una trasformazione a tutti i sublayer di quel layer specifico. Preso coscienza della tridimensionalità, esiste ancora una proprietà che fa cambiare prospettiva a tutto quello che vediamo e per farlo bisogna per forza applicarlo alla -sublayerTransform property, in modo che venga propagato lungo tutta la gerarchia di sublayer. In particolare ci interessa toccare un parametro della matrice di trasformazione: il valore m34 (non è un gruppo musicale, quelli sono gli m83 e hanno fatto la colonna sonora di Oblivion). Il valore di m34 rappresenta il punto di fuga applicato alla prospettiva dei layer ed è proprio quello che fornisce realismo a questo effetto.

Per una applicazione che stiamo sviluppando qui a Cloud In Touch, ho deciso di creare un effetto simile a quello di iOS7. Dopo aver navigato sul web, sono venuto a conoscenza del progetto di Arnaud Coomans chiamato Parallax. Si trattava di un ottimo punto di partenza che, purtroppo, non si adattava al porting su un’app da rilasciare in AppStore. Sembrava perfetto… ma solo in apparenza: un bug abbastanza fastidioso portava l’immagine ad impazzire quando l’utente provava a tenere il dispositivo dritto, in modalità portrait.

fuuuuuuuu

FUUUUUUUUUUUUU

Partendo da quel progetto in sé ben studiato (cosa rara), ho deciso di trovare un fix. Ammetto che per un certo tempo la mia mente ha vagato nel vuoto, poi i miei mille anni di ingegneria mi hanno ricordato un concetto di aeronautica che aveva attirato particolarmente la mia attenzione: il cosiddetto GIMBAL LOCK (blocco cardanico) e i QUATERNIONI.

QUATER CHE???????

Cosa è il Gimbal lock e perché è così importante? In pratica è :

perdita di un grado di libertà in uno spazio tridimensionale che avviene quando due, dei tre giunti cardanici, si trovano una configurazione parallela, bloccando il sistema in una rotazione su due dimensioni

Questa immagine con un aereo può fornire un aiuto per comprendere meglio la situazione.

Gimbal lock, courtesy wikipedia

Gimbal lock

Il Gimbal lock è una cosa seria che ha quasi compromesso la missione Apollo 11 (quella dell’allunaggio). Inoltre la questione è ben nota anche a chi ha visto il film nel film Apollo 13.

Non devo allunare, ma solo prendere dei valori corretti da sensori. E in questo caso vengono in aiuto due entità matematiche: le matrici di trasformazione o i quaternioni. Bisogna abbandonare gli angoli di Eulero (imbardata, beccheggio e rollio) per addentrarsi in mondo un po’ più complicato.

Descrivere un quaternione è una cosa complessa vi basti sapere che è un’estensione dei numeri immaginari, trattasi di spazio vettoriale a dimensione 4. Per saperne di più, ecco un bel link a wikipedia (per me rimane un mistero).

Utilizzando questi valori o la matrice di rotazione è possibile evitare quel fastidioso effetto di “agitazione” dell’immagine.

Una volta rilasciata l’app verrà rilasciata anche la classe che crea questo effetto, nel frattempo godetevi il video!! SII PUO’ FARE!!!!

Fonti:

Wikipedia

http://www.macworld.com/article/2042808/inside-the-technology-behind-ios-7s-parallax-effect.html