I am uploading a bunch of files from the iPhone to a web service. I want to have a UIAlertView on screen with a UIActivityIndicatorView inside. I got that to work fine. However, I want to update the title of the UIAlertView as each file gets uploaded. ("Uploading file 1...", "Uploading file 2...", etc.)
I know that you can't just set the title in a loop with synchronous web requests as the UI run loop will never get called. I tried using NSTimer to fire off the web requests but since each request duration is unpredictable, that doesn't work. (The message could update before the request is actually finished.)
I really want to upload each file synchronously, one at a time, as the iPhone bandwidth is pretty limited. I just can't figure out a mechanism to say 'once this synchronous operation finishes, let the UI update for a tick, then do another synchronous operation.'
OK here's the approach I took to solve this:
Create the UIAlertView
Start an asynchronous web request to post the item, set the delegate to self
In the connectionDidFinishLoading: method, check to see if there are more items to post. If there are, update the title and kick off another async request. If not, dismiss the alert.
It means you have to maintain a few class variables to track which request is uploading but it works perfectly and doesn't involve any complicated threading or anything else.
did you try this?
alertView.title = #"new title";
[alertView setNeedsDisplay];
Related
im using a UIWebView to show html content in my app, the app contains two arrows to navigate between topics just like a RSS reader app, but when the user hits up or down arrow, the next topic doesn't show up until the data come back and the user still able to interact with the UI which is a bit confusing,
My question: how to block the UI when user moves to the next/back topic ? in other words how to make loadHTMLString:baseURL: works as a synchronous calling ? thanks !
You can let the load happen asynchronously, but set the web view's userInteractionEnabled property to NO. (then back to YES, on the didFinishLoad callback).
Or you could put up a clear colored view (with userInteractionEnabled set to NO) above the web view that has an activity indicator and button that lets the user cancel the load.
An even better idea would be to place two other web views offscreen and start loading them for page N-1 and N+1. When the user presses a page arrow, swap frames with the corresponding prefetched web view.
Try this - https://github.com/gavrix/UISynchedWebView-demo
You don't actually want to block the UI. If you do that, there is a very high probability that Apple will reject your app once you send it in for app store submission. Anything that even remotely makes the application feel unresponsive will weigh heavily on you. Instead, create a background thread using GCD or performSelectorInBackground, handle your loading in that, and then once the loading is done, make all of the information available to your UIWebView all at once and alert it to render the display.
If you are in a pinch and have a UIPageViewController and still want to use a UISynchedWebView to ensure that a page has loaded before you run javascript, you can run a block on the main thread's event queue. Still has a slight delay while the javascript runs but won't cause recursion in the run loop.
dispatch_async(dispatch_get_main_queue(), ^{
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]]]; // self.webView is a UISynchedWebView
NSLog(#"url:%#", [self.webView stringByEvaluatingJavaScriptFromString:#"window.location.href;"]); // shows google.com instead of about:blank
});
I am using TabBarKit, and I want to execute a request to pull a new peice of content from a webservice each time a user goes back to a tab.
I can't put the request code in viewDidLoad as its not fired when coming back to the tab. With that said, I've noticed viewWillAppear / viewDidAppear are called multiple times when returning back to a tabs view controller.
If I put the requesting code in there, it is fired multiple times resulting in the webservice being pinged needlessly.
How can I solve this problem? Which method should I place my HTTP request call in so it executes once per view?
You could try setting/checking a downloadInProgress flag before submitting the asynchronous download, then resetting that flag when the request completes.
If you're using something like the ASIHTTPRequest, that calls a delegate method when the request completes or fails, which is the point where you could reset the flag. It allows you to tag each request individually so you can track the success or failure of each one, so this wouldn't restrict you from running one background request at a time.
Turn's out there was an extra call to viewWillAppear in the controller code. If you're interested in following the changes, there is a thread on the issues section of the Git project.
When the return button on the keyboard for a textfield is tapped I want to add a UIView, then connect to a website with NSURlConnection sendsynchronousrequest and I have the code in that order
But when I run in the simulator (I can't run on device) the connection is run first then the subview is added (ie the opposite of the order of the code)
Why is this and how can stop it, because I want the view to added, then the connection done and then the view removed.
The subview is being added, but views are drawn by the runloop. By making a synchronous request on the main thread, you are blocking the runloop, so the view won't be drawn until after the request completes. Do the request asynchronously, either by using the async API or by doing a synchronous request in a background thread.
Many actions happen on the run loop, rather than in the order you code. If you really want to code the way you have, then performSelector:withObject:afterDelay: with a delay of 0 might work to trigger your NSURlConnection (you will need to move that code to a method).
As JK suggests, an asynchronous request might solve it anyway, and improve the UI. I'm a great fan of ASIHPPTRequest library, which makes async trivial.
So I am trying to create a check that tries to connect to the WWW. when it fails it needs to then retry several times before the application gives up and quits. Each time it retries the user is propted with an UIAlertView with the options to Retry or Cancel.
So here is the problem.
I have a chain of actions in an NSOperationQueue, all the operations should fail with no connection. I"m using the NSoperation Queue so that the UI dosn't lock and the data is being processed in the background.
inside an NSInvocationOperation my method will hit [AlertView show], however this is not truly modal.
My operation then returns and continues through the chain of NSOperations, as there seems to be no way to return them with an Error value to stop additional processing. Eventually the UI catches up, displays the Modal AlertView, but i have no context of what has happened.
I am sure this is a common requirement. any ideas how to achieve this?
If I understand you correctly, you want a modal version of UIAlertView, but only modal within the calling thread/NSOperation? A few problems with this:
You should probably only call interface operations from the main thread (easily addressed using performSelectorOnMainThread:)
Modal dialogs are not really part of the OS; you'll need to address this programatically.
I have a few tabs in my iPhone application which take a few seconds to load (pulling large amounts of data from a local sqlite database). When the users touch the tabs it appears as if the application is doing absolutely nothing. I've attempted to put a window showing a spinner up, however it is never shown due to the fact that the processing immediately follows.
I know i have a couple of different options for loading the data asynchronously, however i wanted to poll the community and see if there were any potential problems with just forcing another cycle of the NSRunloop to show the window.
Here's what my code looks like...
[[ActivityIndicator sharedActivityIndicator] show];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
On a scale of 1 to 10 how bad would you rate this hack?
I don't know where I'd rate it, but I know I wouldn't want to do it that way. Messing with the system default runloop just seems like a bad idea.
There are a couple of what I think are good approaches. The simplest is to put the additional processing in a separate private method, and then do this:
[[ActivityIndicator sharedActivityIndicator] show];
[self performSelector:#selector(processingMethod) withObject:nil afterDelay:0];
That will cause processingMethod to be called at the end of the run loop, after your indicator is showing. Should work fine.
The one caveat is that if your indicator is animated, depending on how it's set up it may not be animated while processingMethod is running. In that case you'd want to run processingMethod in a background thread, which might be a little more complicated, or might be as simple as doing this instead:
[self performSelectorInBackground:#selector(processingMethod) withObject:nil];
The potential complication there is that at the end of processingMethod, when you're going to display the results of your processing, you may have to call a method back onto the main thread.
My experience is that the event handling code on iPhone is not reentrant. So if you're running the runloop in default mode be prepared for various crashes.
I've found others are having issue too:
http://lists.apple.com/archives/xcode-users/2009/Apr/msg00313.html
http://www.iphonedevsdk.com/forum/iphone-sdk-development-advanced-discussion/16246-trying-make-modal-dialog-using-nested-nsrunloop.html