Accéder au contenu principal

Articles

Affichage des articles du 2020

UISceneSession is only available on iOS 13

Lorsque vous faites une nouvelle application, celle-ci est par défaut pour le dernier IOS.  Mais si vos clients, comme les miens, ont de vieux appareils, vous pourriez vouloir que votre application fonctionne avec IOS9 (pour le moment on peut encore faire des apps IOS9, mais je crois que la limite sera IOS12 dans pas longtemps). Dans les infos de déploiement, vous changez donc le IOS 14  en IOS 9. Mais, le lancement de l'app vous donne un écran noir ! Pour régler le problème, il faut ajouter une propriété "window" dans l'interface du appDelegate. @interface AppDelegate : UIResponder < UIApplicationDelegate > @property ( strong , nonatomic ) UIWindow * window ; @end Reste les warnings... Le plus simple est de faire confiance à Xcode, tapez sur les warnings et un menu vous proposera un "fix" qui ajoutera un API_AVAILABLE à la définition de la fonction. - ( void ) scene :( UIScene *)scene willConnectToSession :( UISceneSession *)session options :(

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).

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

Bug IOS14 avec UITableViewCell contenant des objets interactifs

 J'ai eu la désagréable surprise de voir que lorsque je faisais une mise à jour pour IOS14, mon système de configuration, une UITable pouvant contenir des UISwitch, UIButton, UISlider, etc... ne fonctionne plus. Tous les gadgets interactif à l'intérieur d'une cellule ne sont plus interactif ! Pour que tout revienne à la normal, il faut ajouter un  cell. contentView . userInteractionEnabled = NO ; avant de retourner la cellule dans la fonction  -( UITableViewCell *) tableView :( UITableView *)tableView cellForRowAtIndexPath :( NSIndexPath *)indexPath C'est tout, mais ce fût dur à trouver. C'est même très bizarre d'enlever l'interactivité de la vue conteneur pour mettre l'interactivité aux enfants de ce conteneur. Heureusement, il n'y a pas besoin de faire les mises à jours, le problème ne se pose qu'avec les applications compilées avec le SDK d'iPadOS 14.

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

UIApplication::OpenURL deprecated

La fonction UIApplication::OpenURL est deprecated car Apple désire pouvoir ajouter des options et surtout un handler qui permet de vérifier que l'opération s'est bien déroulée. Comme mes applications sont compilées pour IOS9 (iPad2) cette nouvelle fonction ne peut pas être utilisé car elle est apparue avec IOS 10. Il faut donc mettre un test sur la version d'IOS, mais cela ne suffit pas car il reste le warning deprecated qui devrait disparaitre mais qui ne disparait pas ce qui est pour le moins agaçant et il faut ajouter des #pragma pour enlever le warning.    NSURL *url = [ NSURL URLWithString : "http://monsite.fr" ];    if ( @available (iOS 10.0 , *)) {     [[ UIApplication sharedApplication ]  openURL :url                                         options : @{}                               completionHandler : nil ];   } else { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations"      [[ UIApplication sharedApplic

Anonymisation d'un fichier CSV

 Bon, c'est pas parfait, mais cela fait le job : #!/usr/bin/perl my @lower = ( 'a' .. 'z' ); my @upper = ( 'A' .. 'Z' ); my @digit = ( '0' .. '9' ); my $row0 = <STDIN>; # ignore la première ligne (noms des colones) print $row0; while ( read (STDIN,$b, 1 )) {  if ($b=~/[a-z]/)    { print $lower[ int rand @lower]; } elsif ($b=~/[A-Z]/) { print $upper[ int rand @upper]; } elsif ($b=~/[ 0-9 ]/) { print $digit[ int rand @digit]; } else { print $b; } } Ce script ne touche pas à la première ligne du CSV qui contient normalement les noms des colonnes, puis cela remplace tous les caractères ASCII minuscules pour une minuscule au hasard, idem pour les majuscules et les chiffres. Pour faire un outil pro, il faudrait analyser par cellule et reconnaitre une date, une heure, des coordonnées GPS, etc. Et les remplacer par des données cohérentes.

Sélecteur de fichier pour exporter un fichier avec MacCatalyst

Lorsque l'on gère des fichier sous IOS, on utilise généralement UIDocumentInteractionController pour exporter vers n'importe où le fichier. Avec MacCatalyst cela ne fonctionne pas et il vaut mieux utiliser UIDocumentPickerViewController en mode ExportToService.   NSURL *url = [ NSURL fileURLWithPath :file]; #if TARGET_OS_MACCATALYST  UIDocumentPickerViewController *browser = [[UIDocumentPickerViewController alloc] initWithURL:url inMode:UIDocumentPickerModeExportToService];   assert (browser);  browser. modalPresentationStyle = UIModalPresentationFormSheet ;  browser. directoryURL = [ NSURL   fileURLWithPath : NSHomeDirectory ()];  [ self presentViewController :browser animated : YES completion : nil ]; #else  documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:url];  assert(documentInteractionController);  documentInteractionController.delegate = self ;  CGRect frame = [sender convertRect:CGRectMake( 0 , 0 , sender.fra

UITouches dans un formulaire

Les UIViewController utilisés comme FormSheet modal peuvent être fermé d'un simple glissement du doigt vers le haut. Cette fonctionnalité apparût dans IOS 11 pose problème si vous gérez les déplacement des doigts à l'intérieur du formulaire (je m'en sert par exemple pour mettre une signature). La solution la plus évidente est d'éviter les FormSheet si vous utilisez les déplacements au doigt. L'autre solution est de désactiver la fonctionnalité en désactivant la reconnaissance de mouvement: -( void ) viewDidAppear :( BOOL )animated {     [ super viewDidAppear :animated];      if  ( @available (iOS  11.0 , *)) {        for ( UIGestureRecognizer *gr in self . presentationController . presentedView . gestureRecognizers ) {              if ([gr. name isEqualToString : @"_UISheetInteractionBackgroundDismissRecognizer" ]) {                 // disable la possibilité de quitter la feuille FormSheet ou PageSheet avec un glissement vers le haut            

editActionsForRowAtIndexPath et trailingSwipeActionsConfigurationForRowAtIndexPath

Lorsque l'on passe une application de l'iPad au Mac avec Catalyst, certaines fonctions se retrouvent deprecated. C'est le cas de  editActionsForRowAtIndexPath  qui permet de rajouter des bouton sous la ligne d'une table. La solution préconisée par Apple est d'utiliser  trailingSwipeActionsConfigurationForRowAtIndexPath  mais cette fonction est apparue avec IOS11 et mes applications fonctionne à partie de IOS9. On doit donc mettre les deux fonctions avec des #if TARGET_OS_.... Voici un exemple de fonction qui fonctionne de IOS9 à IOS13 : #if !TARGET_OS_MACCATALYST - (NSArray<UITableViewRowAction *> *) tableView :(UITableView *)tableView                   editActionsForRowAtIndexPath :(NSIndexPath *)indexPath {     UITableViewRowAction *action_reuse =     [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault                                        title: @"Utiliser"                                      handler:^(UITableViewRo

imagePickerController avec Mac Catalyst

Lorsque l'on utilise UIImagePickerController sous IOS, la fonction didFinishPickingMediaWithInfo renvoie l'image originale venant de la caméra ou de la photothèque à travers la variable imageInfo qui est un dictionnaire.      UIImage  *img = imageInfo[ @"UIImagePickerControllerOriginalImage" ]; Cela fonctionne très bien sous IOS, mais avec Mac Catalyst, lorsque l'on récupère une image de la photothèque, celle-ci n'est plus transmise, il n'y a que l'URL qui l'est :      NSURL  *url = imageInfo[ @"UIImagePickerControllerReferenceURL" ]; Mais je n'ai pas trouvé de fonction simple pour récupérer l'image à partir de l'URL. J'ai donc utilisé PHAsset::fetchAssetsWithLocalIdentifiers: mais cette fonction prend en paramètre l'UUID de l'image or cet UUID est en paramètre dans l'URL. Par exemple, voici une URL que peut contenir imageInfo : assets-library://asset/asset.jpeg?id=6D3B81E3-7332-46D7-AAB8-023F28AC4ADD&ext=j

cellForItemAtIndexPath return nil

Dans ma dernière application, comme d'habitude, j'affiche un UIAlertController lorsque l'on tape sur une cellule d'un UICollectionView. J'utilise l'événement didSelectItemAtIndexPath qui fournit l'indexPath de la cellule tapée. Pour afficher le menu, je dois donner la vue à l'origine du menu et sa position. Très bien... j'utilise cellForItemAtIndexPath pour récupérer la cellule... Mais catastrophe la fonction retourne Nil ! Selon la doc Apple, elle ne le devrait pas, cellForItemAtIndexPath retourne Nil si la cellule n'existe pas ou n'est pas visible mais nous venons de la taper. La solution est de faire précéder la demande de cellule par un layoutIfNeeded.         [ collection layoutIfNeeded ];         UICollectionViewCell *cell  = [ collection   cellForItemAtIndexPath :indexPath]; Jusque là, j'avais utilisé cellForItemAtIndexPath sans soucis dans une vingtaine d'application. Je n'ai pas trouvé la raison de