Blog

iOS & Modal Partial Curl

Il y a quelques jours une personne me demandait comment récupérer une donnée d’une vue modale s’affichant avec une transition « Partial Curl » (soulèvement partiel de la vue parente comme une page d’un livre) dans son UIViewController parent.

Son problème étant que son code fonctionne parfaitement sous iOS 4 mais pas sous iOS 5.

Je regarde son code et là je remarque que tout est bien compliqué pour une chose aussi simple.

En effet, il observe la valeur de sa vue modale et si elle est à nil (l’animation de fermeture de la vue a eu lieu) alors il déclenche un callback sensé transmettre une donnée à sa vue parente, représentée par une propriété de type UIViewController, sachant que la vue modale se ferme elle-même.

Seulement, le changement d’état de la vue modale n’est pas intercepté et donc la valeur n’est pas transmise.

Outre ce soucis, il aurait dû paraître étrange que la vue modale se supprime et qu’elle possède une propriété UIViewController car dans iOS (et bien d’autres langages objets) on prend la bonne habitude d’étendre les classes de base et ce pour permettre la réutilisabilité par exemple.

Dans iOS, un design pattern est très souvent utilisé, c’est le delegate (délégation ou proxy).

Le principe est simple : déléguer à un autre objet des traitements, c’est une inversion de responsabilité.

Pour démontrer que c’est simple, je vais vous indiquer les étapes à suivre sous iOS 5 avec les storyboards :
– créez un nouveau projet de type « Single View Application » dans iOS
– déposez un UIButton dans la vue du contrôleur principal de votre storyboard
– donnez lui un titre
– déposez un nouveau UIViewController dans votre storyboard
– faîtes un segue modal (ça se prononce ségoué) à partir du bouton vers le nouveau UIViewController

(pour rappel : le segue va instancier le nouveau UIViewController et faire la transition comme définie dans le code ou l’inspecteur)

– cliquez sur le segue et donnez lui un identifiant

– cliquez sur le nouveau UIViewController et fixez le style de transition à « Partial Curl » ou bien faîtesle directement sur le segue.
– ajoutez un UIButton par exemple

– créez une nouvelle classe héritant de UIViewController : PageCurlController et ajouter un protocole PageCurlDelegate déclarant une méthode (style dismissWithData:), dans votre déclaration de classe ainsi qu’une propriété de type id répondant à ce protocole afin d’être réutilisable. Vous pouvez l’appeler… delegate ;-)
Ce qui est important : la référence vers cet objet est faible (« weak ») car le contraire n’aurait aucun sens ici, si la référence était forte alors si PageCurlController disparaît le délégué aussi mais comme c’est son parent… tout disparaît ! On ne veut pas cela n’est-ce pas ?

@protocol PageCurlDelegate <NSObject>

-(void)dismissWithData:(NSString *)data;

@end

@interface PageCurlController : UIViewController

@property(nonatomic, weak) id<PageCurlDelegate> delegate;

@end

– n’oubliez pas de la synthétiser :

@synthesize delegate = _delegate;

– retournez dans le storyboard et changer la classe du nouveau UIViewController en PageCurlController (par exemple) dans l’inspecteur d’identité

– reliez le bouton à l’action closePage, c’est ici que l’on fait appel au délégué afin de lui dire que PageCurlController est prêt à disparaître et qu’il lui transmet une donnée.

- (IBAction)closePage:(UIButton *)sender 
{
    //faisons appel au delegate afin de transmettre nos données
    //et de fermer cette vue
    [_delegate dismissWithData:sender.titleLabel.text];
}

– on complète notre contrôleur principal (logiquement le pointeur est donc fort sur page) :

@interface PageTrickViewController : UIViewController<PageCurlDelegate>
    @property(nonatomic, strong) IBOutlet PageCurlController * page;
@end

– et dans l’implémentation (on n’oublie pas de synthétiser) :

@synthesize page = _page;

//méthode déclenchée à chaque fois qu'un segue est déclenché
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    //vérification du segue déclenché
    if([segue.identifier isEqualToString:@"modalCurl"]){
        //ici l'exemple est simple mais il serait judicieux de tester la classe
        //avec isKindOfClass !
        _page = segue.destinationViewController;
        //très important !
        _page.delegate = self;
    }
}

-(void)dismissWithData:(NSString *)data
{
    //ici j'ai juste affiché dans le debug la String transmise
    //mais on peut imaginer la stocker dans une ivar ou l'employer dans une autre méthode
    NSLog(@"%@", data);
    //c'est notre contrôleur principal qui supprime la vue !
    [_page dismissModalViewControllerAnimated:YES];
    //si vous n'avez plus besoin de _page
    _page = nil;  
}

Et voici le résultat en image :

Pour les curieux, voici comment Apple voit la délégation : http://developer.apple.com/library/ios/#documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html

Manuel François

Written by

The author didnt add any Information to his profile yet