I am writing an iOS app. In my app, I want to copy some files from one folder to another. But because some files is too large, it will take a long time to finish the copy. So I want to add a progress bar to show the percentage of the copy. But I find that the file manager has no callback method to get the percentage. Does anyone have the good solution to it?
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
回答1:
In high level :
- Run your copying process in a seperate thread (T1)
- Run another thread (T2) which reads periodically (say every 100ms) the destination file
current_size
. - Calculate the percentage :
current_size / total_size
- Update you progress bar ui element
回答2:
I've created a simple class with @giorashc approach.
If anyone needs something like this feel free to use it.
The .h
#import <UIKit/UIKit.h>
@protocol IDCopyUtilsDelegate;
@interface IDCopyUtils : NSObject
@property (nonatomic, weak) id<IDCopyUtilsDelegate> delegate;
- (void)copyFileAtPath:(NSString *)sourcePath toPath:(NSString *)targetPath;
@end
// 3. Definition of the delegate's interface
@protocol IDCopyUtilsDelegate <NSObject>
- (void)setCopyProgress:(float)progress;
- (void)didFinishedCopyWithError:(NSError *)error;
@end
The .m
#import "IDCopyUtils.h"
@interface IDCopyUtils()
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, strong) NSString *sourcePath;
@property (nonatomic, strong) NSString *targetPath;
@end
@implementation IDCopyUtils
- (void)copyFileAtPath:(NSString *)sourcePath toPath:(NSString *)targetPath
{
self.sourcePath = sourcePath;
self.targetPath = targetPath;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
if ([fileManager fileExistsAtPath:self.targetPath] == YES) {
[fileManager removeItemAtPath:self.targetPath error:&error];
}
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.100
target:self
selector:@selector(checkFileSize)
userInfo:nil
repeats:YES];
[self performSelector:@selector(startCopy) withObject:nil afterDelay:0.5];
}
- (void)checkFileSize
{
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *attributesSource = [[NSFileManager defaultManager] attributesOfItemAtPath:self.sourcePath error:NULL]; unsigned long long fileSize = [attributesSource fileSize];
NSDictionary *attributesTarget = [[NSFileManager defaultManager] attributesOfItemAtPath:self.targetPath error:NULL]; unsigned long long fileSizeTarget = [attributesTarget fileSize];
double progress = (float)fileSizeTarget / (float)fileSize;
if (self.delegate && [self.delegate respondsToSelector:@selector(setCopyProgress:)])
{
[self.delegate setCopyProgress:progress];
}
NSLog(@"Size: %f", progress);
});
}
- (void)startCopy
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
if ([fileManager fileExistsAtPath:self.targetPath] == YES) {
[fileManager removeItemAtPath:self.targetPath error:&error];
}
if ([fileManager fileExistsAtPath:self.targetPath] == NO) {
[fileManager copyItemAtPath:self.sourcePath toPath:self.targetPath error:&error];
[self.timer invalidate];
self.timer = nil;
if (self.delegate && [self.delegate respondsToSelector:@selector(didFinishedCopyWithError:)])
{
[self.delegate didFinishedCopyWithError:error];
}
}
});
}
@end
You can use it like this (for example):
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"iso"];
NSString *targetPath = [documentsDirectory stringByAppendingPathComponent:@"test.iso"];
IDCopyUtils *copyUtils = [[IDCopyUtils alloc] init];
copyUtils.delegate = self;
[copyUtils copyFileAtPath:sourcePath toPath:targetPath];
And you will we able to update you progress view and get notified when the file did finidhed copying using the delegate methods.