3

I hope it's even possible.

I've got code to load really big file (dictionary) after app started:

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    NSString *filePath = [NSString stringWithContentsOfFile:[[NSBundle mainBundle]
        pathForResource:@"en_US" ofType:@"txt"]
        encoding:NSUTF8StringEncoding error:nil];
    dictionary = [NSSet setWithArray:[NSArray arrayWithArray:
        [filePath componentsSeparatedByCharactersInSet:
        [NSCharacterSet whitespaceAndNewlineCharacterSet]]]];
}

Also I've got proper code in viewDidLoad:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.progressView.progress = 0.0;
    [self performSelectorOnMainThread:
        @selector(applicationDidFinishLaunching:)
        withObject:nil
        waitUntilDone:NO];
}

But I've got not a clue how to force UIProgressView to show progress of creating NSSet dictionary from an NSArray. It takes few seconds in iOS Simulator, so on device it can take over a dozen. I really need to show progress, and I prefer this way than Activity Indicator View.

I'm interested in how to get count of imported items at the moment (count of all items is known).

Any idea? :)

  • Do you know which part is taking longer than you would like? Reading the file or building the dictionary? – Mike D Jul 29 '13 at 21:25
  • I am not sure your above code reads from a file. Instead, it seems to form a set from an array formed by file path components. In no way it is reading the actual file bytes. Am I missing anything here? – Nirav Bhatt Jul 29 '13 at 21:36
  • @MikeD First one. Building the dictionary from array is almost non-noticeable. –  Jul 29 '13 at 22:21
  • @NiravBhatt Well, you're right. I thought I can repeatedly read count of NSArray items in meantime of creating it, and use it to calculate a progress, but it looks like impossible. Have you any idea to make it in another way? –  Jul 29 '13 at 22:48
  • Depending on your data structure requirement, you would end up calling arraywithcontentsoffile or dictionarywithcontentsoffile or any variant of it. Your filePath var will be parameter to one of these functions. Not that they provide timebased progress though. They are just one-off funcs that return when the full read is done. For progress you may need to write your own C routine. – Nirav Bhatt Jul 30 '13 at 05:31

3 Answers3

0

Use SVProgressHUD. It has delegates such as showProgress to show progress.

EDIT:

Note that this is just a suggestion to the approach and not practical implementation. Depending on your observation you may or may not choose to implement it.

If it's a local file, you can take some calculated guess about the total time it takes to read the entire file. Then divide this time by 100.

Create an async queue to read from file using one of arrayWithContentsOfFile or dictionaryWithContentsOfFile or whatever. Implement a timer on main thread, and increment progress by 1 through it, and keep posting progress on main thread (do not read progress from async thread because it is never going to tell you of that, we are faking it).

Once arrayWithContentsOfFile returns, post 100% on main thread. You may need to tweak a bit to be accurate (like in Windows where you keep seeing file download time fluctuating) but variation will not be much I suppose. Every type of target device would have given hardware and memory so it would be simply a function of file size.

Nirav Bhatt
  • 6,940
  • 5
  • 45
  • 89
  • 1
    This doesn't help much, since the crux of the question is more about showing progress based on the number of bytes read not which view to use. – Mike D Jul 29 '13 at 21:16
0

If you have a long text file to parse, the framework classes will take long time. You can't change that. And you can't change that they will never call back during work to tell you about their progress.

Your only chance is to perform text file splitting on your own and set your progress as you dig yourself through your textfile. You can split a textfile with regular expressions. The "\w+" collect words with at least one character.

If you set the progress bar in a heavy working method you have to give it some time to refresh the screen once in a while. See the "futuredate magic" below.

Maybe the following can act as a starting point - in the code below the content of the text file is stored in the NSString "content".

NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression
                              regularExpressionWithPattern:@"\\w+"
                              options:NSRegularExpressionCaseInsensitive
                              error:&error];

[regex enumerateMatchesInString:content
                        options:0
                          range:NSMakeRange(0, [content length])
                     usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
                         // push one match to the dictionary
                         [dictionary addObject:[content substringWithRange:match.range]];

                         // update the progress bar
                         NSDate* futureDate = [NSDate dateWithTimeInterval:0.001 sinceDate:[NSDate date]];
                         [[NSRunLoop currentRunLoop] runUntilDate:futureDate];
                         self.progressbar.progress = (float) match.range.location/content.length;
}];

Note that the code is not optimized for speed. For example you should not update the progress bar with every token. Depending on how much tokens you have - count the detected ones and update the pgogress bar every 100 matches, or so...

DerWOK
  • 1,061
  • 9
  • 13
  • I'm not sure if your solutinon makes sense. You posited that there exists `NSString *content` with the text file already loaded, but it doesn't. And creating it takes time I'd like to show with progress bar. –  Jul 30 '13 at 23:16
  • OK. How "big" is your textfile? Mine loads 4MB in a splitsecond on the device. But the actual parsing takes 90% of the time. – DerWOK Jul 31 '13 at 07:27
  • You had the same starting situation: `NSString *filePath = [NSString stringWithContentsOfFile`. So I think showing progress is important not on loading. Try the solution. If also loading takes to long have a look at reading with streams as clarified [here](http://stackoverflow.com/questions/1044334/objective-c-reading-a-file-line-by-line?answertab=votes#tab-top). Good luck! – DerWOK Jul 31 '13 at 07:33
0

You need to use NSURLRequest/NSURLConnection to start an asynchronous request to get the dictionary data. The delegates of NSURLConnection will give you the bytes read and bytes remaining where you can perform a async dispatch to the main thread to update your progress view value.

DrBug
  • 2,004
  • 2
  • 20
  • 21