40 tutorial per sfondi astratti originali

Può essere utile saper creare una grafica originale per non dover pagare royalties o dover citare gli autori di lavori altrui. Se parliamo di sfondi, allora, questo elenco di 40 tutorial per Photoshop (ma riadattabili facilmente anche per software gratuiti come Gimp) è un ottimo aiuto e fonte di ispirazione. Si tratta di tecniche per realizzare sfondi di tipo astratto.

Una stringa contiene un’altra stringa?

Per controllare se una data stringa ne contiene un’altra, è sufficiente usare il seguente snippet:

1
2
3
4
5
6
7
8
9
10
11
12
13
NSRange textRange;
textRange =[string rangeOfString:substring];
 
if(textRange.location != NSNotFound)
{
 
//contiene substring
 
}else{
 
//non contiene substring
 
}
NSRange textRange;
textRange =[string rangeOfString:substring];

if(textRange.location != NSNotFound)
{

//contiene substring

}else{

//non contiene substring

}

La proprietà location dice anche in che posizione si trova la substring, se questa è stata trovata.

Acquisti in-app: una guida

I cosiddetti in-app purchase sono un ottimo modo per espandere a pagamento le potenzialità di una app altrimenti gratuita. Apple ha creato per questo un framwork StoreKit che gestisce gran parte del processo di vendita, ma senza interfaccia grafica.

Questo significa che da parte dello sviluppatore ci siano parecchie righe di codice da dover scrivere. E, nel caso volessimo un in-app store, un’intera interfaccia grafica da progettare.

In App Purchases: A Full Walkthrough è un ottimo tutorial che spiega ogni singolo passaggio e propone un codice di esempio per un ipotetico passaggio dalla versione free alla pro (proprio di recente Apple ha stabilito che è l’in-app purchase la strada corretta da seguire, anziché due app separate).

La guida è molto dettagliata soprattutto nel caso di Invalid Product IDs, trattato a parte in un articolo/checklist. Da sottolineare la particolarità di questa implementazione per cui, per poter funzionare, si devono aggiungere uno o più prodotti direttamente dalla pagine dell’app in iTunes Connect. Secondo l’autore della guida, bisogna attendere qualche ora affinché i nuovi prodotti siano riconosciuti dal codice come esistenti, ma dal mio test ora sembra essere tutto pressoché immediato.

Nonostante l’intero processo non sia semplicissimo da implementare, si scorge una certa linearità nella progettazione del framework StoreKit.

Usare un NSDictionary dentro gli standardUserDefaults

Gli standardUserDefaults, ottenuti dalla classe NSUserDefault, sono molto utili per memorizzare le preferenze dell’utente di un’app.

In alcuni casi può essere utile espandere la semplice associazione stringa-chiave che viene spesso usata, e usare altri tipi di oggetti, ad esempio un NSDictionary.

Per fare questo bisogna innanzitutto creare il dizionari da inserire negli User Defaults:

1
2
3
4
    NSDictionary *dictionaryOfSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                               [NSNumber numberWithBool:NO], @"SettingOne",
                               [NSNumber numberWithBool:YES], @"SettingTwo",
                               nil];
    NSDictionary *dictionaryOfSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                               [NSNumber numberWithBool:NO], @"SettingOne",
                               [NSNumber numberWithBool:YES], @"SettingTwo",
                               nil];

nell’esempio ho approfittato anche per insere un BOOL, così da mostrare come deve essere trattato per essere inserito in un NSDIctionary.

Questo dizionario andrà poi inserito in quello generale e quindi usato per registrare i defaults:

1
2
3
4
5
6
NSDictionary *userDefault = [NSDictionary dictionaryWithObjectsAndKeys:
                                 @"Any", @"AnotherSetting",
                                 dictionaryOfSettings, @"DictionaryOfSettings",
                                 nil];
 
    [[NSUserDefaults standardUserDefaults] registerDefaults:userDefault];
NSDictionary *userDefault = [NSDictionary dictionaryWithObjectsAndKeys:
                                 @"Any", @"AnotherSetting",
                                 dictionaryOfSettings, @"DictionaryOfSettings",
                                 nil];

    [[NSUserDefaults standardUserDefaults] registerDefaults:userDefault];

Nel momento di prelevare tali valori booleani bisogna usare questo tipo di codice:

1
BOOL value = [[[[NSUserDefaults standardUserDefaults] objectForKey:@"DictionaryOFSettings"] valueForKey:@"SettingOne"] boolValue];
BOOL value = [[[[NSUserDefaults standardUserDefaults] objectForKey:@"DictionaryOFSettings"] valueForKey:@"SettingOne"] boolValue];

Mentre per settare un nuovo valore boleano si può usare questo codice:

1
2
3
4
5
NSNumber *value = [NSNumber numberWithBool:YES];
        NSMutableDictionary *newDictionaryOfSettings = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults] objectForKey:@"DictionaryOfSettings"]];
        [newDictionaryOfSetting setObject:value forKey:@"SettingOne"];
        [[NSUserDefaults standardUserDefaults] setObject:newDictionaryOfSettings forKey:@"DictionaryOfSettings"];
        [[NSUserDefaults standardUserDefaults] synchronize];
NSNumber *value = [NSNumber numberWithBool:YES];
        NSMutableDictionary *newDictionaryOfSettings = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults] objectForKey:@"DictionaryOfSettings"]];
        [newDictionaryOfSetting setObject:value forKey:@"SettingOne"];
        [[NSUserDefaults standardUserDefaults] setObject:newDictionaryOfSettings forKey:@"DictionaryOfSettings"];
        [[NSUserDefaults standardUserDefaults] synchronize];

Abilitare e disabilitare i pulsanti Avanti e Indietro di un UIWiebView

Quando serve di caricare una pagina HTML in una app, può essere utile abilitare i link e quindi necessario poter andare avanti e indietro nella cronologia di navigazione. Questi movimenti fanno parte del corredo di UIWebView e si chiamano goForward e goBack: possono essere collegati ad dei pulsanti dell’interfaccia.

È utile però disabilitare questi pulsanti quando il movimento non è possibile. Ad esempio non è possibile andare indietro quando si è alla prima pagina HTML aperta, come non è possibile andare avanti quando si è sull’ultima. Per gestire tutto questo è necessario che il ViewController che ospita la WebView implementi il protocollo UIWebViewDelegate e che si implementi in particolare il metodo seguente (fonte):

1
2
3
4
5
6
7
8
9
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
  // Enable or disable back
  [myBackButton setEnabled:[myWebView canGoBack]];
 
  // Enable or disable forward
  [myForwardButton setEnabled:[myWebView canGoForward]];
 
 }
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
  // Enable or disable back
  [myBackButton setEnabled:[myWebView canGoBack]];

  // Enable or disable forward
  [myForwardButton setEnabled:[myWebView canGoForward]];

 }

Grazie ai metodi canGoBack e canGoForward, quindi, i pulsanti vengono abilitati solo quando è possibile usarli. L’interfaccia è ora molto più coerente e intuitiva.

Produrre una versione Lite senza duplicare un progetto

Spesso le App a pagamento vengono pubblicate anche in versione gratuita (ed in qualche modo limitata) per fare in modo che gli utenti possano rendersi conto dell’utilità dell’App stessa senza il timore di spendere del denaro. Una volta sincerati della qualità della versione gratuita, gli utenti di solito acquistano volentieri l’App completa.

Nonostante per Apple le App e le “App Lite” (o “Free”) siano tecnicamente due app distinte, è possibile produrre la versione Lite senza duplicare il progetto dell’App completa. Questo garantisce una facilità di allineamento fra le funzioni e i bug fix delle due versioni, ma necessita di alcune accortezze.

Se si definisce una variabile globale di tipo BOOL, ad esempio nell’AppDelegate.h con il comando:

1
#define isLite YES
#define isLite YES

allora in qualunque file di implementazione si può lanciare un comando quando l’App è Lite e un’altro quando è completa, con un semplice if.

Altra accortezza è quella di modificare almeno la grafica dell’icona dell’App, di modo che si distingua dall’App completa.

A livello di profili di provisioning è necessario creare un distinto App ID e una coppia di profili di development e distribution.

Nel progetto, invece, va cambiato l’identifier dell’iOS Application Target (lo trovate sul Summary del Target) di modo che sia identico a quello definito nell’App ID. Poi, nei Build Settings del Project selezionate i nuovi profili nella sezione Code Signing (ovviamente dopo aver scaricato e installato i nuovi profili di provisioning).

A questo punto la solita archiviazione genererà una nuova sezione dedicata alla versione Lite e, durante la validazione e la sottomissione verrà in automatico proposto il corretto profilo di provisioning di distribuzione.

Facile, no?

Accedere agli oggetti in un file Xib

Progettare una interfaccia utente via Xib è certamente molto semplice e riduce il tempi di caricamento dell’app. Ma se ci sono molti oggetti e bisogna accedere via codice alle loro proprietà può essere molto complicato (nonché noioso) creare una @property per ogni oggetto.

Se però teniamo conto che un file Xib è un file XML con una struttura fatta di array e oggetti, allora possiamo accedervi grazie ad un ciclo.

Nell’esempio che riporto immaginiamo un caso complesso: una UIView entro cui c’è una UIScrollView, entro cui c’è un’altra UIView. In quest’ultima UIView sono posizionate UILabel ed altri oggetti. Il codice deve quindi accedere al livello corretto (basta leggere la lista degli oggetti in Interface Builder per sapere che indice dell’array prelevare)  e sfogliare il livello alla ricerca di UILabel, per le quali, ad esempio, modificare il font. Ecco come:

1
2
3
4
5
6
7
8
9
10
11
12
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
 
    NSArray *objects = [[[[[[topLevelObjects objectAtIndex:0] subviews] objectAtIndex:1] subviews] objectAtIndex:0] subviews];
 
    for (id __strong currentObject in objects) {
        if ([currentObject isKindOfClass:[UILabel class]]) {
            UILabel *label = (UILabel *)currentObject;
            float currentSize = [[label font] pointSize];
            [label setFont:[UIFont fontWithName:@"BradleyHandITCTTBold" size:currentSize]];
            currentObject = label;
        }
    }
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];

    NSArray *objects = [[[[[[topLevelObjects objectAtIndex:0] subviews] objectAtIndex:1] subviews] objectAtIndex:0] subviews];

    for (id __strong currentObject in objects) {
        if ([currentObject isKindOfClass:[UILabel class]]) {
            UILabel *label = (UILabel *)currentObject;
            float currentSize = [[label font] pointSize];
            [label setFont:[UIFont fontWithName:@"BradleyHandITCTTBold" size:currentSize]];
            currentObject = label;
        }
    }

settare __strong è necessario dal momento che siamo in regime di ARC. Ricordatevi che per poter prelevare la dimensione attuale delle UILabel, nel caso non siano tutte uguali, queste devono (via Interface Builder) avere un font di sistema, per evitare di non riuscire a determinarne la grandezza.
Se poi, come nell’esempio, bisogna fare delle impostazioni via codice dopo il caricamento dell’interfaccia (come settare il contentSize dell’UIScroll View), ricordatevi di caricare l’interfaccia (ovvero usare il metodo loadNibNamed:) prima di queste impostazioni, altrimenti verrebbero annullate dal successivo caricamento dello XIb. Un buon metodo in cui caricarlo è in ViewDidLoad.

Font personalizzati in iOS 4

Un ultimo bug che affliggeva iOS 4 è quello dei font personalizzati. Ci sono pochi font che non necessitano di essere caricati nel bundle dell’app; anche quelli che vengono visualizzati correttamente in Interface Builder non è detto che in iOS 4 vengano caricati correttamente. Il rischio è quindi quello di vedere una UILabel correttamente formattata in un dispositivo con iOS 5 e invece del tutto invisibile in iOS 4.

La soluzione richiede una serie di passaggi:

  1. procurarsi il font (ad esempio in formato truetype o openfont)
  2. caricarlo nel progetto Xcode
  3. aggiungere alle proprietà delle Info del Target dell’app, una voce “Fonts provided by application” (che è di tipo Array)
  4. nell’array aggiungere un stringa con il nome del file del font (comprensivo di estensione)
  5. Nel codice, settare il font della UILabel con il nome del font. Attenzione: il nome del font non è necessariamente identico al nome del file. Ad esempio il font file “bradley hand tic tt bold.ttf” ha come nome “BradleyHandITCTTBold”. Se non si conosce il nome esatto provare a cercarlo nei log generati da questo codice:
1
2
3
4
5
6
for (NSString *familyName in [UIFont familyNames]) {
        NSLog(@"Font Family: %@", familyName);
        for (NSString *fontName in [UIFont fontNamesForFamilyName:familyName]) {
            NSLog(@"\t%@", fontName);
        }
    }
for (NSString *familyName in [UIFont familyNames]) {
        NSLog(@"Font Family: %@", familyName);
        for (NSString *fontName in [UIFont fontNamesForFamilyName:familyName]) {
            NSLog(@"\t%@", fontName);
        }
    }

[EDIT: in caso di problemi seguire questa procedura http://stackoverflow.com/a/24863291]