I'm trying to restart an animation that stops once you minimise the app then re-open it. So when I start the app fresh from Xcode, it animates fine, the genie bobs up & down... But when I close the app with the bottom button, and open it, the app returns to it's original y coordinate and then stops.
The function genie_animation works fine, like I called an NSLOG in the below active function and it worked, but the anim didn't.
I called the function in the applicationDidBecomeActive :
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
ViewController *vc;
vc = [[ViewController alloc] init];
[vc genie_animation];
}
Then this is my code for the anim:
-(void)genie_animation {
CGRect frm_up = tpl_genie.frame;
frm_up.origin.y -= 4;
[UIImageView animateWithDuration:1
delay:0.0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState
animations:^{
tpl_genie.frame = frm_up;
}
completion:nil
];
}
Related
I have an app which plays video, and I don't want people to use the new iOS-11 feature to record these videos and make them public. That feature is described here.
I could not find any documentation regarding an option for my app to prevent users from recording it.
Can anybody please guide me to anything related to this?
Thank you!
I am publishing here the official response from Apple Developer Technical Support (DTS):
While there is no way to prevent screen recording, as part of iOS 11, there are new APIs on UIScreen that applications can use to know when the screen is being captured:
UIScreen.isCaptured Instance Property
UIScreenCapturedDidChange Notification Type Property
The contents of a screen can be recorded, mirrored, sent over AirPlay, or otherwise cloned to another destination. UIKit sends the UIScreenCapturedDidChange notification when the capture status of the screen changes.
The object of the notification is the UIScreen object whose isCaptured property changed. There is no userInfo dictionary. Your application can then handle this change and prevent your application content from being captured in whatever way is appropriate for your use.
HTH!
The feature is available on and above iOS11. Better keep it inside didFinishLaunchingWithOptions
Objective-C syntax
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if (#available(iOS 11.0, *)) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(screenCaptureChanged) name:UIScreenCapturedDidChangeNotification object:nil];
}
return YES;
}
-(void)screenCaptureChanged{
if (#available(iOS 11.0, *)) {
BOOL isCaptured = [[UIScreen mainScreen] isCaptured];// will keep on checking for screen recorder if it is runnuning or not.
if(isCaptured){
UIView *colourView = [[UIView alloc]initWithFrame:self.window.frame];
colourView.backgroundColor = [UIColor blackColor];
colourView.tag = 1234;
colourView.alpha = 0;
[self.window makeKeyAndVisible];
[self.window addSubview:colourView];
// fade in the view
[UIView animateWithDuration:0.5 animations:^{
colourView.alpha = 1;
}];
}else{
// grab a reference to our coloured view
UIView *colourView = [self.window viewWithTag:1234];
// fade away colour view from main view
[UIView animateWithDuration:0.5 animations:^{
colourView.alpha = 0;
} completion:^(BOOL finished) {
// remove when finished fading
[colourView removeFromSuperview];
}];
}
} else {
// Fallback on earlier versions
// grab a reference to our coloured view
UIView *colourView = [self.window viewWithTag:1234];
if(colourView!=nil){
// fade away colour view from main view
[UIView animateWithDuration:0.5 animations:^{
colourView.alpha = 0;
} completion:^(BOOL finished) {
// remove when finished fading
[colourView removeFromSuperview];
}];
}
}
}
My UI uses animation of a UILabel to indicate a particular state. I turn that state on and off by using a key in NSUserDefaults standardUserDefaults. The animation restarts correctly (in the simulator) when the app becomes active after simulating the Home button and then clicking on the app. But it doesn't restart correctly if I simulate the Lock button and then click Home. Both events show in the console and the method -(void)startFlashingButton is called in both cases. I can't figure out why it works in one case and not in the other. I would appreciate any help.
Edit 2/14/17: I read several posts on other boards relating to UINotification. It is apparently very difficult to get to work. My animation gets triggered when a notification comes in to the app delegate. I wanted to set up a kind of "do not disturb" system that would silence notifications at a particular time. My understanding is that this cannot be automated by UINotification because the app does not see a notification unless it is front and center OR it is in the background and the user taps on one of the action buttons. If the app is in background and the user ignores the alert, the app will not get triggered and thus has no way of knowing if the do not disturb time has been reached. Also, you can't do this reliably with an NSTimer because the timer won't be recognized when the app is suspended, which it would be shortly after going to background. So, to put it briefly, I have a much bigger problem than the simulation not running. But thanks to all of you for your replies.
Here is the startFlashingButton method:
-(void) startFlashingButton
{
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
if([storage boolForKey:#"animation started"] == YES){
// Fade out the view right away
[UIView animateWithDuration:2.0
delay: 0.0
options: (UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction)
animations:^{
self.timerLabel.alpha = 0.0;
}
completion:^(BOOL finished){
// Wait one second and then fade in the view
[UIView animateWithDuration:1.0
delay: 2.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.timerLabel.alpha = 1.0;
}
completion:nil];
}];
}
[storage synchronize];
}
In -viewDidLoad, I set up the app to be notified when it becomes active as follows:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(startFlashingButton) name:UIApplicationDidBecomeActiveNotification object:nil];
In the app delegate, I synchonize the user defaults when the app enters background as follows:
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
NSLog(#"application did enter background");
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
[storage synchronize];
As Apple's documentation on background transition cycle states
When the user presses the Home button, presses the Sleep/Wake button, or the system launches another app, the foreground app transitions to the inactive state and then to the background state. These transitions result in calls to the app delegate’s applicationWillResignActive: and applicationDidEnterBackground: methods.
Hence The app might resign being active but not being in the background when you lock and then press the home button.
Try creating this helper method and call it in both applicationWillResignActive: and applicationDidEnterBackground:
-(void)saveUserDefaults {
NSLog(#"application did enter background");
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
[storage synchronize];
}
Also, make sure that Application does not run in background in your info.plist is set to NO else your app will go to the suspended.
I'd like to display a custom View Controller immediately each time the user returns to the app. Currently I can do this easily with didFinishLaunchingWithOptions by simply choosing what the initial rootViewController will be before anything appears on the screen.
However if I put the intercept VC in applicationDidBecomeActive, then the previous VC is on screen for half a second, before I can load the intercept.
How can I make it so when the app is put back into foreground the VC immediately on screen is my custom intercept? That VC will then restore the foreground view on its own.
FYI: I am using this intercept to verify the user is within supported location, then allowing the app to return to state or display an unsupported screen.
There are two solutions to this problem.
Solution:1 You could write your interceptVC - functionality on the application's delegate method
func applicationWillEnterForeground(application: UIApplication)
instead of writing in
func applicationDidBecomeActive(application: UIApplication)
Solution:2
As you did the interceptVC - functionary on applicationDidBecomeActive can also be applied to while application went background,
func applicationDidEnterBackground(application: UIApplication)
so that, when the applicationDidBecomActive delay can be covered.
Note: Here the application once entered background will always has the interceptVC in the background.
Solution by OP.
A solution I used was to set a custom background image when the app resigns active. This is also the screen shown in the multitask selector. When the app resumes active it is the initial screen shown until my interceptVC loads. The image I used is the same background as my interceptVC so it appears instant. This was also easier than attempting to preload the intercept VC on resign of active. Some fade animations are also used on the imageview to make it smoother and non-jarring.
- (void)applicationWillResignActive:(UIApplication *)application {
// fill screen with our own imageView
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.window.frame];
if(IS_IPHONE_6P){
imageView.image = [UIImage imageNamed:#"splash_1242x2208.png"];
} else if(IS_IPHONE_6){
imageView.image = [UIImage imageNamed:#"splash_750x1334.png"];
} else if(IS_IPHONE_5){
imageView.image = [UIImage imageNamed:#"splash_640x1136.png"];
} else if(IS_IPHONE_4_OR_LESS){
imageView.image = [UIImage imageNamed:#"splash_640x960.png"];
} else {
// should not happen
imageView.image = [UIImage imageNamed:#"splash_750x1334.png"];
}
imageView.tag = 1234;
imageView.alpha = 0;
[self.window addSubview:imageView];
[self.window bringSubviewToFront:imageView];
// fade in the view
[UIView animateWithDuration:0.5 animations:^{
imageView.alpha = 1;
}];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// grab a reference to our imageView
UIView *imageView = [self.window viewWithTag:1234];
// fade away imageView view from main view
[UIView animateWithDuration:0.5 animations:^{
imageView.alpha = 0;
} completion:^(BOOL finished) {
// remove when finished fading
[imageView removeFromSuperview];
}];
}
I have an app that uses a xib defined AdBannerView. If the app runs on an iPhone (4 or 5) everything works as expected, ads get shown, banners get hidden / shown etc.
However if the app is run on an iPad it crashes after repeatedly failing to receive the ad. Examining the call stack shows repeated calls to bannerView:didFailToReceiveAdWithError:
Watching allocations while its running on an iPad shows continuous memory growth until the crash.
Messing with the network connectivity doesn't seem to alter the fact that it works on an iPhone but not on an iPad.
I read this SO question which instead of using a AdBannerView in the xib it creates it on the fly and then releases it appropriately when the ad fails to load.
EDIT:
I changed the devices setting in the project file from iPhone to Universal. The app now does not crash but of course all the views are now 'messed up'. So one option for a fix would be to implement the iPad idiom throughout the app.
My questions are -
What is going on? No, really! I'm confused as to why there is differing behaviour between devices.
Should I look to creating the AdBannerView programmatically? That kind of feels defeatist.
How can I fix this behaviour?
Here is the code
#pragma mark ADBannerViewDelegate
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
[self showBanner];
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
[self hideBanner];
}
- (void)bannerViewActionDidFinish:(ADBannerView *)banner
{
[self hideBanner];
}
#pragma mark ADBanner helpers
- (void)hideBanner
{
CGRect hiddenFrame = self.bannerDisplayFrame;
hiddenFrame.origin.y = self.view.frame.size.height;
[UIView animateWithDuration:0.3f
animations:^{
[self.adBannerView setFrame:hiddenFrame];
}
completion:^(BOOL finished)
{
if (finished)
{
[self.adBannerView setAlpha:0.0f];
}
}];
}
- (void)showBanner
{
[self.adBannerView setAlpha:1.0f];
[UIView animateWithDuration:0.3f
animations:^{
[self.adBannerView setFrame:self.bannerDisplayFrame];
}
completion:^(BOOL finished)
{
if (finished)
{
[NSTimer scheduledTimerWithTimeInterval:60.0f target:self selector:#selector(hideBanner) userInfo:nil repeats:NO];
}
}];
}
It looks like you are creating new iAD banner views every time, the suggested way is to use a shared one throughout the whole app. This might be the reason of continuous memory growth in your app and you will definitely end up with a warning from apple servers if you request ads too many times. Have a look at here in Apple's documentation for more details iAD Best Practices
This is how I implemented shared adbannerview, it might be of help.
AppDelegate.h
#property (nonatomic, strong) ADBannerView *adBanner;
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
adBanner = [[ADBannerView alloc] initWithFrame:CGRectZero];
adBanner.delegate = self;
adBanner.backgroundColor = [UIColor clearColor];
adBanner.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
...
}
prefix.pch or better in a header file included in prefix.pch
#define SharedAdBannerView ((AppDelegate *)[[UIApplication sharedApplication] delegate]).adBanner
And I have a implemented a uiviewcontroller category to handle iADs
#implementation UIViewController (SupportIAD)
-(void)bannerViewDidLoadAd:(ADBannerView *)banner
{
SharedAdBannerView.hidden = FALSE;
}
-(void) bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
SharedAdBannerView.hidden = TRUE;
}
//This method adds shared adbannerview to the current view and sets its location to bottom of screen
//Should work on all devices
-(void) addADBannerViewToBottom
{
SharedAdBannerView.delegate = self;
//Position banner just below the screen
SharedAdBannerView.frame = CGRectMake(0, self.view.bounds.size.height, 0, 0);
//Height will be automatically set, raise the view by its own height
SharedAdBannerView.frame = CGRectOffset(SharedAdBannerView.frame, 0, -SharedAdBannerView.frame.size.height);
[self.view addSubview:SharedAdBannerView];
}
-(void) removeADBannerView
{
SharedAdBannerView.delegate = nil;
[SharedAdBannerView removeFromSuperview];
}
#end
And now in every viewcontroller that is going to display iADs, import the category and in viewDidLoad:
- (void)viewDidLoad
{
...
[self removeADBannerView];
[self addADBannerViewToBottom];
...
}
To prevent the views getting messed up, try turning "Auto Layout" off.
Xcode 4.5 corrupting XIBs?
I have solved this problem.
The root of the problem is that iPad's view property of iPad's UIViewController looping recursively.
All you need is to break infinite call.
In my case I just add these lines:
if (_banner.alpha == 0)
{
return;
}
in banner hiding method.
I guess you have crash here:
hiddenFrame.origin.y = self.view.frame.size.height;
Anyway, its not a good approach do not check property before changing.
I'm wanting to spoof the feel of the main splash screen fading out whenever applicationDidBecomeActive is called, but it's not working. What am I doing wrong?
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if(IS_IPHONE_5)
splash = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default-568h.png"]];
else
splash = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default.png"]];
[self.window.rootViewController.view addSubview:splash];
[UIView animateWithDuration:0.5
animations:^{
splash.alpha = 0;
}
completion:^(BOOL finished) {
[splash removeFromSuperview];
}];
}
Then you need to define the following somewhere. I use the project .pch but you can use your header if you want.
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
I find, from ios6 you get a nice transition doing this
-(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[UIView animateWithDuration:0.2
delay:0
options: UIViewAnimationCurveEaseIn
animations:^{
self.window.viewForBaselineLayout.alpha = 0; // and at this alpha
}
completion:^(BOOL finished){
}];
return YES;
}
then immediately at the start of
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[UIView animateWithDuration:0.5
delay:0
options: UIViewAnimationCurveEaseOut
animations:^{
self.window.viewForBaselineLayout.alpha = 1; // and at this alpha
}
completion:^(BOOL finished){
}];
It gives a cross fadeish effect from the loading screen to the now loaded app screen.
If that is really your code, you probably have a typo in the image name. (If not, let us know what "not working" means.)
Also, the splash screen doesn't normally come up every applicationDidBecomeActive:. didFinishLaunchingWithOptions: is the time you know that you have been launched and the splash screen had been shown on your behalf.
Try adding it directly to your window instead of the rootViewController.view.
[self.window addSubview:splash];
You may also need to rotate the image using view.transform to align with the startup image.
Your code looks about right; I do this in several apps.
However, you want to do this as part of applicationDidFinishLaunching:options: and not in applicationDidBecomeActive:. It only makes sense to fade the splash screen when it is shown, which is only when the app is launched and not already running. When your app becomes active, it may have been in the background -- i.e. already launched -- so fading the splash screen in this case doesn't make sense.
Or, did you want your splash screen to appear ALWAYS when it becomes active, even if it is resumed from the background from a suspended state?