Pregunta

Parece después de mucho buscar que no parece ser un problema común cuando se trata de hacer una copia del archivo y mostrar un indicador de progreso en relación con la cantidad del archivo que se ha copiado. Después de pasar un tiempo considerable tratando de resolver este problema, me encuentro a merced de los dioses StackOverflow una vez más :-) - Esperemos que algún día voy a ser uno de los que pueden ayudar a los novatos también

!

Estoy tratando de obtener una barra de progreso para mostrar el estado de un proceso de copia y una vez que el proceso de copia ha terminado, llamar a un método de cacao. El desafío - necesito hacer uso de archivos llama Administrador de carbono debido a NSFileManager no me da la necesidad de que la capacidad completa .

Empecé tratando de utilizar el código en el sitio de Matt Long El cacao es mi novia . El código me consiguió una buena distancia. Me las arreglé para conseguir el funcionamiento de progreso de la copia de archivos. Las actualizaciones de las barras y (con un poco de búsqueda adicional dentro de documentos de Apple) que encontraron la manera de saber si el proceso de copia de archivos ha terminado ...

if (stage == kFSOperationStageComplete)

Sin embargo, tengo un último obstáculo que es un poco más grande que mi salto en este momento. No sé cómo pasar una referencia de objeto en la devolución de llamada y no sabe cómo llamar a un método de cacao de la devolución de llamada una vez terminado. Se trata de un límite de mi Carbono -> Cacao -> comprensión de carbono. Uno de los comentarios en el blog dijo

"En lugar de acceder al indicador de progreso a través de un puntero estática, sólo puede utilizar el campo * info vacío de la estructura FSFileOperationClientContext, y pasando ya sea la AppDelegate o el propio indicador de progreso."

suena como una gran idea. No está seguro de cómo hacer esto. Por el bien de todos los demás que parece encuentran con este problema y está viniendo de un fondo no son de carbono, basado sobre todo en el código de ejemplo de Matt, aquí hay un código simplificado como un ejemplo del problema ...

En un método de cacao normal:

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);
}

A continuación, la devolución de llamada (en el mismo archivo)

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

} 

Así que la conclusión es ¿cómo puede I:

  1. pasar un puntero a una instancia de una barra de progreso a partir del método de llamar a la devolución de llamada
  2. Una vez completado, la espalda llamada a un método normal de cacao

Y como siempre, ayuda es muy apreciada (y es de esperar que la respuesta va a resolver muchos de los problemas y quejas que he visto en muchos hilos !!)

¿Fue útil?

Solución

Se puede hacer esto utilizando el último parámetro a FSCopyObjectAsync(), que es una estructura de tipo FSFileOperationClientContext. Uno de los campos de esta estructura es info, que es un parámetro void * que se puede utilizar básicamente como mejor le parezca. Lo que se asigna a ese campo de la estructura se pasa en FSCopyObjectAsync() se pasará a su vez a la función de devolución de llamada como el último parámetro de la función info allí. A * vacío puede ser cualquier cosa, incluyendo un puntero a un objeto, por lo que puede usarlo para pasar a la instancia del objeto que desea manejar la devolución de llamada.

El código de configuración se vería así:

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

A continuación, en su función de devolución de llamada:

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];
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top