Preventing Sensitive Information From Appearing In The Task Switcher - Apple Code Not Working - iOS 8 glitch? - background

This document: Preventing Sensitive Information From Appearing In The Task Switcher describes a way to present a view controller in applicationDidEnterBackground so as to hide critical information in the task switcher:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Your application can present a full screen modal view controller to
// cover its contents when it moves into the background. If your
// application requires a password unlock when it retuns to the
// foreground, present your lock screen or authentication view controller here.
UIViewController *blankViewController = [UIViewController new];
blankViewController.view.backgroundColor = [UIColor blackColor];
// Pass NO for the animated parameter. Any animation will not complete
// before the snapshot is taken.
[self.window.rootViewController presentViewController:blankViewController animated:NO completion:NULL];
}
Yet, in iOS 8, this exact code does not work, and the very simple, plain, black view controller is not shown until after the app becomes active again. The task switcher shows the sensitive information and nothing is hidden. There are no animations in this code, so I cannot understand - why is this happening?

In iOS 8 the app is not being given enough time to display the view controller before the screenshot is taken.
The fix that works for me is to just introduce a small run loop run at the end of applicationDidEnterBackground.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIViewController *blankViewController = UIViewController.new;
blankViewController.view.backgroundColor = UIColor.blackColor;
[self.window.rootViewController presentViewController:blankViewController animated:NO completion:NULL];
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}

Related

React Native get notified just before app minimizes but still active

I need a way to hide sensitive information in a react native app so if you minimize the app and leave your phone unlocked the snapshot of the app in the multitasking view would be blurred and the navigation stack would be switched back to login screen when the app becomes active again.
Even just showing the Login screen just before the app becomes inactive->background would be sufficient but it seems that the AppState's change event is called after the state is already changed from active to inactive and at this point the snapshot is already made and the screen changing occurs after the app is restored. This way the screen with the sensitive data is visible in the multitasking.
I know how to make this with ease in native iOS environment but it seems it's not quite that trivial in React Native.
You can't avoid here from the native code.
in iOS:
In your AppDelegate.m file these 2:
1.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Your application can present a full screen modal view controller to
// cover its contents when it moves into the background. If your
// application requires a password unlock when it retuns to the
// foreground, present your lock screen or authentication view controller here.
UIViewController *blankViewController = [UIViewController new];
blankViewController.view.backgroundColor = [UIColor blackColor];
// Pass NO for the animated parameter. Any animation will not complete
// before the snapshot is taken.
[self.window.rootViewController presentViewController:blankViewController animated:NO completion:NULL];
}
And this
2.
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// This should be omitted if your application presented a lock screen
// in -applicationDidEnterBackground:
[self.window.rootViewController dismissViewControllerAnimated:NO completion:NO];
}
Source:
https://developer.apple.com/library/content/qa/qa1838/_index.html
Found this also:
https://github.com/kayla-tech/react-native-privacy-snapshot

Custom UIActivity ViewController Background Image Transparency Doesn't Work

- (UIViewController *)activityViewController
I created a custom UIACtivity that returns a view controller that displays a popup. This allows the user to do some editing before performing the actual activity.
With ios below 8, my background with transparency that looks like an overlay works (I can see my game underneath) but after updating to ios8, the background becomes solid color gray. I checked the UIImageView displaying my overlay image with alpha and it is set to clear. Can someone tell me why the background suddenly becomes solid? I couldn't see the view of my game underneath anymore.
Here's my code:
- (IBAction)didPressShareButton:(id)sender
{
...
[_rootViewController presentViewController:[self getActivityViewController] animated:YES completion:nil];
...
}
The _rootViewController is the main view controller of my application.
The [self getActivityViewController] returns an instance of UIActivityViewController which includes my custom UIActivity for instagram
My InstagramUIActivity overrides this function to return a custom viewcontroller (see attached image)
- (UIViewController *)activityViewController
{
dismissalAC = [[InstagramDismissal alloc]init];
presentationAC = [[InstagramPresentation alloc]init];
instagramVC = [[InstagramViewController alloc]initWithInstagramPhoto:_instagramPhoto];
instagramVC.delegate = self;
if ([instagramVC respondsToSelector:#selector(setTransitioningDelegate:)]) {
instagramVC.transitioningDelegate = self;
}
return instagramVC;
}
dismissalAC and presentationAC are just objects that implement the UIViewControllerAnimatedTransitioning protocol so I could have my own transition animation.
When I return my custom view controller, it pops up but along with it is a view with white background. I don't know why.
try
instagramVC.modalPresentationStyle = UIModalPresentationOverFullScreen;
or
instagramVC.modalPresentationStyle = UIModalPresentationOverCurrentContext;
I encountered this problem in my apps too.
Since iOS 8, Apple forbids subclassing nor customizing the subviews of an UIActivityViewController.
If you did so on your app, the app shows an overlay over your view and an empty gray list without any buttons. In this case, you must kill your app to dismiss the UIActivityViewController.
To replace this behavior, I simply creating a view (either programmatically or from storyboard) with the same layout and you can make it appear from bottom of the screen (with an animation). Ask me some example code if needed.

Hiding the KeyWindow to cause a blank screenshot to be taken

I am trying to prevent the Apple implemmntation of taking a screenshot of the current screen contents when an app suspends into the background. I have found a piece of code that sort of works but it comes with a catch. What it does is that it clears the keywindow on the screen so when the snapshot is taken, it is of a blank screen. This is the code snippet for the functionality:
- (void)applicationWillResignActive:(UIApplication *)application
{
[ UIApplication sharedApplication ].keyWindow.hidden = YES;
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[ UIApplication sharedApplication ].keyWindow.hidden = YES;
}
The trouble with the above code is that when the app returns to the foreground, the view is gone as it has become hidden and i cannot unhide it with a simple
[ UIApplication sharedApplication ].keyWindow.hidden = NO;
in the applicationWillEnterForeground method of the app delegate. Does anyone know of a way to regain back the hidden view once i have hidden it in the background methods? Right now it is a black screen as the view has been hidden. What exactly happens when you hide a keywindow before going to background and then coming back. is that keywindow you hid before no longer the keywindow? Can anyone point me in the correct direction?
Thanks
Just made a demo project and was able to reproduce your issue. Indeed, the keyWindow property of the application is nil when applicationWillEnterForeground: is called.
Many times, your application's delegate will have a reference to its window - this is usually the Xcode default template for many applications. I was able to resolve the issue by calling
self.window.hidden = NO;
Instead of [UIApplication sharedApplication.keyWindow.hidden = NO;. Assuming that, like most of the templates, your application delegate has a window reference.
Another alternative that worked for me is to call [self.window makeKeyAndVisible];.
All this was done on the iOS 6 simulator.
Hope this helps!

IOS FacebooSDK 3.0 FBLoginVIew in modal viewController

I have modal view controller displayed on rightBarButtonItem click. I'm using FbLoginView in this controller as in sample ios-Facebook SDK 3.0 Error 5 When Posting Status Update.
But i'm unable to show modal view controller more than one time.
I tried to release FBLoginView on ViewDidUnload but it always crashes on second atempt to open modal view controller.
Got the same problem and deal with it for couple days already. And finally this is my solution:
if (!FBSession.activeSession.isOpen) {
theLoginView = [[FBLoginView alloc] init];
theLoginView.frame = CGRectOffset(theLoginView.frame,
([[UIScreen mainScreen] bounds].size.width-theLoginView.frame.size.width)/2,
([[UIScreen mainScreen] bounds].size.height-theLoginView.frame.size.height)/2 -50);
theLoginView.delegate = self;
[self.view addSubview:theLoginView];
[theLoginView sizeToFit];
}
//Only close the session when application is terminating, this will save the token information:
- (void)applicationWillTerminate:(UIApplication *)application {
[FBSession.activeSession close];
}
//And keep the FBSession within the app until the user want to logout:
[FBSession.activeSession closeAndClearTokenInformation];
Right now for me its working completely fine. Hope this help.
The FB SDK doesn't seem to like you creating more than one FBLoginView. Maybe you can if you properly terminate the session, but I found it easier just to create the LoginView once and keep it around.
I did this as follows:
1) in my .m modal view controller file, I created a static variable
static FBLoginView* loginView;
2) When loading the modal view controller in my viewDidLoad, instead of
FBLoginView *loginview = [[FBLoginView alloc] initWithPermissions:
[NSArray arrayWithObject:#"status_update"]];
loginview.frame = CGRectOffset(loginview.frame, 10, 10);
I added a check to find if its already initialized, like this:
if (!loginView) {
loginView = [[FBLoginView alloc] initWithPermissions:
[NSArray arrayWithObject:#"status_update"]];
loginView.frame = CGRectOffset(loginView.frame, 10, 10);
}
Beyond that, I just followed the example of FB's HelloFacebook project.
Not pretty code, but it seems to work.
I had the same problem. Try to add something like this:
if(!yourFBLoginView)
{
yourFBLoginView = [FBLoginView alloc] init...];
}
And/or do not forget to close your active session when you dismissing your modalViewController.
if ([[FBSession activeSession] isOpen])
{
[[FBSession activeSession] close];
}
I think the answer for me (a variant of what was said) was only that I needed to have:
[FBSession.activeSession closeAndClearTokenInformation];
in the:
(void)applicationWillTerminate:(UIApplication *)application
function. The problem specifically for me was, while I was testing...I was constantly terminating the app without actually logging out the user without ever destroying the FBSession, so that when I went back into the app to test what I had changed - my Facebook user was still logged in, and thus some of the conditionals were being incorrectly met. I think this is very important for anyone who is testing (and I'm actually thinking that you should have that line in there anyway) to make sure to clear the session every time the application terminates to avoid this problem...I can imagine a scenario where my app just crashed on somebody and now they are reopening it and they experience the crash because the session was never cleared.

How can I display a splash screen for longer on an iPhone?

How can I display a splash screen for a longer period of time than the default time on an iPhone?
Read the Apple iPhone Human Interface Guidelines (HIG). The "splash screen" isn't supposed to be for branding or displaying a logo, it's supposed to look like the default condition of the app so it appears to start up quickly.
Making it stay there for longer would be a violation of the HIG.
The simplest way to do this is to create a UIImageView who's image is your Default.png. In your applicationDidFinishLaunching: method, add that image view to your window, and hide it when you'd like your splash screen to go away.
I needed to do this to block showing a table view until the data was loaded over the network. I used a variation of one I found here:
http://michael.burford.net/2008/11/fading-defaultpng-when-iphone-app.html
In the interface of your App Delegate:
#interface AppDelegate : NSObject
{
UIImageView *splashView;
}
In the implementation:
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// After this line: [window addSubview:tabBarController.view];
splashView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
splashView.image = [UIImage imageNamed:#"Default.png"];
[window addSubview:splashView];
[window bringSubviewToFront:splashView];
// Do your time consuming setup
[splashView removeFromSuperview];
[splashView release];
}
Make sure you have a Default.png in the resources
in your appDelegate , theres a method called applicationDidFinishedLaunching use a sleep function. Pass a digit in the sleep function for the no. of seconds you want to hold screen.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[window makeKeyAndVisible];
[window addSubview:viewController.view];
sleep(5);
return YES;
}
I searched so much for this thing and everybody gave their own complex point of view. I couldn't find a simple way that would just let me do it.
KISS ( Keep it simple and Smart :)
I avoided the actual as its offensive.
Even though it is against the guidelines but if you still want to do this than a better approach rather than sleeping thread will be
//Extend the splash screen for 3 seconds.
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
this way the main thread is not blocked and if it is listening for any notifications and some other network related stuff, it still carries on.
UPDATE FOR SWIFT:
NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow:3))
I did it pretty simply, by having my rootViewController push a modalViewController, loading from "Splash.nib" in a subclass of UIViewController I called "SplashViewController". The exact call was:
- (void) viewDidLoad {
SplashViewController *splashScreen = [[[SplashViewController alloc]
initWithNibName:#"SplashViewController" bundle:nil] autorelease];
[self presentModalViewController:splashScreen animated:NO];
//continue loading while MVC is over top...
When you launch the app, it pops right up, like a splash screen should. Then, the SplashViewController nib is just a full-screen UIImageView with a splash png, 320x480. After a 1-second NSTimer (anything more did seem to get in the way), it fires timerFireMethod, a custom method that just calls
[self dismissModalViewControllerAnimated:YES];
Then the modal VC just slides down and away, leaving my top tableView. The nice thing is, while the MVC is up, the underlying table can continue to load due to the independent nature of modal view controllers. So, I don't think this violates the HIGs, and actually does allow for faster launching. What would you rather look at, a cute picture, or an empty default view (snore)?
Yes, the simplest way is (remember to add your 'default.png' to targets -> [yourProjectName]: launch images in 'xCode'):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[NSThread sleepForTimeInterval:3.0];
}
Make your app take longer to load.
In all seriousness, Paul Tomblin is correct that this usually isn't a good idea. Default.png is a mechanism intended to make your app appear to load faster by holding an "empty" screenshot. Using it for a splash screen is a minor abuse, but intentionally making that splash screen appear for longer than it needs to is almost sick. (It will also degrade your user experience. Remember, every second the splash screen is visible is a second that the user is impatiently staring at your logo, swearing they'll switch to the first decent competitor they can find.)
If you're trying to cover for some sort of secondary loading--for example, if the interface has loaded and you're just waiting to get some data from the network--then it's probably okay, and Ben Gottlieb's approach is fine. I'd suggest adding a progress bar or spinner to make it clear to the user that something really is going on.
simply use sleep(time in seconds); in your applicationDidFinishedLaunching method
Here is my simple splash screen code. 'splashView' is an outlet for a view
that contains an image logo, UIActivityIndicator, and a "Load.." label (added
to my 'MainWIndow.xib' in IB). The activity indicator is set to 'animating' in IB,
I then spawn a separate thread to load the data. When done, I remove the
splashView and add my normal application view:
-(void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:splashView];
[NSThread detachNewThreadSelector:#selector(getInitialData:)
toTarget:self withObject:nil];
}
-(void)getInitialData:(id)obj {
[NSThread sleepForTimeInterval:3.0]; // simulate waiting for server response
[splashView removeFromSuperview];
[window addSubview:tabBarController.view];
}
Inside your AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Sleep is code to stop the slash screen for 5MoreSeconds
sleep(5);
[self initializeStoryBoardBasedOnScreenSize];
return YES;
}
//VKJ
In Xcode 6.3, you can show the launch screen.xib and even put a indicator on it, first it will show the default launch screen it is replaced by the nib so the user doesn't know it changed and then if everything is loaded hide it :-)
func showLaunchScreen() {
// show launchscreen
launchView = NSBundle.mainBundle().loadNibNamed("LaunchScreen", owner: self, options: nil)[0] as! UIView
launchView.frame = self.view.bounds;
self.view.addSubview(launchView)
// show indicator
launchScreenIndicator = UIActivityIndicatorView(frame: CGRectMake(0, 0, 50, 50)) as UIActivityIndicatorView
launchScreenIndicator.center = CGPointMake(self.view.center.x, self.view.center.y+100)
launchScreenIndicator.hidesWhenStopped = true
launchScreenIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.Gray
launchView.addSubview(launchScreenIndicator)
self.view.addSubview(launchView)
launchScreenIndicator.startAnimating()
self.navigationController?.setNavigationBarHidden(self.navigationController?.navigationBarHidden == false, animated: true) //or animated: false
}
func removeLaunchScreen() {
println("remove launchscreen")
self.launchView.removeFromSuperview()
self.launchScreenIndicator.stopAnimating()
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
Write an actual splash screen class.
Here's a freely usable splash screen that I recently posted in my iPhone in Action blog:
http://iphoneinaction.manning.com/iphone_in_action/2009/03/creating-a-splash-screen-part-one.html
The simplest way is to put your application's main thread into a sleep mode for desired period of time. Provided that "Default.png" exists in your application's bundle it will be displayed for as long as the main thread is asleep:
-(void)applicationDidFinishLaunching:(UIApplication *)application {
[NSThread sleepForTimeInterval:5];
window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[window setBackgroundColor:[UIColor yellowColor]];
[window makeKeyAndVisible];
}
As you are already aware, it's a horribly bad idea to do but it should work just fine...
just make the window sleep for some seconds in applicationDidFininshLaunchings method
example: sleep(3)
According to the Apple HIG you should not do that. But if your application needs to do so for definite purpose, you can do:
import <unistd.h> in your AppDelegate.m
Write the following line at the first of the "application didFinishLaunchingWithOptions:" method
sleep(//your time in sec goes here//);
I have done this as below:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"LaunchScreen" bundle:nil];
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"splashView"];
self.window.rootViewController = viewController;
Note: LaunchScreen is the launch story borad and splashView is the storyboardIdentifier
For stylish splash screen tutorial check out this
http://adeem.me/blog/2009/06/22/creating-splash-screen-tutorial-for-iphone/
I agree it can make sense to have a splash screen when an app starts - especially if it needs to get some data from a web site first.
As far as following Apple HIG - take a look at the (MobileMe) iDisk app; until you register your member details the app shows a typical uitableview Default.png before very quickly showing a fullscreen view.
What I did is presented a modalview controller in the initial screen and then dissmiss it after several seconds
- (void)viewDidLoad
{
[super viewDidLoad];
....
saSplash = [storyboard instantiateViewControllerWithIdentifier:#"SASplashViewController"];
saSplash.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentModalViewController: saSplash animated:NO];
}
-(void) dismissSASplash {
[saSplash dismissModalViewControllerAnimated:NO];
}
There are many options already posted here, but I ran into cocoapod today that allows you to display the contents of your LaunchScreen.xib as the initial view controller:
https://github.com/granoff/LaunchScreen (Also see this blog post from the author with more implementation details.)
This seems like a fairly straight-forward way to do this, and better than the vast majority of answers posted here. (Of course it wasn't possible until the introduction of LaunchScreen files in the first place.) It is possible to display an activity indicator (or anything else you want) on top of the view.
As for why you would want to do this, I'm surprised that no one has mentioned that there are often publisher and/or partner requirements around this sort of thing. It's VERY common in games, but advertising-funded applications as well.
Also note that this does act counter to the HIG, but then so does waiting to load any content after your application launches. Remember that the HIG are guidelines, and not requirements.
One final note: It's my personal opinion, that any time an initial screen like this is implemented, you should be able to tap to dismiss it.
Swift 2.0
Use following line in didFinishLaunchingWithOptions: delegate method:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
NSThread.sleepForTimeInterval(5.0)
return true
}
But I recommend this:
Put your image in a UIImageView full screen as a subview on the top of your main view thus covering your other UI. Set a timer to remove it after some seconds (possibly with effects) now showing your application.
import UIKit
class ViewController: UIViewController
{
var splashScreen:UIImageView!
override func viewDidLoad()
{
super.viewDidLoad()
self.splashScreen = UIImageView(frame: self.view.frame)
self.splashScreen.image = UIImage(named: "Logo.png")
self.view.addSubview(self.splashScreen)
var removeSplashScreen = NSTimer.scheduledTimerWithTimeInterval(3.0, target: self, selector: "removeSplashImage", userInfo: nil, repeats: false)
}
func removeSplashImage()
{
self.splashScreen.removeFromSuperview()
}
}
There is default image (default.png) is shown when you start your app.
So you can add a new viewcontroller which will display the that default image in the application didFinishLoading.
So by this logic you display the default image for a bit longer.
Swift version:
Add this line in the AppDelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
NSThread.sleepForTimeInterval(2.0)//in seconds
return true
}