Xcode 7 : Rafraîchir une vue pendant l'exécution du code d'un bouton

Funzy

Membre enregistré
24 Juin 2016
6
0
26
Bonjour,

Je suis nouveau en objective c et en développement Mac en général, et je conçois un programme de vérification de doublons pour une entreprise.
Mais là n'est pas le problème, ce qui me pose problème est l'évolution de ma vue pendant l'exécution du code, code qui s'exécute lorsque l'on clique sur un bouton "valider". Ce code comporte plusieurs méthodes que j'ai créé pour détecter des fichiers, et doit pouvoir modifier le contenu d'un label pour connaître la progression de la vérification (pour savoir au fur et à mesure combien de fichiers ont été trouvés par exemple).
Cependant, le code "principal", qui est assez long, s'exécute d'un seul coup lors du clic sur le bouton, ce qui fige ma vue le temps de l'exécution (avec la petite roue de couleurs), et ne permet donc pas de faire évoluer celle-ci.

Voici le code de mon bouton dans mon viewController.m :
Bloc de code:
- (IBAction)btValiderClick:(id)sender{
  
    [self Events];
  
}

"Events" étant le "code principal" exécuté.

Si cela peut être utile, voici le code de "Events" :

Bloc de code:
- (void) Events{
   
    _lbprog.stringValue=@"Listing des fichiers...";
    _lbprog.hidden=NO;
   
    NSDate *start = [NSDate date];
   
    int cptfilenew=0;
   
    //Connexion à la BDD
    NSString *BDDpath=[self GetBDDPath];
    sqlite3 *BDD;
   
    // Copie de la BDD
    [self CopieEditableDeLaBDD];
   
    NSLog(@"Copie de la BDD effectuée");
   
    if (sqlite3_open([BDDpath UTF8String], &BDD) == SQLITE_OK) {
       
        sqlite3_stmt *EtatCompil;
       
        const char *rDisk = "SELECT * FROM Disque WHERE id_disque=?";
        if(sqlite3_prepare_v2(BDD, rDisk, -1, &EtatCompil, NULL) == SQLITE_OK) {
            sqlite3_bind_text( EtatCompil, 1,[textPath.stringValue UTF8String], -1, SQLITE_TRANSIENT);
        }
       
        NSLog(@"Identification du disque effectuée");
       
        //Création et initialisation du tableau de fichiers du dossier choisi
        NSMutableArray* listedesFichiers=[[NSMutableArray alloc] init];
        listedesFichiers=Initliste(listedesFichiers, textPath.stringValue, _lbprog);
       
        NSLog(@"Sortie listedesFichiers");
       
        BOOL Exist;
       
        //Si le path de dossier sélectionné n'est pas dans la BDD (donc pas scanné)
        if (sqlite3_step(EtatCompil) != SQLITE_ROW){
           
            Exist=NO;
           
            //Enregistrement du tableau dans la table du dossier dans la BDD
            SaveFilesinDB(listedesFichiers, textPath.stringValue, BDD, EtatCompil, Exist, _lbprog);
           
            sqlite3_finalize(EtatCompil);
           
        }
       
        //Si le path du dossier est dans la BDD (donc déjà scanné)
        else {
           
            Exist=YES;
           
            //Création et initialisation du tableau des fichiers correspondant au dossier choisi, présents dans la BDD + Suppression des fichiers de la BDD qui ne sont plus dans le dossier
            NSMutableArray* listedesFichiersBDD=[[NSMutableArray alloc] init];
            listedesFichiersBDD=InitListeBDD(listedesFichiersBDD, textPath.stringValue, BDD, EtatCompil, _lbprog);
           
            NSString *cptfileBDD=[_lbprog.stringValue stringByReplacingOccurrencesOfString:@"Fichiers listés : " withString:@""];
            NSLog(@"nb files : %@", cptfileBDD);
           
            //Création et initialisation du tableau des fichiers nouveaux (ou modifiés) du disque, et suppression des fichiers supprimés ou modifiés dans la base de données
            NSMutableArray* listedesFichiersNew=[[NSMutableArray alloc] init];
            listedesFichiersNew=InitListeCompareDossierAndBDD(listedesFichiersBDD, listedesFichiers, textPath.stringValue, BDD, EtatCompil, _lbprog, cptfileBDD);
           
            for (Fichier *File in listedesFichiersNew)
            {
                cptfilenew++;
            }
           
            //Ajout des new fichiers dans la BDD
            SaveFilesinDB(listedesFichiersNew, textPath.stringValue, BDD, EtatCompil, Exist, _lbprog);
           
        }
       
        //Vérification des doublons
        NSMutableArray *TabDoublons=[[NSMutableArray alloc]init];
        TabDoublons=CheckDoublons(TabDoublons, textPath.stringValue, BDD, EtatCompil, _lbprog);
        sqlite3_close(BDD);
       
        //Exportation en fichier CSV
        NSMutableString *csv = [NSMutableString stringWithString:@"\n \n Nouveau rapport : \n MD5, Path, Date de creation, Date de derniere modif"];
       
        NSString *md5fichier;
        NSString *pathfichier;
        NSDate *datecrea;
        NSDate *datemodif;
       
        int cptdoublons=0;
       
        NSDate *reference = [NSDate date];
        NSDate *now;
        NSTimeInterval interval;
       
        //Création de chaque ligne dans le tableau CSV
        for (Fichier *fileDoub in TabDoublons)
        {
            md5fichier=fileDoub.GetMD5Fichier;
            pathfichier=fileDoub.GetPathFichier;
            datecrea=fileDoub.GetDateCreaFichier;
            datemodif=fileDoub.GetDateModifFichier;
            cptdoublons+=1;
           
            [csv appendFormat:@"\n\"%@\", %@,\" %@\", %@", md5fichier, pathfichier, datecrea, datemodif];
           
            now = [NSDate date];
            interval = [now timeIntervalSinceDate:reference];
            if (interval >= 1)
            {
                if ([_lbprog.stringValue isEqualToString:@"Creation du fichier CSV."])
                {
                    _lbprog.stringValue=@"Creation du fichier CSV..";
                }
                else if ([_lbprog.stringValue isEqualToString:@"Creation du fichier CSV.."])
                {
                    _lbprog.stringValue=@"Creation du fichier CSV...";
                }
                else
                {
                    _lbprog.stringValue=@"Creation du fichier CSV.";
                }
                reference=now;
                NSLog(@"test interval CSV");
            }
        }
        NSLog(@"oui %@", csv);
       
       
        NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES)objectAtIndex:0];
        NSString *results=[docPath stringByAppendingPathComponent:@"rapport.csv"];
        if (![[NSFileManager defaultManager] fileExistsAtPath:results]) {
            [[NSFileManager defaultManager] createFileAtPath:results contents:nil attributes:nil];
        }
        NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:results];
        [fileHandle seekToEndOfFile];
        [fileHandle writeData:[csv dataUsingEncoding:NSUTF8StringEncoding]];
        [fileHandle closeFile];
       
        NSLog(@"CSV Saved");
       
        NSDate *methodFinish = [NSDate date];
       
        //Gestion de l'interface finale
        NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:start];
        NSInteger hours = executionTime/3600;
        NSInteger minutes = (executionTime - (hours*3600))/60;
        NSInteger seconds = executionTime - (minutes*60);
       
        _lbprog.stringValue=@"Fin du processus";
        _lbtime.stringValue=[NSString stringWithFormat:@"Temps d'execution : %li h %li min %li sec",(long)hours, (long)minutes,(long)seconds];
        _lbtime.hidden=NO;
        if (cptdoublons==0)
            _lbdoublons.stringValue=@"Pas de doublon détecté";
        else
            _lbdoublons.stringValue=[NSString stringWithFormat:@"%i doublons détectés", cptdoublons];
        _lbdoublons.hidden=NO;
       
        if (cptfilenew==0)
            _lbnewfile.stringValue=@"Pas de nouveau fichier";
        else
            _lbnewfile.stringValue=[NSString stringWithFormat:@"%i nouveaux fichiers indexés", cptfilenew];
        _lbnewfile.hidden=NO;
    }
   
}

Et voici le code d'une méthode, comme exemple, du viewController.h :

Bloc de code:
//Remplit le tableau des fichiers du dossier choisi (fichiers des sous-dossiers compris)
NSMutableArray* Initliste(NSMutableArray* lalistedesFichiers,NSString* lepathduDossier, NSTextField* lbprog){
   
    NSLog(@"Entree");
   
    NSDate *reference = [NSDate date];
    NSDate *now;
    NSTimeInterval interval;
   
    //Tableau de tous les chemins d'accès (sous-dossiers compris)
    NSArray *allPaths;
    BOOL isDir=NO;
   
    //Création des variables utilisées
    NSData *datafile;
    NSString *entirePathFile;
   
    //Création du manager de fichier, qui trie les fichiers
    NSFileManager *fileManager = [[NSFileManager alloc] init];
   
    //Remplit allPaths
    NSLog(@"TEst test");
    if ([fileManager fileExistsAtPath:lepathduDossier isDirectory:&isDir] && isDir)
    {
        NSLog(@"Test init in");
        allPaths = [fileManager subpathsAtPath:lepathduDossier];
    }
   
    NSLog(@"Test Init");
    int cpt=0;
   
   
    //Remplit le tableau de fichiers en prenant uniquement les chemins d'accès des fichiers dans allPaths
    for (NSString *pathFile in allPaths)
    {
        //Distinction entre fichiers (avec extension) et dossiers (sans extensions)
        if ([[pathFile pathExtension] isEqualToString: @""]==false)
        {
            //Vérifie si le fichier n'est pas déjà dans le tableau des fichiers
            if ([lalistedesFichiers containsObject:pathFile]==false)
            {
               
                //Récupération du MD5 du fichier
               
                entirePathFile = [NSString stringWithFormat:@"%@/%@", lepathduDossier, pathFile];
                datafile = [NSData dataWithContentsOfFile:entirePathFile];

                unsigned char result[CC_MD5_DIGEST_LENGTH];
                CC_MD5( datafile.bytes, (int)datafile.length, result ); // This is the md5 call
                NSString *md5file = [NSString stringWithFormat:
                        @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                        result[0], result[1], result[2], result[3],
                        result[4], result[5], result[6], result[7],
                        result[8], result[9], result[10], result[11],
                        result[12], result[13], result[14], result[15]
                        ];
               
                //Récupération des dates de création et de dernière modification du fichier
                NSString *pathFile2=pathFile;
                pathFile2 = [lepathduDossier stringByAppendingPathComponent:pathFile];
                NSDictionary* attributesDictionary = [fileManager attributesOfItemAtPath: pathFile2 error:NULL];
                NSDate *datecreafile = [attributesDictionary objectForKey: NSFileCreationDate];
                NSDate *lastdatefile = [attributesDictionary objectForKey: NSFileModificationDate];
               
                //Création et initialisation de l'objet fichier (chemin d'accès, MD5 et date de dernière modification du fichier)
                Fichier *file= [[Fichier alloc]init];
                [file InitFichier:entirePathFile :md5file :datecreafile :lastdatefile];
               
                //Ajout du fichier dans le tableau des fichiers
                [lalistedesFichiers addObject:file];
                cpt=cpt+1;
                NSLog(@"Fichier ajouté num %i", cpt);
            }
            now = [NSDate date];
            interval = [now timeIntervalSinceDate:reference];
            if (interval >= 0.5)
            {
                lbprog.stringValue=[NSString stringWithFormat:@"Fichiers listés : %i", cpt];
                reference=now;
                NSLog(@"test interval Liste");
            }
        }
    }
   
    return lalistedesFichiers;
}

J'ai passé plusieurs heures à chercher des solutions et à les tester, sans succès. Avez-vous une idée de comment régler ce problème ?

Je tiens à préciser que ceci est mon premier post sur ce forum, je ne connais donc pas toutes les fonctionnalités de celui-ci. Veuillez donc m'excuser pour l'imperfection de ce post.

Merci d'avance,
Funzy
 
Dernière édition:
Bonjour,

J'ai quelques connaissances avec le langage swift, mais je pense que la logique est la même : il faut que ton traitement de recherche de doublons se fasse dans un autre thread (ou "queue"). Quand tu veux rafraîchir l'affichage, tu renvoies vers le thread principal ("main queue") qui est le seul à pouvoir gérer l'interface utilisateur.

Si tu laisses tout dans le même thread, le rafraîchissement de l'affichage ne se fait qu'à la fin, ce que tu as constaté.

Cordialement,
Nicolas.
 
J'ai déjà vu un sujet sur ce forum évoquant le multi-threading pour que cela fonctionne, d'ailleurs il me semble que tu en étais l'auteur étonnamment :)
Cependant j'ai testé de différentes manières cette solution, mais mon programme ne se build plus quand je veux le tester. C'est sûrement moi qui m'y prend mal, mais je n'ai aucune idée d'où vient le problème.

EDIT : Voilà le code que j'avais écrit en m'aidant de ce sujet :

Bloc de code:
- (IBAction)btValiderClick:(id)sender
{

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
    {
   
        [self Events];
       
        //for (...)
        //{
           
            dispatch_async(dispatch_get_main_queue(), ^(void)
            {
                [self.view setNeedsDisplay:YES];
            }
           
        //}
    }
}

Xcode ne comprend pas les 2 dernières accolades.
 
Dernière édition:
En objective C, j'aurai du mal à t'aider.
Beaucoup de ressources sur internet et les forums.

Sur iTunes U, regarde les cours de developpement iOs de Paul Hegarty à Stanford (version iOS9 et xcode7, cours "multithreading and text field" du 15 mai 2016). C'est en swift, mais le principe sera identique.
Bon courage

(edit : il faisait des cours avant swift, tu as peut-être le même cours en objective C datant d'il y a quelques années ?)
 
Si tu comptes le nombre de parenthèses et pas que les crochets, tu verras qu'il y a un soucis !

Exemple d'un dispatch_async (premier résultat sur le net, j'avoue) :
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Blablabla
});
 
Wow...merci Larme, j'avoue que je me sens un peu bête sur ce coup, c'est tellement évident quand j'y pense maintenant :)
Tout fonctionne parfaitement maintenant, merci beaucoup :D