Obtenir une description de l’arborescence d’une UIView

Dans la zone de Debug, il suffit d’utiliser recursiveDescription :

po [self.view recursiveDescription]

Et on obtient la description de toutes les UIView de l’arborescence de la vue concernée :

(lldb) po [self.view recursiveDescription]
(id) $3 = 0x0bbc87b0 <UIView: 0xb988f00; frame = (0 0; 320 460); autoresize = RM+BM; layer = <CALayer: 0xb988f60>>
   | <UIImageView: 0xb988f90; frame = (0 -12; 320 460); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0xb988ff0>>
   | <UIButton: 0xb981370; frame = (20 355; 44 55); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0xb981430>>
   |    | <UIImageView: 0xb984800; frame = (0 0; 44 55); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb984860>>
   |    | <UIButtonLabel: 0xb9832e0; frame = (0 0; 0 0); clipsToBounds = YES; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb983380>>
   | <UIButton: 0xb9865a0; frame = (77 355; 50 54); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0xb986660>>
   |    | <UIImageView: 0xb984980; frame = (0 0; 50 54); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb9849e0>>
   |    | <UIButtonLabel: 0xb988440; frame = (0 0; 0 0); clipsToBounds = YES; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb9884e0>>
   | <UIButton: 0xb983ce0; frame = (238 332; 54 78); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0xb983da0>>
   |    | <UIImageView: 0xb97b580; frame = (0 0; 54 78); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb97b5e0>>
   |    | <UIButtonLabel: 0xb985b80; frame = (0 0; 0 0); clipsToBounds = YES; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb985c20>>
   | <UIButton: 0xb97c2a0; frame = (9 245; 109 82); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0xb97c360>>
   |    | <UIImageView: 0xb97f630; frame = (0 0; 109 82); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb97f690>>
   |    | <UIButtonLabel: 0xb97e120; frame = (0 0; 0 0); clipsToBounds = YES; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb97e1c0>>
   | <UIButton: 0xb97eb20; frame = (147 359; 40 50); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0xb97ebe0>>
   |    | <UIImageView: 0xb97f7b0; frame = (0 0; 40 50); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb97f810>>
   |    | <UIButtonLabel: 0xb980950; frame = (0 0; 0 0); clipsToBounds = YES; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb9809f0>>
   | <UIImageView: 0xb989910; frame = (0 0; 320 47); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0xb989970>>
   | <UIImageView: 0xb98b1a0; frame = (0 428; 320 32); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0xb98ab80>>
   | <UIButton: 0xb979710; frame = (9 245; 109 82); hidden = YES; opaque = NO; autoresize = RM+BM; layer = <CALayer: 0xb974d70>>
   |    | <UIImageView: 0xb97cdb0; frame = (0 0; 109 82); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb97ce10>>
   |    | <UIButtonLabel: 0xb97bfa0; frame = (0 0; 0 0); clipsToBounds = YES; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb97c040>>
   | <UIButton: 0xb975950; frame = (100 265; 89 61); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0xb975a60>>
   |    | <UIImageView: 0xb97cf30; frame = (0 0; 89 61); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb97cf90>>
   |    | <UIButtonLabel: 0xb978950; frame = (0 0; 0 0); clipsToBounds = YES; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xb978a40>>

 

 

Personnaliser la UITabBar

Pour définir les images de chaque UITabBarItem, dans leur état sélectionné et déselectionné :

UITabBarItem *item0 = [self.tabBar.items objectAtIndex:0];
UITabBarItem *item1 = [self.tabBar.items objectAtIndex:1];
...

item0.title = @"Disciplinas";
item1.title = @"Notas";
...

[item0 setFinishedSelectedImage:[UIImage imageNamed:@"disciplinas-active"] withFinishedUnselectedImage:[UIImage imageNamed:@"disciplinas-inactive"]];
[item1 setFinishedSelectedImage:[UIImage imageNamed:@"notas-active"] withFinishedUnselectedImage:[UIImage imageNamed:@"notas-inactive"]];
...

Pour changer l’apparence du texte apparaissant sous l’icône, par exemple sa police et sa couleur, on peut utiliser UIAppearance. Attention, cela affectera toutes les UITabBar de l’application :

[[UITabBarItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
                                                           [UIColor colorWithRed:247./255. green:228./255. blue:211./255. alpha:1.], UITextAttributeTextColor,
                                                           [UIFont fontWithName:@"MyFontName" size:11.0], UITextAttributeFont, nil]
                                                 forState:UIControlStateNormal];

[[UITabBarItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
                                                           [UIColor colorWithRed:67./255. green:33./255. blue:5./255. alpha:1.], UITextAttributeTextColor,
                                                           [UIFont fontWithName:@"MyFontName" size:11.0], UITextAttributeFont, nil]
                                                 forState:UIControlStateSelected];

Voilà le résultat obtenu :

 

 

Attention, pour l’état sélectionné il faut bien utiliser UIControlStateSelected. Si vous utilisez UIControlStateHighlighted, l’erreur suivante apparaitra dans la console :

button text attributes only respected for UIControlStateNormal, UIControlStateHighlighted and UIControlStateDisabled. state = 1 is interpreted as UIControlStateHighlighted.

On note qu’un effet de brillance est toujours présent sur le UITabBarItem sélectionné.

Afin de personnaliser cet « indicateur de sélection », on peut utiliser la méthode

[[UITabBar appearance] setSelectionIndicatorImage:indicatorImage];

// En revanche si l'on souhaite se passer de cet indicateur, il n'est pas
// possible d'utiliser nil avec la précédente méthode

[[UITabBar appearance] setSelectionIndicatorImage:nil]; // ne fonctionne pas !

// En revanche, on peut utiliser une image transparente, cela fonctionne !

[[UITabBar appearance] setSelectionIndicatorImage:[UIImage imageNamed:@"transparent.png"]];

Ci-dessous le résultat sans l’effet de surbrillance sur le UITabBarItem sélectionné :

Afficher un clavier sans UITextField

On peut avoir besoin d’afficher le clavier iOS sans pour autant vouloir utiliser un UITextField ou une UITextView.

Pour cela il suffit d’utiliser le protocol UIKeyInput dans l’interface du UIViewController :

MyViewController : UIViewController<UIKeyInput>

Dans l’implémentation du UIViewController, il est désormais possible d’utiliser les méthodes suivantes :

- (BOOL)hasText;
- (void)insertText:(NSString *)text;
- (void)deleteBackward;

Pour afficher le clavier, ne pas oublier d’implémenter :

- (BOOL)canBecomeFirstResponder {
     return YES;
}

Supprimer les balises HTML dans une NSString

Dans un flux RSS, je récupère un article au format HTML et je souhaite l’afficher sans tenir compte des balises HTML. Pour supprimer les balises HTML (tags) d’une chaine de caractère NSString, j’ai intégré la solution dans une « extension » de la classe NSString.

Tout d’abord le fichier NSString-Extensions.h

@interface NSString(Extensions)
- (NSString *)removeHTMLtags;
@end

Puis le fichier NSString-Extensions.m qui contient la méthode removeHTMLtags qui permet de supprimer les balises HTML d’une NSString.

#import "NSString-Extensions.h"

@implementation NSString(HTML)

- (NSString *)removeHTMLtags {

NSString *text = nil;
NSString* html = self;

NSScanner *theScanner = [NSScanner scannerWithString:html];

while ([theScanner isAtEnd] == NO) {

        [theScanner scanUpToString:@"<" intoString:NULL] ;
        [theScanner scanUpToString:@">" intoString:&text] ;

        html = [ html stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"%@>", text] withString:@" "];
    }
    return html;
}
@end

Source : http://rudis.net/content/2009/01/21/flatten-html-content-ie-strip-tags-cocoaobjective-c

URLWithString retourne nil

En cherchant à créer un NSURL à partir d’un fichier local, j’ai utilisé la méthode URLWithString de NSURL comme ceci :

NSURL *baseURL = [NSURL URLWithString:url];

URLWithString retournait nil… Le problème venait du chemin local qui contenait un espace, ce chemin était du type :

/Library/Application Support/iPhone Simulator/4.0.2/Applications/516C..

L’espace entre iPhone et Simulator n’était pas apprécié par URLWithString. Pour contourner le problème, il suffit de supprimer du chemin tous les caractères non-ASCII :

NSURL *baseURL = [NSURL URLWithString:[url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

Source : http://stackoverflow.com/questions/1981390/urlwithstring-returns-nil

Application iPhone rejetée à cause du logo Google Maps

J’ai dernièrement soumis une nouvelle application sur l’App Store. Avant d’être disponible en vente, une application doit passer par l’étape d’Approval c’est-à-dire de validation.

Apple fournit un ensemble de Guidelines à suivre à la lettre si l’on ne souhaite pas voir son application rejetée. Il y a un paquet de règles à suivre, je les ai « globalement » lues mais malheureusement je me suis fait avoir à cause d’une erreur que j’aurais pu éviter.

Je ne critique pas le principe de soumission d’une application, je ne fait que partager mon erreur pour éviter à d’autres de faire la même !

Mon application est passée en Rejected parce que le logo de Google était caché sur cette application où j’utilise Google Maps

J’ai donc reçu l’e-mail suivant de la part d’Apple :

We’ve completed the review of your application, but because the application displays images provided by Google Maps without the corresponding Google branding, as required under section 8.6 of the App Store Review Guidelines <https://developer.apple.com/appstore/resources/approval/guidelines.html>, we cannot post this version to the App Store.

Google requires their logo and branding to be retained on all their map images.  We hope you’ll consider revising and resubmitting your application.

C’est une erreur stupide qui fait perdre du temps puisqu’il faut alors :

  • corriger le problème (= rendre visible le nom de Google).
  • builder le projet pour  obtenir le fichier d’application .app
  • et surtout, l’étape la plus longue, refaire une soumission de l’application : encore quelques journées (voir semaines) de perdues…

A bon entendeur, salut !

Compiler pour iOS 3.1.3 avec Xcode 3.2.3

Je ne suis pas pressé de passer à l’ OS 4 sur mon iPhone 3G… apparemment ça rame pas mal ! En revanche je me suis mis à jour avec la dernière version de Xcode 3.2.3 et l’iphone SDK 4.0.2.

Après avoir installé la version de Xcode 3.2.3, il m’était impossible de compiler en utilisant le SDK 3.1.3, en effet, ce SDK n’apparait plus comme avant dans la liste des SDK disponibles à la compilation.

Quand je compile en utilisant le SDK 4 et que je veux essayer sur mon iPhone possédant l’ iOS 3.1.3, évidemment, ça ne fonctionne pas…  En effet, vu les problèmes liés à l’utilisation de l’OS 4 sur les iPhones comme le 3G, il y a fort à parier que les possesseurs d’iPhone « non 4G » seront bien content de trouver des applis qui ne nécessitent pas l’installation de l’iOS4. Perso, mes applications n’ont pas besoin des fonctionnalités du SDK 4 et des cibles en 3.1.3 me conviennent parfaitement…

Alors comment compiler pour iOS 3.1.3 avec Xcode 3.2.3 ?

En fait, ce n’est pas possible mais on peut compiler avec le SDK 4 et rendre l’appli compatible pour des iPhones ayant par exemple l’OS 3.1.3.

Pour cela, il suffit de compiler pour l’iOS 4 mais en précisant l’iPhone OS 3.1.3 comme Deployment Target. Pour cela, Project > Edit Active Target puis mettez la valeur iPhone OS 3.1.3 pour le setting iPhone OS Deployment Target.

L’application fonctionnera sur des iPhones équipés de l’OS 3.1.3 ou de l’OS 4.

Source  sur stackoverflow.com

Apple annonce le App Store Volume Purchase Program

Avec ce nouveau programme de vente, les écoles et universités peuvent désormais acheter un important volume de vos applications et vous pouvez leur faire un prix de gros afin de rendre l’offre intéressante.

Ce programme va tout d’abord permettre de simplifier l’achat lorsqu’une école souhaite acheter un grand nombre d’applications.

Il sera également possible de faire une réduction du prix de vente jusqu’à 50% et ce pour un achat d’au moins 20 applications. Cette ristourne est applicable à toutes vos applications ou seulement celles de votre choix.

Petit bémol, ce programme n’est actuellement valable que pour les écoles des États-Unis
Ce n’est que mon avis mais on peut imaginer que si ce programme est intéressant pour Apple, il sera probablement étendu aux autres pays.

Problème de connexion à iTunes Connect

Le site  iTunes Connect à l’adresse https://itunesconnect.apple.com fournit un ensemble d’outils qui permettent de gérer vos applications sur l’AppStore. Il permet notamment d’accéder à la rubrique Sales And Trades qui liste les ventes réalisées par toutes vos applications.

Depuis le 7 août, je n’arrivais plus du tout à accéder à la partie Sales And Trades… La connexion à iTunes Connect fonctionnait mais impossible de voir le nombre de ventes en cliquant sur Sales And Trades : le navigateur rame pendant un moment puis annonce un problème de connexion au serveur à l’adresse reportingitc.apple.com.

La solution à ce problème d’accès à la page Transaction Reports sur iTunes Connect est très simple : arrêter de passer via https://itunesconnect.apple.com mais utiliser l’adresse https://itts.apple.com qui arrive directement sur la page Transaction Reports.

StatusBar caché pendant le Splash screen

Pour cacher la Status Bar dans votre application, il suffit d’utiliser la ligne suivante :

[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];

Pour cacher la StatusBar pendant le splash screen, c’est à dire pendant l’apparition de Default.png cette ligne là n’est pas suffisante, il faut également mettre UIStatusBarHidden à YES dans Info.plist.