Stellen Sie die Fortschrittsansicht in der Schleife fest oder Aktualisierung der Fortschrittsansicht

StackOverflow https://stackoverflow.com/questions/19834016

  •  25-07-2022
  •  | 
  •  

Frage

for (int i=0; i<[array count]; i++)
{
    NSError *error;
    NSArray *ipaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *idocumentsDir = [ipaths objectAtIndex:0];
    NSString *idataPath = [idocumentsDir stringByAppendingPathComponent:@"File"];
    NSLog(@"idataPath:%@",idataPath);

    //Create folder here
    if (![[NSFileManager defaultManager] fileExistsAtPath:idataPath])
    {
        [[NSFileManager defaultManager] createDirectoryAtPath:idataPath withIntermediateDirectories:NO attributes:nil error:&error];
    }
    // Image Download here
    NSString *fileName = [idataPath stringByAppendingFormat:@".jpg"];
    NSLog(@"imagePathDOWNLOAD:%@",fileName);

    _imgData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[array objectAtIndex:i]]];
    [_imgData writeToFile:fileName atomically:YES];

    tempImg.image = [UIImage imageWithData:_imgData];   
}

So setzen Sie die Fortschrittsansicht für diese Schleife, ich möchte die Fortschrittsansicht zum Herunterladen von Daten festlegen. Neben Progress -Label (dh %) möchte ich prozentuale Dezimalstellen.

War es hilfreich?

Lösung

Die einfache Lösung besteht darin, dies asynchron zu tun, um die Fortschrittsansicht zu aktualisieren, während Sie gehen:

  1. Erstellen Sie die Fortschrittsansicht und fügen Sie sie Ihrer Ansicht hinzu

  2. Versenden Sie Ihren Code in eine Hintergrundwarteschlange

  3. Versenden Sie nach Abschluss jedes Downloads die Aktualisierung der Fortschrittsansicht zurück in die Hauptwarteschlange

Im Pseudocode würde das so aussehen

UIProgressView *progressView = [[UIProgressView alloc] init];
// configure the progress view and add it to your UI

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    for (int i=0; i<[array count]; i++)
    {
        NSError *error;
        NSArray *ipaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *idocumentsDir = [ipaths objectAtIndex:0];
        NSString *idataPath = [idocumentsDir stringByAppendingPathComponent:@"File"];
        NSLog(@"idataPath:%@",idataPath);

        //Create folder here
        if (![[NSFileManager defaultManager] fileExistsAtPath:idataPath])
        {
            [[NSFileManager defaultManager] createDirectoryAtPath:idataPath withIntermediateDirectories:NO attributes:nil error:&error];
        }
        // Image Download here
        NSString *fileName = [idataPath stringByAppendingFormat:@".jpg"];
        NSLog(@"imagePathDOWNLOAD:%@",fileName);

        _imgData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[array objectAtIndex:i]]];
        [_imgData writeToFile:fileName atomically:YES];

        // now dispatch any UI updates back to the main queue
        dispatch_async(dispatch_get_main_queue(), ^{

            [progressView setProgress: (CGFloat) (i + 1.0) / [array count] animated:YES];
            tempImg.image = [UIImage imageWithData:_imgData];
        });
    }
});

Es gibt auch eine ganze Reihe immer eleganterer Ansätze:

  1. Verwenden Sie die gleichzeitige Warteschlange (anstatt die oben genannten, die die Bilder seriell herunterladen), um die Bilder herunterzuladen, was erheblich schneller ist. Ich könnte die Operation Queue mit vorschlagen maxConcurrentCount von 5, um Parallelität zu genießen, aber stellen Sie sicher, dass Sie die iOS -Grenze in der Anzahl der gleichzeitigen Anfragen nicht überschreiten.

  2. Verwenden NSURLConnectionDataDelegate basierter Download statt der NSData Methode initWithContentsOfURL, die während der individuellen Downloads Zwischenfortschritte bieten können. Sehen Download-Manager oder Operation herunterladen zum Beispiel.

  3. Verwenden AFNetworking Dies liefert auch Download-Block-basierte Schnittstelle.


Oben, in Punkt 1, schlug ich vor, dass Sie in Betracht ziehen, eine gleichzeitige Warteschlange zu verwenden, also habe ich mich entschlossen, sie zu bewerten. Für mich war diese GCD-Implementierung unten 3-4-mal langsamer als die NSOperationQueue Implementierung folgt.

Hier ist die GCD -Implementierung:

CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();

UIProgressView *progressView = [self addProgressView];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    NSInteger downloadSuccessCount = 0;
    NSInteger downloadFailureCount = 0;

    NSString *idataPath = [self createDownloadPath];

    for (int i = 0; i < [array count]; i++)
    {
        // Image Download here
        NSString *filename = [self pathForItem:i array:array folder:idataPath];
        NSURL *url = [self urlForItem:i array:array];
        NSData *data = [[NSData alloc] initWithContentsOfURL:url];
        UIImage *image = nil;
        if (data)
            image = [UIImage imageWithData:data];
        if (image) {
            downloadSuccessCount++;
            [data writeToFile:filename atomically:YES];
        } else {
            downloadFailureCount++;
        }

        // now dispatch any UI updates back to the main queue
        dispatch_async(dispatch_get_main_queue(), ^{

            [progressView setProgress: (CGFloat) (downloadSuccessCount + downloadFailureCount) / [array count] animated:YES];

            // update the image in the UI if you want

            [UIView transitionWithView:self.imageView duration:0.25 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
                tempImg.image = image;
            } completion:nil];
        });
    }

    NSLog(@"Completed in %.1f seconds", CFAbsoluteTimeGetCurrent() - start);
});

dazu NSOperationQueue Implementierung:

CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();

UIProgressView *progressView = [self addProgressView];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 5;

NSString *idataPath = [self createDownloadPath];
self.downloadSuccessCount = 0;
self.downloadFailureCount = 0;

NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Completed in %.1f seconds", CFAbsoluteTimeGetCurrent() - start);
}];

for (int i = 0; i < [array count]; i++)
{
    NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        // Image Download here
        NSString *filename = [self pathForItem:i array:array folder:idataPath];
        NSURL *url = [self urlForItem:i array:array];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = nil;
        if (data)
            image = [UIImage imageWithData:data];
        if (image)
            [data writeToFile:filename atomically:YES];

        // now dispatch any UI updates back to the main queue
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{

            if (image) {
                self.downloadSuccessCount++;

                // update the image in the UI if you want, though this slows it down

                [UIView transitionWithView:self.imageView duration:0.25 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
                    tempImg.image = image;
                } completion:nil];
            }
            else
                self.downloadFailureCount++;

            [progressView setProgress: (CGFloat) (self.downloadSuccessCount + self.downloadFailureCount) / [array count] animated:YES];
        }];
    }];

    [queue addOperation:operation];
    [completionOperation addDependency:operation];
}

[queue addOperation:completionOperation];

Fazit, wenn Sie verwenden NSOperationQueue (Was nicht nur Parallelität liefert, die Sie auch in einer gleichzeitigen Warteschlange tun können, sondern auch die Anzahl der gleichzeitigen Vorgänge (die Sie auf fünf oder weniger für Netzwerkoperationen beschränken sollten) problemlos kontrollieren können), genießen Sie eine bedeutender Leistungsnutzen.

Noch besser wäre, wie ich vorgeschlagen habe, die Einstellung von Afnetworking, bei dem Sie nicht nur diesen Nutzen für die Ereigniswarteschlange genießen, auch bei beiden anderen Vorteilen.

Andere Tipps

[progressView setProgress: (CGFloat) (i + 1.0) / [array count] animated:YES];
self.progressLabel.text = [NSString stringWithFormat:@"%.0f",self.progressView.progress*100];
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top