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?
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];
}];
}
}
}
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 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
];
}
I am trying to figure out my iPad's orientation at start up on a flat table.
I always says it's starting in portrait mode, which it isn't true.
I have used the code sampled here to detect the orientation at start up.
It so happens that when my device is face up on a flat surface it claims it is in portrait mode. However the status bar is visually in landscape mode.
Is there a way around this?
I am using iOS 5 and Xcode 4.3.
EDIT: More details
Here is my update orientation method:
- (void)updateOrientation {
UIInterfaceOrientation iOrientation = self.interfaceOrientation;//[[UIApplication sharedApplication] statusBarOrientation];
UIDeviceOrientation dOrientation = [UIDevice currentDevice].orientation;
bool landscape;
if (dOrientation == UIDeviceOrientationUnknown || dOrientation == UIDeviceOrientationFaceUp || dOrientation == UIDeviceOrientationFaceDown) {
// If the device is laying down, use the UIInterfaceOrientation based on the status bar.
landscape = UIInterfaceOrientationIsLandscape(iOrientation);
} else {
// If the device is not laying down, use UIDeviceOrientation.
landscape = UIDeviceOrientationIsLandscape(dOrientation);
// There's a bug in iOS!!!! http://openradar.appspot.com/7216046
// So values needs to be reversed for landscape!
if (dOrientation == UIDeviceOrientationLandscapeLeft) iOrientation = UIInterfaceOrientationLandscapeRight;
else if (dOrientation == UIDeviceOrientationLandscapeRight) iOrientation = UIInterfaceOrientationLandscapeLeft;
else if (dOrientation == UIDeviceOrientationPortrait) iOrientation = UIInterfaceOrientationPortrait;
else if (dOrientation == UIDeviceOrientationPortraitUpsideDown) iOrientation = UIInterfaceOrientationPortraitUpsideDown;
}
whiteView.hidden = NO;
splashScreen.hidden = NO;
if (landscape) {
splashScreen.image = [UIImage imageNamed:#"Default-Landscape~ipad"];
} else {
splashScreen.image = [UIImage imageNamed:#"Default-Portrait~ipad"];
}
splashScreen.contentMode = UIViewContentModeScaleToFill;
[self.view bringSubviewToFront:whiteView];
[self.view bringSubviewToFront:splashScreen];
// Set the status bar to the right spot just in case
[[UIApplication sharedApplication] setStatusBarOrientation:iOrientation];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (splashScreen){
[self updateOrientation];
[UIView animateWithDuration:0.5 delay:1 options:UIViewAnimationOptionCurveEaseInOut
animations:^{
splashScreen.alpha = 0;
}
completion:^(BOOL success){
[splashScreen removeFromSuperview];
splashScreen = nil;
}];
[UIView animateWithDuration:0.5 delay:1.5 options:UIViewAnimationOptionCurveEaseInOut
animations:^{
whiteView.alpha = 0;
}
completion:^(BOOL success){
[whiteView removeFromSuperview];
whiteView = nil;
}];
}
The problem is noticeable when I start my app in a flat surface in landscape mode I see the wrong image being loaded up for the spashscreen.
Use [[UIApplication sharedApplication] statusBarOrientation] instead of [UIDevice currentDevice] orientation].
There's a difference between device orientation and user interface orientation (and some elaboration can be found on this related question). Many apps might have a user interface designed to only work certain ways even though the device is pointed another way.
I'm experiencing the same problem, and I have not found any good solution.
It seems that [[UIApplication sharedApplication] statusBarOrientation] always return 1 before application:didFinishLaunchingWithOptions: has finish, and only if your device is face up (maybe face down too, i have not tried)
I finally done this :
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// For the cases where device is not face up.
if(UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]))
[self doTheJobInPortrait];
else
[self doTheJobInLandscape];
// For the cases where device is face up
[NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:#selector(hackForFaceUpLandscapeDevices) userInfo:nil repeats:NO];
}
-(void)hackForFaceUpLandscapeDevices {
if(UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]))
[self doTheJobInPortrait];
else
[self doTheJobInLandscape];
}
Of course, the job will be done two times, and in face up lanscape mode there will be a short moment where your interface will be in portrait...
If someone find a better solution, I would be glad to know it.
That's why there is often used a 'blank view' at startup, that just initializes your app. When the first view is laoded, you can easily check for the correct device-orientation.
After this you load your device-orientation-specific View. So you have:
One ViewController
One Blank View
Two Views with content (one landscape, one portrait)
I think, e. g. Safari Mobile does it that way.