Massimo Oliviero

I deliri digitali di un programmatore informatico e aspirante fotografo…

Archive for the ‘Informatica’ Category

luglio 25th, 2011 by Massimo

iOS colorare la NavigationBar More e il Configure di un UITabBarController (color)

Il comportamento di default della classe UITabBarController è quello di aggiungere un TabBarItem More quando ci sono più di 5 item . Il more è uno speciale NavigationBarController  con all’interno una UITableView e l’elenco degli item non visibili nella schermata principale (Fig. 1).

Figura 1 - un UITabBarController con il tasto More.

E’ possibile colorare la NavigationBar agendo direttamente su una proprietà specifica della classe UITabBarController all’interno del nostro AppDelegate.m in questo modo:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   UIColor *c = [UIColor colorWithRed:(102.0/255.0) green:(153.0/255.0) blue:(51.0/255.0) alpha:1];
   UINavigationController *nav = self.tabBarController.moreNavigationController;
   UINavigationBar *bar = nav.navigationBar;
   bar.tintColor = c;
 
   self.window.rootViewController = self.tabBarController;
   [self.window makeKeyAndVisible];
   return YES;
}

A questo punto la NavigationBar risulta correttamente colorata (Fig. 2).

Figura 2 - Il more colorato

E se premiamo il tasto Edit? Beh il risultato la figura 3 non lascia scampo, un’altra NavigationBar con il colore standard!

Figura 3 - Il Configure

Come fare? Con un trucchetto, prima bisogna impostare come UITabBarController delegate la classe AppDelegate in questo modo:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   UIColor *c = [UIColor colorWithRed:(102.0/255.0) green:(153.0/255.0) blue:(51.0/255.0) alpha:1];
   UINavigationController *nav = self.tabBarController.moreNavigationController;
   UINavigationBar *bar = nav.navigationBar;
   bar.tintColor = c;
 
   // impostare il delegate sul tabbarcontroller
   self.tabBarController.delegate = self;
 
   self.window.rootViewController = self.tabBarController;
   [self.window makeKeyAndVisible];
   return YES;
}

e poi creare il metodo willBeginCustomizingViewControllers che verrà richiamato al momento opportuno:

- (void)tabBarController:(UITabBarController *)controller willBeginCustomizingViewControllers:(NSArray *)viewControllers
{
    UIView *editView = [controller.view.subviews objectAtIndex:1];
    UINavigationBar *modalNavBar = [editView.subviews objectAtIndex:0];
    modalNavBar.tintColor = [UIColor colorWithRed:(102.0/255.0) green:(153.0/255.0) blue:(51.0/255.0) alpha:1];
 
    editView.backgroundColor = [UIColor whiteColor];
}

Ed ecco alla fine il risultato voluto da ogni punto di vista.

Figura 4 - Il configure colorato

luglio 22nd, 2011 by Massimo

Xml vs Json (ma anche NSXMLParse vs JSONKit vs SBJson vs YAJL vs TouchJSON)

Dialogare con servizi web che espongono dati in lettura e/o scrittura è diventata la norma. Molto spesso questi servizi si basano sulla ben nota architettura REST con tutti i vantaggi e gli svantaggi del caso.  La questione su cui vorrei concentrarmi è il formato per l’interscambio delle informazioni. L’architettura REST non impone nessun vincolo in merito e con il tempo si sono consolidati 2 formati principali: XML e JSON. XML lo conosciamo bene da anni è l’arcinoto markup language pensato e concepito proprio per l’interscambio di dati e assai diffuso fin dalla sua nascita nel lontano 1998. JSON invece, nonostante sia relativamente giovane (parliamo del 2001/2002 anche se l’RFC è del 2006), ha preso piede dopo che Yahoo prima e Google poi lo hanno utilizzato nei propri servizi web e protocolli. Ora è diventato un formato che va molto di moda soprattutto sul web.

Senza fare una sterile guerra di religione tra i due formati voglio esprimere la mia personale e relativa preferenza per JSON. JSON è un formato per serializzare semplice e decisamente più leggero in termini di dimensione e di elaborazione computazionale. XML al contrario presenta una forma più complessa per la rappresentazione dell’informazione a scapito, chiaramente, delle prestazioni e delle dimensioni. Con questo non voglio dire l’XML non sia la scelta giusta da fare, anzi. Proprio l’espressività di XML può essere necessaria quando l’informazione d’interscambio è complessa e strutturata. JSON d’altro canto è fin troppo semplice e richiede del lavoro aggiuntivo quando si deve poter gestire dati particolarmente complicati e fortemente tipizzati.

La mia scelta per JSON ricade in un ambito ben preciso e delineato: lo sviluppo mobile. I miei primi progetti mobile con iOS utilizzavano pesantemente XML come formato d’interscambio con la parte server del sistema. Come conseguenza di questa scelta vi era un utilizzo, talvolta eccessivo, della classe NSXMLParse fornita dall’SDK. Utilizzare NSXMLParse può essere un vero delirio e quando il fattore tempo è determinante una scelta tecnologica sbagliata può essere fatale.

Negli ultimi due progetti ho deciso di utilizzare pesantemente JSON per la comunicazione tra client e server . In particolare mi sono avvalso del framework opensource SBJson per tutta l’attivita di parsing. Sono rimasto piacevolmente colpito dalla semplicità e dalla potenzialità del framework, ma soprattutto dalla sua velocità. Su un dispositivo con risorse limitate, come l’iphone, è importante ridurre al minimo il consumo dei vari componenti. In questo caso JSON ci viene incontro con la sua semplicità e un parser come SBJson può fare la differenza rispetto al blasonato NSXMLParser e XML.

SBJson non è l’unico framework per iPhone e iPad. Esistono almeno altri 3 framework interessanti e sono JSONKit, TouchJSON e YAJL. Ognuno con le proprie caratteristiche, ognuno con un set di funzionalità apprezzabili. Si ma quale scegliere? Un metro di valutazione potrebbe essere la velocità, fattore importante su un device che fa della rapidità un suo cavallo di battaglia. Per aiutarci nella scelta su GitHub è possibile scaricare il JSON Benchmarks che permette di effettuare dei benchmarks appunto sui i 4 framework citati. Ho fatto alcune prove con il mio iPhone 4 (con iOS 4.3.4) e questi sono i risultati a fronte di 100 iterazioni:

A mio avviso SBJson offre un buon compromesso tra prestazioni e funzionalità e in definitiva è la mia scelta. E poi è semplice da usare. E’ sufficiente passare al parser una variabile NSData (o una NSString) con all’interno i dati in json e lui ti ritorna un NSDictionary o un NSArray a seconda del formato. Niente a confronto della complessità associata ad un NSXMLParser.

Non escludo però di valutare nell’immediato futuro anche il JSONKit che promette prestazioni da urlo.

luglio 22nd, 2011 by Massimo

iOS, dimensionare l’altezza di una UITextView in base al testo contenuto

Niente di più semplice

1
2
3
CGRect frame = textView.frame;
frame.size.height = textView.contentSize.height;
textView.frame = frame;
luglio 21st, 2011 by Massimo

iOS, allineamento verticale di una UILabel

La UILabel possiede una comoda proprietà Alignment che permette l’allineamento orizzontale del testo. Non esiste una proprietà per all’allineamento verticale. Anzi il testo risulterà sempre centrale rispetto all’altezza della label. Come fare allora? Beh possiamo utilizzare un semplice ma alquanto efficace snippet di codice che permette, con un trucchetto, di aggiustare la situazione a runtime:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NSString *testo = @"Testo di prova";
UILabel *myLabel = self.label; // recupero in qualche modo la label
 
// Determina la dimensione reale del testo, calcolata
// in base al font utilizzato nella label e al line break mode.
CGSize labelSize = [testo sizeWithFont:myLabel.font
                    constrainedToSize:myLabel.frame.size
                    lineBreakMode:myLabel.lineBreakMode];
 
// ALLINEAMENTO VERSO l'ALTO
// Ridimensiona l'altezza della label
// in base all'altezza effettiva del testo.
CGRect frame = myLabel.frame;
frame.size.height = labelSize.height;
myLabel.frame = frame;

In questo caso l’allineamento del testo sarà verso l’alto. A questo punto è semplice giocare con i valori del frame per realizzare anche l’allineamento verso il basso. In questo modo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NSString *testo = @"Testo di prova";
UILabel *myLabel = self.label; // recupero in qualche modo la label
 
// Determina la dimensione reale del testo, calcolata
// in base al font utilizzato nella label e al line break mode.
CGSize labelSize = [testo sizeWithFont:myLabel.font
                    constrainedToSize:myLabel.frame.size
                    lineBreakMode:myLabel.lineBreakMode];
 
// ALLINEAMENTO VERSO IL BASSO
CGFloat diff = myLabel.frame.size.height - labelSize.height;
CGRect frame = myLabel.frame;
frame.origin.y = frame.origin.y + diff;
frame.size.height = labelSize.height;
myLabel.frame = frame;
luglio 20th, 2011 by Massimo

iOS, come (ri)pulire la MKMapView da ogni elemento tranne User Location

Piccolo tip per ripulire completamente una MKMapView da tutti gli elementi (annotation + overlays).

1
2
3
4
5
- (void)mapViewCleanup
{
    [self.mapView removeAnnotations:self.mapView.annotations];
    [self.mapView removeOverlays:self.mapView.overlays];
}

dove self.mapView è una property di tipo MKMapView, chiaramente.

Se si vuole ripulire la mappa da tutti i punti tranne che dal pin blu che indica la posizione dell’utente, basta fare così:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)mapViewCleanupWithoutUserLocation
{
    NSInteger num = [self.mapView.annotations count] - 1;
    NSMutableArray *toRemove = [[NSMutableArray alloc]
                             initWithCapacity:num];
 
    for(MKAnnotationView *annotation in self.mapView.annotations) {
        if(![annotation isKindOfClass:[MKUserLocation class]]) {
            [toRemove addObject:annoView];
        }
    }
 
    [self.mapView removeOverlays:self.mapView.overlays];
    [self.mapView removeAnnotations:toRemove];
    [toRemove release];
}

oppure cosi:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)mapViewCleanupWithoutUserLocation
{
    NSInteger num = [self.mapView.annotations count] - 1;
    NSMutableArray *toRemove = [[NSMutableArray alloc]
                             initWithCapacity:num];
 
    for(MKAnnotationView *annotation in self.mapView.annotations) {
        if (annotation != self.mapView.userLocation) {
            [toRemove addObject:annoView];
        }
    }
 
    [self.mapView removeOverlays:self.mapView.overlays];
    [self.mapView removeAnnotations:toRemove];
    [toRemove release];
}
luglio 19th, 2011 by Massimo

iPhone, le dimensioni degli elementi grafici

Quanto è alta la NavigationBar in portrait e in landscape? Che dimensioni ha la status bar? Quando si sviluppa per iPhone conoscere le dimensioni dei vari elementi grafici è essenziale. E’ importante saper sfruttare il massimo dello spazio a disposizione nei limiti e nel rispetto delle regole imposte dall’sdk. In proposito ho trovato molti siti che parlano di questo argomento. Riporto parte delle informazioni raccolte in questo post, in modo da avere un unico contenitore. A fondo post trovate anche i link da cui ho attinto i dati più un bonus pack ;-) .

 

Iniziamo con una bella immagine riassuntiva.

 

In dettaglio le dimensioni di alcuni elementi

Core Elements:

Carrier Status bar – 320×20 – (480×20 in landscape)
UIView – 320×460 – (480×460 in landscape)
UINavigationBar – 320×44 – (480×32 in landscape)
UITabBar – 320×49
UISearchBar – 320×44
UIToolBar – 320×44

Data Input:

UIPickerView – 320×216
UIDatePicker – 320×216
UIKeyboard – 320×216

Buttons:

UISegmentedControl – 320×44
UIButton xx37

Fields:

UITextField – xx37
UISwitch 94×27
UISlider – xx23

Indicators:

UIProgressView -xx9
UIActivityIndicatorView – 37×37
UIPageControl – 38×36

Link

Bonus pack: due PSD con tutti gli elementi grafici preconfezionati + un sito con altri elementi (psd)
luglio 18th, 2011 by Massimo

Falsi memory leak da 16 Bytes di ProofReader in PRRfInit

Oggi mi è capitata una cosa nuova. Come di consueto, al termine di un’importante task di sviluppo su un applicazione, procedo con un’accurata fase di tuning. Questa fase comprende la ricerca e la sistemazione di eventuali memory leak presenti nel codice. Chiaramente lo strumento per definizione in queste occasioni è il potentissimo Instruments fornito con XCode. Ebbene, dopo aver sistemato tutti i problemi trovati il profiler mi segnala ancora uno strano memory leak (ved figura).

In pratica risulta un leak di 16 Bytes effettuato dalla libreria ProofReader. Se però si effettua la stesso test sul device fisico invece che sul simulatore, il leak non compare. Cercando con san Google sembra che questo sia in realtà un cosiddetto falso positivo. Ovvero un leak inesistente in quanto, a volte, il simulatore può produrre risultati non corretti. Nel mio caso, il problema è collegato all’uso della funzione resignFirstResponder della UISearchBar.

Link

luglio 18th, 2011 by Massimo

Dove memorizza i file l’iPhone Simulator?

Un’applicazione iPhone può tranquillamente leggere e scrivere file su un’area riservata. Quando, in fase di sviluppo, si utilizza il simulatore è comodo sapere dove vengono memorizzati questi file sul proprio mac per poter fare le verifiche del caso.

La cartella di base è questa:

/Users/$USER$/Library/Application Support/iPhone Simulator/$SDK$/Applications/$APP$/Documents

Dove:

  1. Per $USER$ s’intende il nome dell’utente loggato
  2. Per $SDK$ s’intende il numero di versione dell’SDK (4.1.2, 4.2, 4.3 etc..)
  3. Per $APP$ s’intende il guid identificativo dell’applicazione installata sul simulatore

 

luglio 17th, 2011 by Massimo

Stanare l’errore EXC_BAD_ACCESS con NSZombieEnabled

EXC_BAD_ACCESS è uno dei peggior incubi per un programmatore Objective-c. L’errore viene scatenato, nella maggior parte dei casi, quando si tenta di accedere ad una variabile precedentemente rilasciata (over-release).

Quando si utilizza NSZombieEnable il Fondation runtime trasforma gli oggetti deallocati in _NSZombie. Questo risulta comodo in fase di debug quando si vuole capire se il nostro codice  cerca di accedere ad una variable zombie. Ed ecco stanata una delle possibili cause di EXC_BAD_ACCESS.

Per abilitare questa utilissima funzione in XCode 4 è sufficiente andare nel menù Product >Edit Scheme…

Nella tabella Environment Variables aggiungere una nuova riga con name NSZombieEnable e value YES.

Attenzione però questa funzionalità presenta uno sgradevole effetto collaterale: un uso eccessivo della memoria in quanto nessuno degli oggetti marcati com zombie verranno veramente deallocati. E’ bene disabilitare l’opzione quando non è più necessaria.

luglio 16th, 2011 by Massimo

putpkt: write failed, broken pipe

Questo è il messaggio che potrebbe apparire in XCode facendo un ad hoc deploy su un iPhone o un iPad. Tecnicamente il motivo sembra essere dovuto ad un bug nel sistema di deploy. Ho notato che succede quasi sempre quando sono fermo su un breakpoint e poi premo il tasto home sul device. Se riavvio l’applicazione in debug sul device dopo qualche secondo di stallo esce l’errore. Il problema è che poi lo fa sempre anche se chiudi XCode e lo riapri.

La soluzione al problema? Quella che funziona a me è:

  1. Chiudere XCode
  2. Rimuovere l’app dall’iPhone
  3. Spegnere l’iPhone
  4. Riaccende l’iPhone
  5. Riavviare XCode
  6. Run!

Ecco alcuni link in merito: