Accéder au contenu principal

UIMenuBuilder avec MacCatalyst

Lorsque l'on porte une application IOS sur MacCatalyst, se pose la question du menu.
Ici je voulais ajouter un menu "Open" à mon application, mais comment faire ?

J'ai mis mon code entre "#if TARGET_OS_MACCATALYST" car ce code fonctionne aussi avec IOS el la notion de UIMenuBuilder n'apparait que dans IOS13, or je développe mes application pour IOS9 car beaucoup d'utilisateurs ont des vieux appareils...

#if TARGET_OS_MACCATALYST

-(void)buildMenuWithBuilder:(id)builder {

    NSLog(@"buildMenuWithBuilder !!");
    [super buildMenuWithBuilder:builder];

    if(builder.system == UIMenuSystem.mainSystem) {

        [builder removeMenuForIdentifier:UIMenuFormat]; 
        // Le menu "Format" ne m'interresse pas

        // On crée la commande... 
        //L'action doit être une fonction déclarée dans le FirstResponder
        UIKeyCommand *command_O = [UIKeyCommand 
                 commandWithTitle:@"Import"
                            image:nil                                                       
                           action:NSSelectorFromString(@"importfile:")
                            input:@"O"
                    modifierFlags:UIKeyModifierCommand
                     propertyList:nil];

        // On crée l'entrée du menu à partir de la commande
        UIMenu *menu = [UIMenu 
                    menuWithTitle:@""              // Inutilisé ici
                            image:nil              // Utile que sous IOS
                       identifier:@"UTI unique"    // Un nom unique
                          options:UIMenuOptionsDisplayInline
                         children:@[ command_O ]]; // Une seule commande

        // On insert l'entrée de menu 
        // en première position dans le menu Fichier.
        [builder insertChildMenu:menu atStartOfMenuForIdentifier:UIMenuFile];

    }
}


#endif

Donc la fonction buildMenuWithBuilder: doit être ajouté dans le AppDelegate.m et contenir :
- L'appel à [super BuildMenuWithBuilder:builder]; pour que l'app crée bien le menu.
- Un test pour vérifier que l'on parle bien du menu système, et si ce n'est pas le cas ne rien faire.
- Vous pouvez supprimer des menu que vous ne voulez pas avec [builder removeMenuForIdentifier:], les menus standards ont évidemment leur constante de définit (comme ici UIMenuFormat).
- Pour chaque entrée du menu :
  - On crée le UIKeyCommand qui définit quelle action va être effectuée.
  - On crée le UIMenu avec UIMenuOptionDisplayInline pour une entrée simple (pas de sous menu).
  - On insère le menu au début ou à la fin d'un menu déjà présent.

Voilà, le menu "Open" apparait... Mais petite remarque sur l'action de la UIKeyCommand. Vous aurez peut-être remarqué qu'il n'y a pas de Target et que j'ai utilisé NSSelectorFromString() ) la place de @selector().

En fait l'action doit être faite par le FirstResponder. C'est pourquoi on ne peut pas utilise @selector() qui est une macro du précompilateur. La fonction pointée par action n'existe pas ici dans le AppDelegate, je l'ai mise dans la classe de mon premier ViewController.  Il faut donc utiliser NSSelectorFromString() pour éviter un Warning, car la fonction n'est pas visible par le compilateur.

Ce système est très intéressant car si la fonction n'est pas définie dans le FirstResponder, l'entrée dans le menu est grisée automatiquement. 

Donc cas 1 : 

Imaginons que vous ayez un premier ViewController qui répond à l'action open, le menu apparait actif, même si l'on passe dans un autre ViewController, l'action reste active car le FirstResponder est capable de répondre à tous les sélecteurs définis dans sa classe, mais aussi à tous les sélecteurs définis dans le ViewController parents.

Et cas 2 :

Si nous définissons notre action dans un ViewController qui n'est pas le root, ce n'est que quand notre ViewController sera actif que l'entrée de menu sera active, et elle restera active même si le ViewController présente de nouveaux ViewController. Et évidemment elle redeviendra inactive si l'on quitte le ViewController.




Commentaires

Posts les plus consultés de ce blog

IOS14 et Bonjour qui ne fonctionne pas.

 J'utilise Bonjour dans certaines de mes application pour échanger des données entre appareils se trouvant sur le même réseau local.  Depuis IOS14, vous devez déclarer dans info.plist : <key> NSLocalNetworkUsageDescription </key> <string> L'accès au réseau local est obligatoire pour.... </string> <key> NSBonjourServices </key> <array> <string> _http._tcp </string> </array>  La clef NSLocalNetworkUsageDescription est obligatoire pour toute utilisation du réseau local (en dehors des produits Apple comme AirDrop, AirPrint, etc.). C'est la même chose que l'accès à la caméra ou au micro, vous devez dire pourquoi vous désirez cet accès dans votre application. La clef NSBonjourServices est spécifique pour le protocole Bonjour, elle contient la liste des services que vous partagez avec Bonjour. Dans l'exemple, il s'agit d'un serveur web (http).

UITableView avec multi-sélection sur Mac Catalyst

Si vous mettez à YES les attributs Multi Selection  et Multi Sélection During Editing d'un UITableView, cela ne fonctionnera pas comme attendu sur Mac. C'est juste énervant et voici ubout de code qui règle le problème. #if TARGET_OS_MACCATALYST -( NSIndexPath *) tableView :( UITableView *)tableView willSelectRowAtIndexPath :( NSIndexPath *)indexPath {     if ([tableView. indexPathsForSelectedRows containsObject :indexPath]) {         [tableView deselectRowAtIndexPath :indexPath animated : NO ];         return nil ;     }     return indexPath; } -( NSIndexPath *) tableView :( UITableView *)tableView willDeselectRowAtIndexPath :( NSIndexPath *)indexPath {     if ([tableView. indexPathsForSelectedRows containsObject :indexPath]) {         return nil ;     }     return indexPath; } -( BOOL ) tableView :( UITableView *)tableView shouldHighlightRowAtIndexPath :( NSIndexPath *)indexPath {     for ( NSIndexPath *ip in tableView. indexPathsForSelectedRows ) {         [tabl

Tiret insécable

 Ecrivant un livre sur CoreAnimation en Objective-C et en Swift, j'écris souvent "Objective-C".  Mais je ne souhaite pas que la césure du texte se face avec le C, il me faut donc un tiret insécable. Malheureusement il n'y a pas de tiret insécable sur le clavier du Mac, il faut donc utiliser le caractère Unicode U+2011. Mais le clavier du Mac ne comprends pas non plus par défaut de possibilité d'ajouter un caractère par son code Unicode.  Il faut donc aller dans les réglages d'Apple, dans les réglages du clavier, dans les méthodes de saisie et ajouter le clavier "Universel (Unicode Hex)". Ensuite, lorsque l'on a besoin d'un caractère Unicode, on change de clavier et on utilise la touche Option + le code en hexadécimal. Pour accélérer le changement d'un clavier à l'autre, toujours dans les réglages du clavier, vous pouvez activer les raccourcis clavier. Bon, c'était encore trop long, alors j'ai mis un remplacement automatique da