While using my app, the screen sometimes suddenly and inexplicably turns black. The screen is still powered, and if I play with the volume controls, the volume indicator shows just fine.
The only way to resolve this is to press the home button, at which point I see the following message in my log:
SpringBoard[15] : Failed to snapshot WaveDeck
When I then open the app again, it's fine.
Can anyone shed some light on this?
Update from the WaveDeck front:
We've used applicationDidEnterBackground in the app's delegate in a bad way: asking all the background operations to finish up using beginBackgroundTaskWithExpirationHandler and keeping the main runloop running inside applicationDidEnterBackground.
That way the method of applicationDidEnterBackground came to an end only after finishing all background operations, whether the app became active again or not.
So apparently the OS could not 'snapshot' the app correctly and would put on a black screen some time after returning from background state - when the delegate function of applicationDidEnterBackground ended.
I encounter a similar issue. Put the long time save operation in next runloop, so applicationDidEnterBackground return immediately, the system's snapshot mechanism works correctly.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// don't block ios's snapshoting, avoid screen suddenly turns black
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(saveAppState) object:nil];
[self performSelector:#selector(saveAppState) withObject:nil afterDelay:0];
}
- (void)saveAppState
{
[self longtimeSave];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(saveAppState) object:nil];
}
Related
In my app I'm downloading lots of images on a method.
I'm using a
downloadTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:downloadTask];
downloadTask = UIBackgroundTaskInvalid;
}];
This is working fine, if I press the home or sleep button, the images continue downloading.
I'm showing the progress in a UIProgressView inside an UIAlertView, and when the percent is 100% the alertView is dissmised and I change the viewController to other where I show the donwloaded images.
But I only want this to happen if the app is really active at the moment the download finish.
I have been looking at the app state and while it's downloading with the screen off.
[UIApplication sharedApplication].applicationState
the state is UIApplicationStateActive during all the donwload
How can I can know if the downloading is happening with the screen off or on?
EDITED AFTER ACCEPTING THE ANSWER:
I just discovered, if I tap the home button, the app enters in UIApplicationStateBackground, if I tap the wake/sleep it enters in UIApplicationStateInactive
Following the approach of the correct answer, my app contines donwloading in both cases.
The screen is off in two states (apart from when the app has not been even opened):
suspended : in this case you don't have to worry because the download won't procede until the app gets active again; It will enter this state on
background : it's in this state for a limited amount of time before going in suspend, and the screen is already off in this moment. Here you may want to check then whether to do all the things you said or not, because in this state code can be still executed. In this state the app status is UIApplicationStateBackground, so you could just perform a check like this:
You probably want to check whether the app is in background execution in order to achieve the result. Just like this:
if([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
// Do stuff
}
If it's in background, so the screen is off.
UPDATE: after few test, what I figured out is that the behaviour you are expieriencing is probably due to the execution of the download on the main thread.
You should send the download on (for instance) the global queue. This way the application will enter the background state as expected:
....
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.bti];
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self doBackgroundStuff];
});
....
This way, if the app is put on background while the download is in progress, the application state will turn into UIApplicationStateBackground, and then you can check it as I wrote initially. If you are doing UI updates during the progress remember to send them back to the main thread (because the download is now on a different one).
You can check whether your app is running in the background or not by setting a flag in the designated application delegate methodsapplicationDidEnterBackground: and applicationWillEnterForeground:. Example:
- (void)applicationDidEnterBackground:(UIApplication *)application
_applicationRunsInForeground = NO;
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
_applicationRunsInForeground = YES;
}
If you don't want to have this _applicationRunsInForeground flag inside your application delegate, you could observe the delegate's NSNotifications in your viewcontroller class instead (UIApplicationWillEnterForegroundNotification and UIApplicationDidEnterBackgroundNotification).
When i keep my application open for awhile, the iPhone/iPod locks the screen. When i unlock it my application gets stuck for like 2 seconds and then it resumes and keep functioning as usual. Why is this ? and how can i prevent it ?
To prevent this from hapenning is there any PLIST method where we could stop the process of the application when it goes to a locked screen (Might not be a better idea)
In your application delegate do you have any code that could slow down your app? Check the following methods?
-(void) applicationWillResignActive:(UIApplication *)application
-(void) applicationDidBecomeActive:(UIApplication *)application
-(void) applicationDidEnterBackground:(UIApplication*)application
-(void) applicationWillEnterForeground:(UIApplication*)application
-(void) applicationWillTerminate:(UIApplication *)application
Also use the above methods ensure you application suspends properly.
Log when your app receives a memory warning inside:
-(void) applicationDidReceiveMemoryWarning:(UIApplication *)application
Maybe when you suspend or reopen your app some there is a memory issue.
I'm not quite sure about the answer for your first question (you maybe do some heavy things inside the applicationDidBecomeActive method or the app simply reallocates memory), but i can answer the second one.
You can simply prevent the auto lock by calling:
[[UIApplication sharedApplication] setIdleTimerDisabled: YES];
A good palce for this is inside the applicationDidFinishLaunching method of the app delegate.
I have a toggle menu which toggle between the words "Pause" and "Resume" when it is pressed, which also pause and resume the whole game. This means when playing, the menu will be shown as "Pause" (tap here to pause), when pausing the menu will be shown as "Resume" (tap here to resume).
Here is the problem, if I tap the home button after I pause the game, then go back into it, it resumes itself and the pause menu is shown as "Resume". And this doesn't make sense to me. The best way I want is to pause the game whenever go into the background and resume from background. I look at the following methods, but they don't really work:
-(void)applicationWillResignActive:(UIApplication *)application{
}
- (void)applicationDidBecomeActive:(UIApplication *)application{
}
-(void)applicationWillEnterBackground:(UIApplication*)application{
}
-(void)applicationDidEnterBackground:(UIApplication *)application {
}
I even just put CCLOG in all of those methods, but nothing has been called. Is there something I am need to put/declare before I use those methods?
Sorry, it is a bit too long to read. Hope you can help me. Thank you.
I'm not sure why, but for my experience (not cocos2d), there's no additional implementation if want apply those methods.
perhaps, you should try look at this.
link 1 & link 2
-(void)applicationWillResignActive:(UIApplication *)application{
}
- (void)applicationDidBecomeActive:(UIApplication *)application{
}
you need custom your cocos2d CCDIRECTOR class. And get now display layer. then active it pause or resume function. All this must need some protocol.
These methods are invoked when the iOS forces your application in the background, or resumes execution of your application, ie they are signals you receive when an external event causes your application to go to background, or come back from it. You should not try to invoke them directly. There is no real relationship with a 'user created' menu like yours (like your Resume/Pause menu), unless you make the relationship explicit.
So , in the following method:
- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(#"<%#>:applicationWillResignActive - received signal, pausing sharedDirector.",[self class]);
// here : place your code for forcing your menu in the 'Resume' state
// i am assuming some kind of change in a button, and
// a state variable of your own that define and control
// what it means to be 'paused' from your applications point of
// view
// then force the director to pause (animations, scheduling, touch, etc ...)
[[CCDirector sharedDirector] pause];
}
after, when the iOS hands you back control by placing your application in the forground as the running application:
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"<%#>:applicationDidBecomeActive - received signal, resuming sharedDirector.",[self class]);
[[CCDirector sharedDirector] resume];
}
You dont really need to do anything fancy here other than restart the CCDirector, since your menu is in the 'Resume' state, garanteed. When user presses Resume, you will start your game again and put the menu in the 'Pause' state.
I have an application that requires the iPhone screen to remain active (or not, depending on user choice). I've done this by disabling the application idle timer, which works fine and dandy until I start playing media via the MPMusicPlayerController. Due to a bug in the SDK, this then reenables the idle timer with no apparent way to disable it again.
My app flow is:
App starts
Screen stays on
<...time passes...>
Play audio file
Idle timer kicks in
Screen turns off
I have an empty audio file playing in the background to stop the phone going into deep sleep, but I'd really like to keep the screen unlocked too.
Has anyone managed to figure out a workaround for this?
I had a similiar problem, and found a fix for it. The fix might work for you too:
I call a method periodically (every 10 seconds), which sets idleTimerDisabled first to NO, then to YES.
- (void)calledEveryTenSeconds
{
[UIApplication sharedApplication].idleTimerDisabled = NO;
[UIApplication sharedApplication].idleTimerDisabled = YES;
}
Only setting to YES alone does not fix the problem. It seems the property has to change first to be recognized by UIApplication.
My problem was, that the screen kept turning dark as soon as I switched music tracks on the iPod player via the headphone remote. My guess is, that this is the same issue as you are experiencing.
You should simply turn off the idle timer. What I usually do in a viewcontroller that needs to stay 'awake' is this:
- (void) viewWillAppear:(BOOL)animated
{
[[UIApplication sharedApplication] setIdleTimerDisabled: YES];
}
- (void) viewWillDisappear: (BOOL) animated
{
[[UIApplication sharedApplication] setIdleTimerDisabled: NO];
}
This will make sure the screen will not get locked due to user inactivity.
I found a solution to this problem. Invoke a method that disables the idleTimer in about 5 seconds after you start playing the music. It's a bit of a hack, but it is a workaround.
[[SoundEngine mainEngine] playMusic];
[self performSelector:#selector(setIdleTimeDisabled) withObject:nil afterDelay:5.0];
- (void) setIdleTimeDisabled {
[UIApplication sharedApplication].idleTimerDisabled = YES;
NSLog(#"Setting idleTimer to TRUE");}
let player = MPMusicPlayerController.applicationMusicPlayer()
player.setQueueWithStoreIDs(["some id"])
player.play()
player.pause()
Is there any way to detect if the iPhone wakes up from sleep while you're app is running? Eg: your app is running, the user locks the screen (or the screen auto locks) and some time later the user unlocks the screen and up pops your app. Is there some way to get an event at that point or detect it somehow?
I've tried searching the Google and this forum, but I can't seem to find anything about it.
See applicationDidBecomeActive: on UIApplicationDelegate.
Stick these in you AppDelegate.m file:
-(void) applicationWillResignActive:(UIApplication *)application {
NSLog(#"Asleep");
}
-(void) applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"Awake");
}
#Kevin - Nothing wrong with your answer - thanks by the way. Just thought I'd save the next person a Google search.