Domanda

Appare dopo molte ricerche che ci sembra essere un problema comune quando si cerca di fare una copia del file e mostrare un indicatore di avanzamento rispetto alla quantità di file che è stato copiato. Dopo aver trascorso molto tempo cercando di risolvere questo problema, mi ritrovo in balia degli Dei StackOverflow ancora una volta :-) - Speriamo che un giorno sarò tra quelli che possono aiutare i rookies troppo

!

Sto cercando di ottenere una barra di avanzamento per mostrare lo stato di un processo di copia e una volta che il processo di copia è terminato, chiamare un metodo di cacao. La sfida - ho bisogno di fare uso di File manager chiama Carbon perché NSFileManager non mi dà la piena capacità necessità I

.

Ho iniziato cercando di utilizzare il codice sul sito di Matt Long cacao è la mia ragazza . Il codice mi ha fatto una certa distanza buono. Sono riuscito a ottenere il lavoro di copia file di progresso. Gli aggiornamenti a barre e (con qualche ricerca in più all'interno di documenti di Apple) ho scoperto come dire se il processo di copia dei file è terminato ...

if (stage == kFSOperationStageComplete)

Tuttavia, ho un ultimo ostacolo che è un po 'più grande della mia salto in questo momento. Non so come passare un riferimento all'oggetto nel callback e non so come chiamare un metodo Cocoa la richiamata una volta finito. Questo è un limite della mia Carbon -> Cacao -> comprensione Carbon. Uno dei commenti sul blog detto

  

"Invece di accesso l'indicatore di avanzamento tramite un puntatore statico, si può semplicemente utilizzare il vuoto * informazioni campo della struct FSFileOperationClientContext, e passando sia l'AppDelegate o l'indicatore di avanzamento stessa".

suona come una grande idea. Non sono sicuro di come si faccia. Per il bene di tutti gli altri che sembra sbattere contro questo problema e sta venendo da un background non-Carbon, basato principalmente sul codice dall'esempio di Matt, qui è un codice semplificato come un esempio del problema ...

In un metodo di cacao normale:

CFRunLoopRef runLoop = CFRunLoopGetCurrent();
FSFileOperationRef fileOp = FSFileOperationCreate(kCFAllocatorDefault);

OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp, 
                     runLoop, kCFRunLoopDefaultMode);

if (status) {
    NSLog(@"Failed to schedule operation with run loop: %@", status);
    return NO;
}

// Create a filesystem ref structure for the source and destination and 
// populate them with their respective paths from our NSTextFields.

FSRef source;
FSRef destination;

// Used FSPathMakeRefWithOptions instead of FSPathMakeRef which is in the 
// original example because I needed to use the kFSPathMakeRefDefaultOptions
// to deal with file paths to remote folders via a /Volume reference

FSPathMakeRefWithOptions((const UInt8 *)[aSource fileSystemRepresentation],
    kFSPathMakeRefDefaultOptions, 
    &source, 
    NULL);

Boolean isDir = true;

FSPathMakeRefWithOptions((const UInt8 *)[aDestDir fileSystemRepresentation],
    kFSPathMakeRefDefaultOptions, 
    &destination, 
    &isDir);

// Needed to change from the original to use CFStringRef so I could convert
// from an NSString (aDestFile) to a CFStringRef (targetFilename)

CFStringRef targetFilename = (CFStringRef)aDestFile;

// Start the async copy.

status = FSCopyObjectAsync (fileOp,
             &source,
             &destination, // Full path to destination dir
             targetFilename,
             kFSFileOperationDefaultOptions,
             statusCallback,
             1.0,
             NULL);

CFRelease(fileOp);

if (status) {

    NSString * errMsg = [NSString stringWithFormat:@"%@ - %@", 
                           [self class], status];

        NSLog(@"Failed to begin asynchronous object copy: %@", status);
}

Poi la richiamata (nello stesso file)

static void statusCallback (FSFileOperationRef fileOp,
           const FSRef *currentItem,
           FSFileOperationStage stage,
           OSStatus error,
           CFDictionaryRef statusDictionary,
           void *info )
{

    NSLog(@"Callback got called.");

    // If the status dictionary is valid, we can grab the current values to 
    // display status changes, or in our case to update the progress indicator.

    if (statusDictionary)
    {

        CFNumberRef bytesCompleted;

        bytesCompleted = (CFNumberRef) CFDictionaryGetValue(statusDictionary,
                 kFSOperationBytesCompleteKey);

        CGFloat floatBytesCompleted;
        CFNumberGetValue (bytesCompleted, kCFNumberMaxType, 
                              &floatBytesCompleted);

        NSLog(@"Copied %d bytes so far.", 
                              (unsigned long long)floatBytesCompleted);

        // fileProgressIndicator is currently declared as a pointer to a 
        // static progress bar - but this needs to change so that it is a 
        // pointer passed in via the controller. Would like to have a 
        // pointer to an instance of a progress bar

        [fileProgressIndicator setDoubleValue:(double)floatBytesCompleted];
        [fileProgressIndicator displayIfNeeded];
     }

if (stage == kFSOperationStageComplete) {

    NSLog(@"Finished copying the file");

    // Would like to call a Cocoa Method here...
}

} 

Così la linea di fondo è come posso:

  1. passare un puntatore a un'istanza di una barra di avanzamento dal metodo chiamando al callback
  2. Al termine, chiamata di nuovo fuori ad un normale metodo di cacao

E, come sempre, l'aiuto è molto apprezzato (e, si spera la risposta risolverà molti dei problemi e le lamentele che ho visto in molti fili !!)

È stato utile?

Soluzione

È possibile farlo utilizzando l'ultimo parametro a FSCopyObjectAsync(), che è una struttura di tipo FSFileOperationClientContext. Uno dei campi di struct che è info, che è un parametro void * che si può sostanzialmente utilizzare come meglio credi. Qualunque cosa si assegna a quel campo della struct si passa in FSCopyObjectAsync() saranno passati a sua volta per la vostra funzione di callback come ultimo parametro della funzione info lì. A * vuoto può essere qualsiasi cosa, tra cui un puntatore ad un oggetto, in modo da poter utilizzare che per passare l'istanza del vostro oggetto che si desidera gestire la richiamata.

Il codice di installazione sarebbe simile a questa:

FSFileOperationClientContext clientContext = {0}; //zero out the struct to begin with

clientContext.info = myProgressIndicator;
//All the other setup code
status = FSCopyObjectAsync (fileOp,
         &source,
         &destination, // Full path to destination dir
         targetFilename,
         kFSFileOperationDefaultOptions,
         statusCallback,
         1.0,
         &clientContext);

Quindi, nella vostra funzione di callback:

static void statusCallback (FSFileOperationRef fileOp,
       const FSRef *currentItem,
       FSFileOperationStage stage,
       OSStatus error,
       CFDictionaryRef statusDictionary,
       void *info )
{
    NSProgressIndicator* fileProgressIndicator = (NSProgressIndicator*)info;
    [fileProgressIndicator setDoubleValue:(double)floatBytesCompleted];
    [fileProgressIndicator displayIfNeeded];
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top