Oggi nessun articolo di programmazione e niente inglese maccaronico.
Vorrei parlare in realtà di una funzionalità a mio avviso molto interessante presente in iOS8.
A volte può essere utile inserire nel vostro telefono informazioni che siano leggibili a tutti in caso di emergenza senza compromettere la privacy di altre, in pratica aprire una piccola finestra in cui far sbirciare piuttosto che aprire la porta di casa.

Read more!

Finally I had enough time to start working on SWIFT, AFSwiftDateExtension is an NSDate extension that makes working with date an easy task.

Read more!

From iOS7 everything now is about blur. In iOS7 it wasn’t easy to blur a view in “real time”, there are a lot of libraries on GitHub that can  (almost) do that.
On iOS8 Apple came to rescue enabling the UIVisualEffectView.

Let’s see how to implement it.

Read more!

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.