How to show the progress of copying a large file in iOS?

前端 未结 2 2113
鱼传尺愫
鱼传尺愫 2020-12-03 19:09

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

相关标签:
2条回答
  • 2020-12-03 19:32

    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.

    0 讨论(0)
  • 2020-12-03 19:42

    In high level :

    1. Run your copying process in a seperate thread (T1)
    2. Run another thread (T2) which reads periodically (say every 100ms) the destination file current_size.
    3. Calculate the percentage : current_size / total_size
    4. Update you progress bar ui element
    0 讨论(0)
提交回复
热议问题