Aggiungere una cartella (es. di file HTML) senza perdere la struttura di sottocartelle

Chi ha voluto inserire una cartella di file HTML all’interno di un progetto Xcode si sarà presto accorto che i file vengono trattati come se fossero tutti dentro la cartella principale, e non distribuiti nelle eventuali sottocartelle. Questo crea problemi, ad esempio, nel linkare i file immagine nel file HTML (che andrebbe modificato ad hoc). Ma non solo.

Per ovviare a questo inconveniente è sufficiente ricordarsi di importare la cartella scegliendo l’opzione “Create folder references for any added folders ” (pur con l’opzione Copy items into destination group’s folder (if needed)” attivata).

Resettare lo zoom di una UIScrollView

Resettare lo zoom di una UISCrollView può essere complicato, dal momento che la proprietà scale è relativa e non assoluta. Un metodo per dettare lo zoom quindi non può prescindere dal resettare tutte quelle proprietà che concorrono alla determinazione della scala.

Ecco un metodo interessante (e funzionante):

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)viewDidLoad {
    NSLog(@"VC view did load");
    [super viewDidLoad];
    // ... other work
    originalImagePos = myImageView.center;
}
 
//...
 
-(void)resetImageZoom {
    NSLog(@"Resetting any image zoom");
    CGAffineTransform transform = CGAffineTransformMakeScale(1.0, 1.0);
    myImageView.transform = transform;
    [myScrollView setContentSize:CGSizeZero];
    myImageView.center = originalImagePos;
}
- (void)viewDidLoad {
    NSLog(@"VC view did load");
    [super viewDidLoad];
    // ... other work
    originalImagePos = myImageView.center;
}

//...

-(void)resetImageZoom {
    NSLog(@"Resetting any image zoom");
    CGAffineTransform transform = CGAffineTransformMakeScale(1.0, 1.0);
    myImageView.transform = transform;
    [myScrollView setContentSize:CGSizeZero];
    myImageView.center = originalImagePos;
}

Error: Could not load the “…” image referenced from a nib in the bundle with identifier “…”

Può capitare di aggiungere una immagine, o una qualsiasi altra risorsa (anche file di codice .h e .m) ma ricevere questo errore in fase di compilazione:

1
Could not load the "..." image referenced from a nib in the bundle with identifier "…"
Could not load the "..." image referenced from a nib in the bundle with identifier "…"

Questo accade perché il progetto contiene la risorsa ma non è correttamente dichiarata nella sezione “Copy Bundle Resources” che si trova nel “Build Phases” del Target. Basterà quindi aggiungerla manualmente tramite il piccolo pulsante “+” e da quel momento la fase di compilazione non restituirà più questo errore.

A cosa serve il pulsante Exit nello Storyboard?

Se il nostro Storyboard lancia dei segue consecutivi, diciamo dal viewController A al B e poi al C, allora potremmo avere la necessità di tornare al viewController A (questo indipendentemente dal tipo di navigazione), ovvero eseguire il cosiddetto unwind. Per fare questo dobbiamo prima creare in A il metodo

1
2
3
4
- (IBAction)done:(UIStoryboardSegue *)segue {
   // eseguire qualsiasi cosa, anche niente 
 
}
- (IBAction)done:(UIStoryboardSegue *)segue {
   // eseguire qualsiasi cosa, anche niente 

}

e poi collegare in C un UIButton al pulsante Exit in fondo al viewController stesso: compariranno tutti i metodi validi come il precedente, permettendo, al tocco del button di passare dal viewController C all’A.

Se invece colleghiamo il viewController stesso all’Exit (partendo dall’icona gialla sotto l’interfaccia fino all’icona verde dell’Exit) possiamo creare un unwind richiamabile via codice (dopo aver assegnato un Identifier nell’Attribute Inspector), usando il metodo performSegueWithIdentifier.

 

 

Rotazioni e ancoraggi per animazioni e gesti

Quando si anima un oggetto dell’interfaccia in iOS si possono eseguire una serie molto ampia di movimenti, cambi di colore, dimensioni e quant’altro. Quando parliamo però di rotazioni talvolta può essere utile ridefinire il punto attorno al quale l’oggetto deve eseguire la rotazione.

Infatti, anche se non ci si pensa spesso, una rotazione senza ridefinire altro che l’angolo, avviene attorno al centro dell’oggetto, ma molte rotazioni non sono corrette se avvengono in questa maniera (pensiamo ad una gamba che calcia un pallone, una mazza da golf che colpisce una pallina o la testa di un personaggio che fa cenno di “no”).

Iniziamo con una rotazione eseguita con Quartz:

1
2
3
4
5
6
7
8
9
10
<code>#import <QuartzCore/QuartzCore.h>
 
CABasicAnimation *fullRotation;
fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
fullRotation.fromValue = [NSNumber numberWithFloat:0];
fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
fullRotation.duration = 3.5f;
fullRotation.repeatCount = MAXFLOAT;
 
[view.layer addAnimation:fullRotation forKey:@"360"];   </code>
<code>#import <QuartzCore/QuartzCore.h>

CABasicAnimation *fullRotation;
fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
fullRotation.fromValue = [NSNumber numberWithFloat:0];
fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
fullRotation.duration = 3.5f;
fullRotation.repeatCount = MAXFLOAT;

[view.layer addAnimation:fullRotation forKey:@"360"];   </code>

Mentre, se vogliamo cambiare il punto di ancoraggio dobbiamo cambiare l’anchorPoint e il position del layer (proprietà su cui agisce CoreGraphics:)

1
2
3
4
self.imgView.layer.anchorPoint = CGPointMake(0.0,1.0);
self.imgView.layer.position = CGPointMake(100,200.0);
CGAffineTransform cgaRotateHr = CGAffineTransformMakeRotation(-(3.141/4));
[self.imgView setTransform:cgaRotateHr];
self.imgView.layer.anchorPoint = CGPointMake(0.0,1.0);
self.imgView.layer.position = CGPointMake(100,200.0);
CGAffineTransform cgaRotateHr = CGAffineTransformMakeRotation(-(3.141/4));
[self.imgView setTransform:cgaRotateHr];

Per una chiara spiegazione dei riferimenti che determinano il punto di ancoraggio delle rotazioni, vedere questo articolo.

Riguardo i gesti invece, è spesso necessario eseguire manipolazioni multiple di oggetti.

Innanzitutto un buon esempio per le gesture che possono determinare le manipolazioni comunemente conosciuto come scale, move e rotate si può trovare a questa pagina, mentre qui trovate una spiegazione di come effettuare il reset di rotazione e scala.

Infine un piccolo suggerimento su come eseguire contemporaneamente più trasformazioni su un oggetto:

1
2
3
4
5
6
// Rotate 45 degrees
CGAffineTransform rotate = CGAffineTransformMakeRotation(45*(M_PI/180));
// Move to the left
CGAffineTransform translate = CGAffineTransformMakeTranslation(-50,0);
// Apply them to a view
self.view.transform = CGAffineTransformConcat(translate, rotate);
// Rotate 45 degrees
CGAffineTransform rotate = CGAffineTransformMakeRotation(45*(M_PI/180));
// Move to the left
CGAffineTransform translate = CGAffineTransformMakeTranslation(-50,0);
// Apply them to a view
self.view.transform = CGAffineTransformConcat(translate, rotate);

 

Copia&Incolla su iOS

UIPasteboard è la classe che Apple mette a disposizione degli sviluppatori per usufruire della comoda funzione di copia e incolla via codice. I cosiddetti “appunti” possono essere richiamati da qualunque applicazione ed è questa la loro principale fonte di utilità.

Per fare questo si possono usare una delle seguenti 5 proprietà:

1
2
3
4
NSString *string = pasteboard.string;
UIImage *image = pasteboard.image;
NSURL *url = pasteboard.URL;
UIColor *color = pasteboard.color;
NSString *string = pasteboard.string;
UIImage *image = pasteboard.image;
NSURL *url = pasteboard.URL;
UIColor *color = pasteboard.color;

Per una guida esaustiva, vedere qui.

Sommare due NSNumber

Può sembrare una domanda banale, ma eseguire operazioni su un NSNumber richiede un minimo di attenzione dal momento che stiamo parlandoci oggetti e non di variabili scalari.

Per sommare due NSNumber, infatti, bisogna prima trasformarli in scalari (ad esempio float) e poi usare il risultato per creare un nuovo NSNumber object. Ecco l’esempio:

1
NSNumber *sum = [NSNumber numberWithFloat:([one floatValue] + [two floatValue])];
NSNumber *sum = [NSNumber numberWithFloat:([one floatValue] + [two floatValue])];

Evitare notification duplicate

Talvolta, per semplificare il codice da utilizzare per un’app, è comodo ricorrere alle notifiche dell’NSNotificationCenter. Una volta aggiunto un observer, però, è buona norma evitare che venga aggiunto una seconda volta, duplicando il codice eseguito una volta innescata la notifica.

Per evitare questo problema è sufficiente ricordarsi di rimuovere l’observer prima di aggiungerlo, poiché rimuovere un observer che non esiste non crea nessun tipo di inconveniente:

1
[[NSNotificationCenter defaultCenter] removeObserver:self name:foo object:bar]
[[NSNotificationCenter defaultCenter] removeObserver:self name:foo object:bar]

Uso del blocco @try

In tutte quelle situazioni in cui possono verificarsi potenziali errori, il linguaggio obiettive-c ci mette a disposizione una struttura di controllo che permette di “provare” il codice. Si tratta del blocco @try, seguito da @catch (eseguito solo se viene sollevata una eccezione) e da @finally (eseguito in ogni caso).

1
2
3
4
5
6
7
8
9
10
11
12
NSString* test = [NSString stringWithString:@"ss"];
 
 @try {
    [test characterAtIndex:6];
 
 }
 @catch (NSException * e) {
    NSLog(@"Exception: %@", e);
 }
 @finally {
    NSLog(@"finally");
 }
NSString* test = [NSString stringWithString:@"ss"];

 @try {
    [test characterAtIndex:6];

 }
 @catch (NSException * e) {
    NSLog(@"Exception: %@", e);
 }
 @finally {
    NSLog(@"finally");
 }

Bisogna ricordarsi di disattivare eventuali breakpoint settari su exception perché il programma si bloccherebbe (in pausa) anche se questo codice è pensato proprio per evitare interruzioni.

Generare un numero casuale

La generazione di un numero casuale, utile soprattutto nei giochi, è possibile tramite ad esempio la funzione arc4random(). Ecco come funziona:

Eseguendo il modulo n del valore restituito si determina l’ampiezza n dell’intervallo dei numeri casuali generati. Quindi ad esempio:

1
int x = arc4random() % 100;
int x = arc4random() % 100;

genera un numero casuale compreso fra 0 e 99.

Mentre aggiungere un valore intero determina ovviamente il valore iniziale. Quindi ad esempio:

1
int y = (arc4random() % 501) + 500;
int y = (arc4random() % 501) + 500;

genera un numero casuale compreso fra 500 e 1000.

Se quindi vogliamo ottenere un valore casuale compreso fra min e max possiamo scrivere

1
int z = (arc4random() % (max - min + 1) ) + min;
int z = (arc4random() % (max - min + 1) ) + min;