Effettuare una chiamata da un’app… e tornare nell’app a fine chiamata

Si può effettuare una chiamata partendo da un app in molte maniere: usando un link presente nell’HTML di una UIWebView o usando direttamente l’openURL del singleton [UIApplication sharedApplication]. In entrambi i casi l’url dovrà avere come stringa @"tel://123456789", dove ovviamene 123456789 è il numero da chiamare.

Ma solo usando una UIWebView si può tornare automaticamente nell’app a fine chiamata. Ma come facciamo a tornare nell’app se nella UI della nostra app non è prevista una webView? Semplice: basta nasconderla. Ecco come:

1
2
3
4
5
6
7
8
NSURL *phoneURL= [NSURL URLWithString:@"tel://123456789"];
 
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
webView.hidden = YES;
 
[webView loadRequest:[NSURLRequest requestWithURL:phoneURL]]; 
 
[self.view addSubview:webView];
NSURL *phoneURL= [NSURL URLWithString:@"tel://123456789"];

UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
webView.hidden = YES;

[webView loadRequest:[NSURLRequest requestWithURL:phoneURL]]; 

[self.view addSubview:webView];

L’ideale sarebbe controllare l’esistenza della webView ogni volta che viene richiesta una chiamata da parte dell’utente, poiché in quel caso sarebbe opportuno rimuovere la webView prima di aggiungerla di nuovo alla view principale.

TIP: Convertire una UIImage in NSData

Un semplice tip nel caso voleste convertire una immagine in un paccheto dati NSData:

1
2
UIImage *img = [UIImage imageNamed:@"some.png"];
NSData *dataObj = UIImageJPEGRepresentation(img, 1.0);
UIImage *img = [UIImage imageNamed:@"some.png"];
NSData *dataObj = UIImageJPEGRepresentation(img, 1.0);

(fonte)

Ritagliare una UIImage

Usando le librerie Core Graphics si possono manipolare in vario modo le immagini. Per ritagliare una immagine è sufficiente definire un rettangono CGRect e usare questo codice:

1
2
3
4
CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
 
[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);
CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);

[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);

Sostituendo a UIImageView l’UIImageView corretta. Il comando CGImageRelease(imageRef) serve solo nel caso non si stia usando l’ARC.

Launch Images e splash screen

Realizzare una sorta di splash screen per una app è relativamente semplice: basta creare un viewController e caricarlo a tutto schermo prima che la struttura principale appaia, e lasciarla visibile per un certo numero di secondi (2 o 3 potrebbero bastare). La cosa complicata è fare in modo che l’immagine usata per il lancio dell’applicazione (chiamata Launch Images o Default) sia identica e allineata con l’immagine usata per lo splash screen.

Ottenere questo risultato crea una gradevole continuità fra il caricamento dell’app e lo splash screen, dando l’illusione che si tratti della stessa immagine.

Per spiegare come ottenere questo risultato immaginiamo che l’app non si apra a tutto schermo e che quindi bisogna tenere conto della Status Bar, dell’altezza di 20 punti (40 px per i display Retina).

Quindi, una volta create le immagini di Default (320×480, 640×960 e 640×1136), dobbiamo creare le splash images croppando i 20px (o 40px nel caso di display Retina) in cima all’immagine.

Poi nello xib dello splash screen inseriamo una UIImageView a tutto schermo (non importa che interfaccia simuliamo se lasciamo le impostazioni predefinite dei Constraints).

Da codice, nel file .m, basterà indicare quale versione dell’immagine splash usare, a seconda dell’altezza in punti logici del display.

1
2
3
4
5
6
CGRect screenBounds = [[UIScreen mainScreen] bounds];
    if (screenBounds.size.height == 568) {
        self.splashImageView.image = [UIImage imageNamed:@"splash-568h"];
    }else{
        self.splashImageView.image = [UIImage imageNamed:@"splash"];
    }
CGRect screenBounds = [[UIScreen mainScreen] bounds];
    if (screenBounds.size.height == 568) {
        self.splashImageView.image = [UIImage imageNamed:@"splash-568h"];
    }else{
        self.splashImageView.image = [UIImage imageNamed:@"splash"];
    }

Ricordate che @2x per i display Retina non si scrive mai: è il sistema operativo che, se necessario, cercherà l’immagine a risoluzione doppia.

UITableView: scroll on top

Se volete implementare la capacità di una tabella di scrollare in cima a sé stessa è sufficiente sfruttare il fatto che le UITableView sono figlie delle UIScrollView e quindi ereditano tutti i loro metodi, compreso setContentOffset:animated:

Per usarlo in una tabella è sufficiente decidere quale elemento grafico, se toccare, deve chiamare il metodo (può essere una toolbar o una label, ade esempio). Il codice da usare sarà:

1
[tableview setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
[tableview setContentOffset:CGPointMake(0.0, 0.0) animated:YES];

Dove il punto (0, 0) indica la distanza dalla cima in cui vogliamo portare lo scroll.

Usando questo metodo anziché un metodo della UITableView come scrollToRowAtIndexPath:atScrollPosition:animated: evitiamo di doverci preoccupare di sapere in anticipo quante righe e sezioni esistono nella tabella al momento dello scroll.

Nuovi stili per le UIAlertView

Con iOS 6 sono stati introdotti nuovi stili per le UIAlertView che permettono di non dover modificare a mano il layout della view quando vogliamo inserire dei campi di testo. In passato, infatti, per inserire una password in un alert bisognava crearsi l’UITextView a mano e rilevarne il contenuto al momento della dismissione dell’alert stesso. Così tempo fa spiegai come farlo e come fare in modo che il campo di testo assomigliasse a quelli mostrati nelle app di Apple.

Ora, grazie ad una nuova proprietà introdotta in iOS, possiamo tutti usare questi nuovi stili semplicemente scrivendo:

1
[alert setAlertViewStyle:UIAlertViewStylePlainTextInput];
[alert setAlertViewStyle:UIAlertViewStylePlainTextInput];

ovvero uno qualunque dei seguenti valori:

  • UIAlertViewStylePlainTextInput
  • UIAlertViewStyleSecureTextInput
  • UIAlertViewStyleLoginAndPasswordInput
  • UIAlertViewStyleDefault

Il valore inseriti si recupera nel metodo delegato alertView:clickedButtonAtIndex: usando il codice seguente:

1
UITextField *textField = [alertView textFieldAtIndex:0];
UITextField *textField = [alertView textFieldAtIndex:0];

Importare un UIViewController e il suo xib in un nuovo progetto

Se abbiamo creato e personalizzato un UIViewController, compreso il suo xib, in un progetto, e siamo stati così bravi da voler riutilizzare il tutto, allora possiamo prendere i file .h, .m e .xib e trascinarli nel nuovo progetto.

Unica accortezza sarà quella di selezionare il corretto target nell’inspector dei file .m e .xib nella proprietà “Target Membership”, questo per evitare di ottenere l’errore seguente:

Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Could not load NIB in bundle: ‘NSBundle <percorso app> (loaded)’ with name ‘<nome controller>”

TIP: app compatibile con il display di iPhone 5

A quanto pare non basta creare un file xib con Xcode 4.5.1, usare l’SDK 6.0 e impostare l’Auto Layout su tale xib.

È assolutamente necessario creare una immagine 640×1136 e usarla come default per l’iPhone Retina da 4.0 pollici, altrimenti l’app continuerà imperterrita ad apparire con bande nere sopra e sotto la view principale.

Convertire un App in Universal (iPhone + iPad)

Piuttosto che creare due app distinte, una per iPhone e una per iPad, può essere più comodo per gli utenti poter usare la stessa app di tipo Universal, risparmiando se l’app è a pagamento, e più utile per lo sviluppatore che rende immediatamente visibile la disponibilità per tutti i dispositivi iOS della propria app (grazie al simbolo + di fianco al prezzo).

Per convertire una app già disponibile per una delle due famiglie di prodotti Apple bisogna innanzitutto specificare nel Summary del Target che il parametro Devices non dev’essere più solo iPhone o solo iPad, ma Universal. Poi, nei Build Settings (sempre nel Target), cercate la chiave Target Device Family e convertitela in iPhone/iPad.

Ora il resto delle modifiche va fatto solo a livello di codice e si può farlo in diversi modi. L’importante è che si lanci il corretto UIViewController iniziale (o anche solo il NibName del corretto file xib) usando la struttura di controllo seguente:

1
2
3
4
5
6
7
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        // iphone code
 
    }else{
        //ipad code
 
    }
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        // iphone code

    }else{
        //ipad code

    }

UIActivityIndicator e UINavigationController

Quando si usa un UIActivityIndicator per indicare un caricamento di dati, ad esempio da un server esterno, si può rischiare di non mostrare alcuna rotazione dell’indicatore di attività dal momento che il sistema è molto occupato a caricare i dati.

Inoltre, se ad esempio parliamo di un reloadData di una UITableView con UINavigationController, potrebbe nascere un problema anche quando si preme il tasto Back e dal dettaglio si torna alla tabella principale.

La soluzione è evitare di far girare il metodo startAnimating di UIActivityIndicator nello stesso thread. Questo si può ottenere usando il metodo performSelector con delay 0 secondi:

1
2
3
4
5
6
7
-(void)viewDidAppear:(BOOL)animated{
    
    [super viewDidAppear:animated];
    
    [self.xmlActivityIndicator startAnimating];
    [self performSelector:@selector(reloadXml:) withObject:nil afterDelay:0];
}
-(void)viewDidAppear:(BOOL)animated{
    
    [super viewDidAppear:animated];
    
    [self.xmlActivityIndicator startAnimating];
    [self performSelector:@selector(reloadXml:) withObject:nil afterDelay:0];
}

Inserire questo codice nel metodo viewDidAppear significa garantire che, ogni volta che si entra nella view e che si effettua il pop dell’eventuale dettaglio del navigationController, vengano ricaricati tutti i dati.