Ciao a tutti, ho pensato di scrivere questo tutorial semplice semplice per venire incontro a tutti gli utenti che ne hanno fatto richiesta sul nostro forum. In particolare creeremo un’applicazione per iPhone che scuotendo il device, oppure cliccando su un apposito pulsante, mostrerà a video una frase random prelevandola da un file plist. Per rendere il tutto più semplice ho scelto di utilizzare Interface Builder per la realizzazione dell’interfaccia e di utilizzare gli strumenti messi a disposizione dal nuovo XCode4 per dichiarare le IBaction e IBOutlet. Il risultato finale è quello che vedete qui a lato, la foto di sfondo è del grande Escher (click sull’immagine per ingrandire)
Creiamo e prepariamo il nostro nuovo progetto XCode
Iniziamo quindi con il creare un nuovo progetto di tipo “view-based appliation” e chiamiamolo “Oracolo di DevAPP”
Occupiamoci quindi del file che conterrà le frasi. Come ho avuto modo di dire più volte in ogni programma ben fatto è necessario separare il codice dai dati. Questo per certi aspetti complica un pò la prima stesura del programma, infatti molti utenti alle prime armi tendono a fare un pò di commistione tra dati e codice, per esempio un approccio errato a questo problema potrebbe essere:
//r è un numero random
if (r == 0) {
NSLog(@"ecco la prima frase random");
}
else if (r == 1) {
NSLog("ecco la seconda frase random");
}
//etc etc...
Perché sia sbagliato è presto detto: supponiamo che la community inizi a suggerirmi delle frasi da aggiungere all’oracolo…se avessi utilizzato questo approccio sarei costretto a rivedere il programma ogni volta, trovando il punto dove ad r viene assegnato il numero random, aggiungere un ulteriore blocco “else if”…insomma un sacco di roba che probabilmente potrebbe farmi generare qualche errore.
Utilizziamo piuttosto un approccio differente, aggiungiamo al nostro programma un file plist e chiamiamolo “elencofrasi.plist”
Visualizzate ora il file pslit come “source code” facendo un click con il tasto destro sul nome del file, e sostituite il contenuto del file con questo:
Ricordati di fare il release degli oggetti allocati
Ricordati del metodo dealloc
Ricordati di impostare il datasource per le UITableView
Ricordati di impostare il delegate per le UITableView
Ricordati che esiste un solo sito per lo sviluppo iOS: devapp.it
Ricordati che rispondere sul forum è una cortesia, non un obbligo
Ricordati che le richieste HTTP devono essere fatte su un thread separato
Ricordati che se copi il contenuto dei nostri tutorial avrai sette anni di disgrazie
Ricordati di condividere le tue conoscenze
Ricordati di rigranziare chi ha scritto questo tutorial :) :)
La logica del programma
La logica del programma è abbastanza semplice, vogliamo che all’apertura vengano caricate le frasi leggendole da un file plist e successivamente, scuotendo il device oppure cliccando su un pulsante, venga visualizzata una frase random.
Dichiariamo quindi nel file Oracolo_di_DevAPPViewController un NSArray e un intero
NSArray *elencoFrasi;
int fraseCorrente;
Non utilizzerò @property perché non servono in questo momento.
Inizializzo queste variabili all’interno del metodo viewDidLoad, in altri casi potrebbe essere necessario inizializzarle in altri momenti, ma in questo esempio va bene questo metodo.
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"elencoFrasi" ofType:@"plist"];
elencoFrasi = [[NSArray alloc] initWithContentsOfFile:filePath];
srand (time(NULL));
fraseCorrente = -1;
}
Le prime due istruzioni servono ad inizializzare la variabile “elencoFrasi” con il contenuto del file plist, attenzione ho scritto “alloc” quindi ricordiamo di farne il release nel metodo dealloc del viewcontroller.
L’istruzione:
srand (time(NULL));
permette invece di impostare il seme per il generatore di numeri pseudo-random.
Questo argomento necessiterebbe di un intero libro, ma per il momento ci basta sapere che la funzione rand() che utilizzeremo tra breve non genera dei numeri realmente casuali, ma dei numeri definiti pseudo-random, la successione di questi numeri è stabilita da un parametro numerico, detto “seme” e la funzione srand() è la funzione che serve per impostare questo seme, gli passiamo come parametro il numero di secondi intercorsi dalla mezzanotte del 1 gennaio 1970 (l’origine dello standard unix time ) in questo modo siamo ragionevolmente sicuri che ad ogni esecuzione del nostro programma il seme verrà inizializzato ad un valore diverso.
Riconoscere lo shake del device
Per riconoscere l’avvenuto shake del device possiamo, per evitare di analizzare i dati dell’accelerometro, inserire questo codice all’interno del viewcontroller:
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
[self showNextAdvice:nil];
}
}
Spiegare in dettaglio cosa facciano questi metodi ci porterebbe un pò fuori tema, diciamo brevemente che ci assicuriamo che il nostro viewController sia, quando visibile, il FirstResponder al quale inviare il messaggio dell’avvenuto shake. Nell’ultimo metodo richiamo espressamente il metodo “showNextAdvice:nil” per visualizzare una nuova frase.
Attenzione: il metodo “showNextAdvice” ancora non è stato dichiarato, continuate a leggere prima di compilare.
L’interfaccia grafica
Salviamo il tutto e iniziamo ad occuparci dell’interfaccia.
Selzioniamo il file “Oracolo_di_DevAPPViewController.xib” e XCode4 visualizzerà tutti i controlli necessari per disegnare la nostra interfaccia.
Io ho aggiunto:
- Una UIImageView per lo sfondo
- Una UIImageView per ospitare il logo di devAPP
- Una UITextView per ospitare la frase
- Un UIButton per richiedere una nuova frase
La UITextView è stata impostata con uno sfondo trasparente, così da poter visualizzare lo sfondo.
Abbiamo bisogno quindi di un IBOutlet che colleghi la UITextView che abbiamo inserito e una IBAction che colleghi l’UIButton ad un metodo della nostra classe. Utilizziamo quindi le nuove features di XCode 4, visualizziamo l’assistant editor cliccando sul pulsante dedicato:

L’assistant editor visualizzerà il file .h corrispondente all’interfaccia visualizzata, quindi possiamo trascinare (sempre con il tasto dx) dall’interfaccia sul codice sorgente per far apparire il menù che ci permette di creare automaticamente gli IBAction e IBOutlet di cui abbiamo bisogno:


Una piccola nota: l’idea di creare un metodo il cui unico compito è quello di visualizzare una nuova frase non è casuale ma ci permetterà di riciclare il codice scritto all’interno di questo metodo in più parti del programma, in questo esempio lo stesso metodo verrà richiamato sia dal “touch up inside” del pulsante sia con lo shake del device. In alcuni casi ho visto scrivere tutta la logica del programma all’interno del metodo richiamato dall’accelerometro, non è un approccio corretto, perché in quel caso se vogliamo eseguire la stessa azione scatenata però da un evento diverso siamo costretti a fare copia-incolla del codice.
Il metodo showNextAdvice
Adesso il passo finale, inseriamo il contenuto del metodo showNexAdvice.
Il mio codice è questo:
- (IBAction)showNextAdvice:(id)sender {
int r;
int maxIndex = [elencoFrasi count] - 1;
if (fraseCorrente == -1) {
r = rand() % maxIndex;
}
else {
r = rand() % (maxIndex - 1);
if (r == fraseCorrente) {
r = maxIndex;
}
}
fraseCorrente = r;
[textArea setText:[elencoFrasi objectAtIndex:r]];
}
Può sembrare farraginoso, ma vi assicuro che c’è un perché.
Per iniziare dichiaro due variabili, r e maxIndex, la prima conterrà il numero random generato, la seconda invece ci servirà per evitare di generare numeri più grandi dell’ultima posizione dell’array, infatti è inizializzata a:
[elencoFrasi count] - 1;
Eseguo poi un test per verificare se “frasecorrente == -1”, questa condizione può essere verificata solo all’avvio del programma, perché poi ad ogni frase scelta la variabile frasecorrente non potrà mai essere -1.
Quindi visto che è stiamo per visualizzare la nostra prima frase scegliamo tranquillamente un numero random tra 0 ed il massimo indice possibile e visualizziamo la frase nella textview.
E se il test precedente restituisce falso? Allora vuol dire che abbiamo già estratto qualche numero random, e mi sono preso la briga di evitare che un numero possa essere estratto due volte di seguito.
Qualcuno potrebbe dire “basta verificare che sia diverso dal precedente, se non lo è ne rigeneri un altro”…purtroppo non è una risposta corretta perché in linea di principio questo tipo di approccio potrebbe portare ad un loop infinito, adottiamo quindi una soluzione più adatta. Se l’array è composto da n frasi, dobbiamo garantire alle n-1 frasi restanti la stessa probabilità di uscita, quindi dobbiamo estrarre un numero non più da 0 a n-1 ma da 0 a n-2 (non confondiamoci con gli indici per carità!) così facendo, però, avremmo rimesso nel mucchio anche il precedente estratto ed escluso quello in posizione n-1, basta quindi fare un controllo, se ho estratto lo stesso numero di prima allora restituisco quello in posizione n-1.
Se non fossero bastate le mie parole a confondervi le idee eccovi un’immagine che vi chiarirà maggiormente le idee:
A questo punto il programma è terminato, non mi resta che augurarvi buon coding.
Alla prossima
Se avete problemi con il tutorial, questo è il nostro file di progetto (per XCode 4).


29 Responses to “T#093 – Utilizziamo lo shake per ottenere delle frasi casuali”
3 Maggio 2011
paolinofigo! thx!
4 Maggio 2011
raffaeleciao mica gentilmente puoi mettere mettere anche il progetto cosi almeno lo vedo per bene…..grazie
4 Maggio 2011
ittagliaSi, ci fosse il progetto sarebbe ottimo, cmq complimenti
4 Maggio 2011
MatteoPosta il codice sorgente. Ottimo lavoro !!!!
4 Maggio 2011
ignaziocarriva arriva..l’ho già inviato all’amministratore.
4 Maggio 2011
raffaelescusate la mia ignoranza, ma non era meglio impostare elencofrasi in un database sqlite?????
4 Maggio 2011
Albertocome si può fare per usare un sqlite?
4 Maggio 2011
ignaziocUtilizzare un dbsqlite per memorizzare un array da 10 stringhe è quello che si chiama in gergo “overkill”.
Bisognerebbe indagare sul motore di parsing usato da apple per caricare l’array, ma sono più che certo che vista la semplicità (rispetto ad un db) sia più veloce di quanto ci metta sqlite a leggere una tabella.
4 Maggio 2011
Cesaree se io dovessi caricare oltre 300 citazioni, sarebbe appropriato un sqlite?
4 Maggio 2011
Rino PicardiAggiunto il sorgente del progetto 🙂
4 Maggio 2011
Ignazioca costo di apparire impopolare…io se non avessi necessità di più tabelle / filtri e roba simile non passerei a sqlite.
Se anche dovessi scrivere 1000 frasi, probabilmente invece di un plist userei un banale file di testo da leggere con read();
un file di testo con 1000 righe occupa esattamente lo spazio necessario per il testo, neanche un byte di più e non è necessaria nessuna libreria per leggerlo…dubito che sqlite riesca a fare di meglio 🙂
5 Maggio 2011
raffaeleragazzi ho effettuato delle modifiche al programma….. ma quando lo avvio per testarlo sul mio iphone, mi da dei problemi sul certificato:
[BEROR]Code Sign error: The identity ‘iPhone Developer: Ignazio Calo’ (3UCL99DEN4)’ doesn’t match any valid certificate/private key pair in the default keychain
come faccio a reimpostare i certificati? senza riscrivere tutto altrimenti esco pazzo….grazie
5 Maggio 2011
raffaeleo riscritto tutto da capo invece di modificare quello……… vabbe almeno cosi mi funziona eprfettamente….cmq grazie mille
8 Maggio 2011
Cesarema su un file di testo si riescono a distinguere i vari aforismi?
8 Maggio 2011
Cesarecome in un file plist, xml o sql
8 Maggio 2011
IgnaziocIn un file di testo potresti separare i vari aforismi da un carattere “a capo” (\n) oppure da qualsiasi altro separatore, quindi a patto di conoscere come funziona la read() in C potresti leggerlo tranquillamente.
10 Maggio 2011
GuiE se volessi mandare la frase per email??
10 Maggio 2011
ignaziocdirei che questo tutorial potrebbe aiutarti:
http://www.devapp.it/wordpress/t012-inviamo-e-mail-dalle-nostre-applicazioni.html
11 Maggio 2011
GuiOk, peró come faccio a passare la variabile r in un altro metodo??
11 Giugno 2011
LucaHo fatto un array 5 elementi, impostando il modo di generazione random qui messo… ma il 4 elemento non mi esce mai…. possibile??
11 Giugno 2011
Ignaziochai ragione, ho messo un “-1” di troppo.
16 Giugno 2011
LucaCioè?? non ho capito quale correzione al codice va fatta???
Altra cosa.. ma se io esco ed entro nell’applicazione la generazione casuale delle frasi è diversa o è la stessa??, mi spiego… è random limitatamente alla visita dell’applicazione o rispetto a tutte le volte che visito l’applicazione… questo concetto non mi è chiarissimo!!
Grazie
16 Giugno 2011
Ignaziocpuoi correggere scrivendo:
r = rand() % (maxIndex );
per ottenere ad ogni esecuzione una sequenza diversa devi modificare il seme del generatore random, di solito si fa scrivendo:
srand(time(null)); se cerchi su google lo trovi subito.
18 Giugno 2011
Lucaok, grazie mille! Tutto ok!
4 Agosto 2011
GuiC’e un modo per evitare che la stessa frase appaia due volte durante l’intera sessione??
4 Agosto 2011
ignaziocPer far si che nella stessa esecuzione non appaia due volte la stessa frase puoi seguire lo stesso approccio che ho usato io però swappando fisicamente i due valori nell’array (quello trovato e quello in ultima posizione) oppure direttamente usi un mutableArray e togli la frase trovata dall’array.
5 Agosto 2011
AlessandroCiao a tutti!
E se invece di frasi volessi inserire dei suoni in .wav casuali. Sarebbe molto diverse la solfa?
Grazie
Alex
24 Maggio 2012
andreanami pongo la stessa domanda di alessandro… se volessi inserire oltre alla frase anche la registrazione sonora di una voce che la legge??
grazie
25 Dicembre 2012
AlexBellissima guida! Ma perchè come prima scritta nella UITextView mi compare:”
Lorem ipsum…”
?????
Per favore aiutatemi