使用fscopyObjectAsync显示文件复制进度
-
05-10-2019 - |
题
在大量搜索之后,似乎在尝试执行文件副本并显示相对于已复制的文件数量的进度指标时似乎存在一个常见的问题。在花了很多时间试图解决这个问题之后,我再次发现自己再次受到堆叠众神的摆布:-)-希望有一天我也会成为可以帮助新秀的人!
我正在尝试获得一个进度栏,以显示复制过程的状态,一旦复制过程完成,请致电可可方法。挑战 - 我需要使用文件管理器碳调用,因为nsfilemanager并不能为我提供所需的全部能力。
我首先尝试在Matt Long网站上使用代码 可可是我的女朋友. 。代码使我有一些距离。我设法使文件复制进度正常。栏更新和(在Apple文档中进行了一些其他搜索),我发现了如何判断文件复制过程是否已完成...
if (stage == kFSOperationStageComplete)
但是,我的最后一个障碍比我现在的飞跃大一点。我不知道如何将对象引用传递到回调中,也不知道如何从回调中调用可可方法。这是我的碳 - >可可 - >碳理解的极限。博客上的评论之一说
“与其通过静态指针访问进度指示器,不如只使用fsfileoperationClientContext结构的void *信息字段,并传递AppDelegate或Progress指示器本身。”
这主意听起来很不错。不知道该怎么做。为了其他所有人似乎都陷入这个问题并来自非碳背景的所有人,主要基于Matt的示例中的代码,这里是一些简化的代码作为问题的示例...
在正常的可可法中:
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);
}
然后回调(在同一文件中)
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...
}
}
因此,最重要的是我如何:
- 将指针传递到进度条的实例,从调用方法到回调
- 完成后,请回电到普通的可可法
与往常一样,帮助您非常感谢(希望答案将解决我在许多线程中看到的许多问题和投诉!!)
解决方案
您可以使用最后一个参数来执行此操作 FSCopyObjectAsync()
, ,这是一种类型的结构 FSFileOperationClientContext
. 。该结构的领域之一是 info
, ,这是一个void*参数,您可以基本上可以使用该参数。无论您分配给您传递的结构的领域 FSCopyObjectAsync()
将依次传递给您的回调函数作为最后一个 info
功能参数。 void*可以是任何东西,包括指向对象的指针,因此您可以使用它来传递要处理回调的对象的实例。
设置代码看起来像这样:
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);
然后,在您的回调函数中:
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];
}