ActivityIndicatorView in nav bar continues animating after ViewDidLoad - ios

I have an issue with the activity indicator view continuing to animate after the page has finished loading. The problem only occurs on UIWebView pages that have already been visited.
I am also using MBProgressHUD and this stops animating after the ViewDidLoad as expected.
The page loads almost instantaniously and then the animation starts in the nav bar. It's almost as if the page is pulled from the device cache and then the application looks to go and load it.
The spinner only stops animating when you visit a different view controller.
Has anyone else faced issues similar to this?
EDIT
- (void)showNetworkActivity {
[self performSelector:#selector(showNetworkActivityWithDelay) withObject:nil afterDelay:0.5];
}
- (void)showNetworkActivityWithDelay {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
- (void)hideNetworkActivity {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
Works okay when I remove the delay.
I inherited the code, so I'm not sure whether the delay is needed. Is there any good reason to have a delay on showing the networkActivityIndicator?

Here the solution.
Try to call [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; directly.
performSelector:withObject:afterDelay: schedules a timer on the same
thread to call the selector after the passed delay.
I don't see reasons to use the delay in this examples. In fact, if you use it, the selector showNetworkActivityWithDelay is called after a delay and you could not see it immediately on the screen.
Obviously you can continue to wrap the activity indicator logic with the previous methods like the following (but it's not necessary):
- (void)showNetworkActivity {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
- (void)hideNetworkActivity {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}

Sounds like you don't have UIWebViewDelegate set up. Set the delegate in the header/implementation, then start animating the indicator in UIWebViewDelegate webViewDidStartLoad, then stop animating in webViewDidFinishLoad and webViewDidFailLoadWithError.

Related

AdColony + SVProgressHUD Conflict

I'm experiencing some odd behavior when integrating AdColony's 2.2.4 library with an application using SVProgressHUD. If the standard configuration call is made in the app delegate...
[AdColony configureWithAppID:kAdColonyAppID zoneIDs:#[kAdColonyZoneID] delegate:nil logging:true];
SVProgressHUD no longer pops up in the app by calling
[SVProgressHUD showWithStatus#"Status..."];
Simply commenting out the AdColony configureWithAppID call causes SVProgressHUD to function again normally. Has anyone else encountered this, or found a way to make them both work in the same application?
It looks like the SVProgressHUD code is doing a check on line 436 that doesn't take into account that there can be multiple UIWindows in existence belonging to the UIWindowLevelNormal level. The consequence of this is that the HUD view is being added to the wrong window. In order to get the SVProgressHUD working, you can modify the for loop starting on line 436 as follows:
for (UIWindow *window in frontToBackWindows) {
if (window.keyWindow) {
[window addSubview:self.hudView];
break;
}
}
Please feel free to contact us (support#adcolony.com) with any further integration questions you may have.
It works for me ,I have added adcolony framework so success messages for SVProgress hud was not showing:
I comment the code:
// for (UIWindow *window in frontToBackWindows)
// if (window.windowLevel == UIWindowLevelNormal) {
// [window addSubview:self.overlayView];
// break;
// }
and replace with
for (UIWindow *window in frontToBackWindows) {
if (window.keyWindow) {
[window addSubview:self.hudView];
break;
}
}
So It works,.. .Thanks
The simplest way which worked for me is
[[[UIApplication sharedApplication] keyWindow] addSubview:self. overlayView];
This performs the same action the accepted answer.

iOS popup/splash screen with external web content

I need to display a popup or something like a splash screen every time I start my app. The content of this popup must be taken from an external web source (something like jpg, png or pdf).
The purpose of this popup is to warn users about news and special offers. The popup should disappear after a certain time (or at the pressure of a button).
From what I read on other threads, the UIPopoverController feature seems be helpful for what I need (as I read in this class reference), but I'm afraid that the main function of this popup is presenting a choice in result of the pressure of a button.
Why can you not simply add a webview to the screen when the app opens?
Like:
in appDelegate:
UIWebview *popover;
- (void)applicationDidBecomeActive:(UIApplication *)application
{
UIWindow *win = [[UIApplication sharedApplication] keyWindow];
popover = [[UIWebView alloc] initWithFrame:win.bounds];
... load content ...
[win addSubview:popover];
[self performSelector:#selector(dismissPopover) withObject:nil afterDelay:3];
}
-(void)dismissPopover
{
[popover removeFromSuperview];
}

UIApplication's -canOpenURL: -openURL: return misleading result

Since iOS6, I can't tell whether the application can launch Safari or not.
If Safari is restricted on the device (Settings>General>Restrictions), nothing happens when trying to open a URL, and there's no indication of what went wrong:
NSURL *url = [NSURL URLWithString:#"http://www.google.com"];
[[UIApplication sharedApplication] canOpenURL:url]; // Returns YES
[[UIApplication sharedApplication] openURL:url]; // Returns YES
However, Safari does not launch, and the user is left wondering why my buttons are "broken".
This seems like a bug to me so I filed a radar #12449905.
Is there another way to solve this problem?
If this is an Apple bug, then it looks like the thing for you to do is to program around it. Once the user clicks the button, you can always write something like this:
[self performSelector:#selector(notifyUserOfRestrictedAccess) withObject:self afterDelay:.5];
In the app delegate, you can set a property such as:
- (void)applicationWillResignActive:(UIApplication *)application {
self.openingExternalProgram = YES;
}
In your view controller, create the method like this:
-(void) notifyUserOfRestrictedAccess {
if (!appDelegate.openingExternalProgram) {
// Message the user via UIAlertView about restricted Safari access
}
appDelegate.openingExternalProgram = NO;
}
I'm sure there are better ways, but at least you don't have to wait on Apple.

iOS how to execute a task with beginBackgroundTaskWithExpirationHandler

I am trying to do two things for a simple test app.
I am stuck at trying to learn how to use beginBackgroundTaskWithExpirationHandler
I want to execute a backgroundTask when the user presses the home button (nothing fancy). In 9 minutes, I'd like to alert the user that the time is about to expire (if possible) and allow the user to switch back into the app to renew the 10 minutes.
I don't need backward compatibility with iOS 3, or 4.
If you want your code to continue in the background, then you'll need to wrap it in a background task. It's also very important that you call endBackgroundTask when you're finished - otherwise the app will be killed after it's allotted time has expired
- (IBAction) buttonPressed: (id) sender
[self beingBackgroundUpdateTask];
// Do your long running background thing here
[self endBackgroundUpdateTask];
});
}
- (void) beingBackgroundUpdateTask
{
self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
}
- (void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}
Put the code in the applicationDidEnterBackground function in your UIApplicationDelegate. You will need to set up a UILocalNotification and schedule it. You should also probably disable it in applicationWillEnterForeground so it doesn't fire off the user goes back to the app before it expires.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UILocalNotification *timerNotification = [[UILocalNotification alloc] init];
//set up notification with proper time and attributes
[[UIApplication sharedApplication] scheduleLocalNotification:timerNotification];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
The cancelling code I gave there will actually cancel all notifications. If you have multiple notifications and only want to cancel a specific one, you should give the userInfo property of your notification a key/value when you set it up. Then, when the application enters the foreground, get the list of all active notifications by doing
NSArray *notifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
and loop through them, checking userInfo until you get to the one you want and then just cancelling that one with
[[UIApplication sharedApplication] cancelLocalNotification:whateverNotification];

Grand Central Dispatch, viewWillAppear, viewDidAppear order of execution confusion

I'm using GCD for background downloading in my Tab Bar app.
First step is to do some background downloading in -viewWillAppear: (to setup some basic data before the view is loaded).
Second step is to the rest of the background downloading in -viewDidAppear:
For some reason, the dispatch block in -viewDidAppear: gets called before the dispatch block in -viewWillAppear:.
This only happens once after loading the application for the first time switching to the tab with the GCD background methods. Switching to another tab and then switching back to the tab with the GCD background methods. The third (and all the rest subsequent times) time I'm switching back it's works as expected (-viewWillAppear: firing first and then -viewDidAppear:).
Here are excerpts of my code (-viewWillAppear: and -viewDidAppear:):
-viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
DLog(#"viewWillAppear method running");
[super viewWillAppear:animated];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self setDiskCareerIds:[CareersParser idsFrom:#"disk"]];
[self setDownloadedCareerIds:[CareersParser idsFrom:#"web"]];
DLog(#"diskCareerIds after being set in viewWillAppear: %#", [self diskCareerIds])
DLog(#"downloadedCareerIds after being set in viewWillAppear: %#", [self downloadedCareerIds])
if ([[self downloadedCareerIds] isEqualToArray:[self diskCareerIds]]) {
DLog(#"viewWillAppear: ids equal, loading careers from disk.");
self.careers = [CareersParser loadCareersFromDisk];
dispatch_async(dispatch_get_main_queue(), ^{
[self.table reloadData];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
});
}
});
//[self downloadData];
}
-viewDidAppear:
- (void)viewDidAppear:(BOOL)animated {
DLog(#"viewDidAppear method running");
[super viewDidAppear:animated];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
if (![[self downloadedCareerIds] isEqualToArray:[self diskCareerIds]]) {
DLog(#"ids not equal, saving careers to disk.");
dispatch_async(dispatch_get_main_queue(), ^{
[self showLoadingView];
});
[CareersParser saveCareersToDisk];
self.careers = [CareersParser loadCareersFromDisk];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.table reloadData];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
[self removeLoadingView];
});
});
//[self download3];
//[self downloadData];
}
Check the debug log at Pastie.
Well, you're printing that log message in that first block (the one scheduled in viewWillAppear:) after it has done a bunch of parsing, not when it actually starts executing.
The thing is that global queue is a concurrent queue. So even though you are scheduling that first block first, it's not surprising that it sometimes falls behind the other block which is executing concurrently with it.
One simple answer would be to create a serial queue, and then you'll be sure the first block completes before the second one is executed. That seems to be what you want, right?

Resources