Is it possible to implement a smooth transition when the app loads, from the launch image to the first view?
The default behavior is on/off, with an immediate change: the launch image appears, then it instantaneously disappears to let the main view controller take place. I'd like to implement some fading or zooming in or out.
Is this possible?
Thank you!
There's no framework support, but you can get that result if you do it yourself, manually. Depending on what your launch image is, and what your UI looks like, you can do it in different ways, but basically: make your first view controller load and display your default.png image in an image view when it loads up. Then animate a fade out of that image to reveal your actual UI.
Modified Dancreek's answer to do it all in AppDelegate application:didFinishLaunchingWithOptions. I like this because the code is guaranteed to only run at app start, and it's not polluting any of the view controllers.
It's very simple:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// set up your root view and stuff....
//.....(do whatever else you need to do)...
// show the main window, overlay with splash screen + alpha dissolve...
UIImageView *splashScreen = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default.png"]];
[self.window addSubview:splashScreen];
[self.window makeKeyAndVisible];
[UIView animateWithDuration:0.3 animations:^{splashScreen.alpha = 0.0;}
completion:(void (^)(BOOL)) ^{
[splashScreen removeFromSuperview];
}
];
}
You are in luck. I just did this a few min ago. You need a splash screen. An image on your view that is exactly the same as your default image that the device loads. Then in your app have it dismiss with a fade animation called from the viewDidAppear function
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self performSelector:#selector(killSplashScreen) withObject:nil afterDelay:1.0];
}
- (void)killSplashScreen {
[UIView animateWithDuration:0.5 animations:^{splashScreen.alpha = 0.0;} completion:NULL];
}
We often use something called "splashView" to do this. It was written by Shannon Applecline and available under the CC license. You will have to do some Googling to find it.
//
// splashView.h
// version 1.1
//
// Created by Shannon Appelcline on 5/22/09.
// Copyright 2009 Skotos Tech Inc.
//
// Licensed Under Creative Commons Attribution 3.0:
// http://creativecommons.org/licenses/by/3.0/
// You may freely use this class, provided that you maintain these attribute comments
//
// Visit our iPhone blog: http://iphoneinaction.manning.com
//
Related
I have a simple iPhone application that loads very quickly, so the splash screen only displays for a fraction of a second. Is there any way to control how long the splash screen displays? I have searched around, and have not found anything that seems like it would work. Do I have to create a subview with my splash image? How would I control its display time and switch between the subview and the mainview?
While I agree with the views expressed here and in the other question about why you should not "abuse" the default screen, it seems to me quite trivial to achieve this effect:
When starting up, simply put up a view that looks exactly like the splash screen and use an NSTimer to dismiss it. Really quite easy.
// viewDidLoad
[self performSelector:#selector(dismiss)
withObject:nil
afterDelay:yourTimeIntervalInSectons];
// dismiss
[self performSegueWithIdentifier:#"ID" sender:nil];
However, don't have the splash screen come on each time the application becomes active. I once did this for a very specific and useful purpose in the context of my app - but Apple rejected it. Hey, they even called me on Saturday evening to explain it to me.
While I agree with all that's been told here, I had to implement a splash screen with a timer once as well, so here's the code:
- (void)showSplashWithDuration:(CGFloat)duration
{
// add splash screen subview ...
UIImage *image = [UIImage imageNamed:#"Default.png"];
UIImageView *splash = [[UIImageView alloc] initWithImage:image];
splash.frame = self.window.bounds;
splash.autoresizingMask = UIViewAutoresizingNone;
[self.window addSubview:splash];
// block thread, so splash will be displayed for duration ...
CGFloat fade_duration = (duration >= 0.5f) ? 0.5f : 0.0f;
[NSThread sleepForTimeInterval:duration - fade_duration];
// animate fade out and remove splash from superview ...
[UIView animateWithDuration:fade_duration animations:^ {
splash.alpha = 0.0f;
} completion:^ (BOOL finished) {
[splash removeFromSuperview];
}];
}
Just call the function somewhere in your AppDelegate's -applicationDidFinishLaunching:withOptions: method
#asgeo1: code works just fine for me (I've used similar code in several projects). I've added an example project on my Dropbox for your convenience.
Don't do this and/or read why here
iOS Duration of Splash Screen (Default.png)
It does really make no sense to extend the duration of the Default.png.
Now, I completely agree with the above posts that you shouldn't do this, but if you still wish to it can be achieved very easily by adding the following to your AppDelegate.m.
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
sleep(2);
}
The "2" represents how many seconds to sleep for. It will accept values like ".5"
Is it possible to add a UIView on the staus bar of size (320 x 20)? I don't want to hide the status bar, I only want to add it on top of the status bar.
You can easily accomplish this by creating your own window above the existing status bar.
Just create a simple subclass of UIWindow with the following override of initWithFrame:
#interface ACStatusBarOverlayWindow : UIWindow {
}
#end
#implementation ACStatusBarOverlayWindow
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Place the window on the correct level and position
self.windowLevel = UIWindowLevelStatusBar+1.0f;
self.frame = [[UIApplication sharedApplication] statusBarFrame];
// Create an image view with an image to make it look like a status bar.
UIImageView *backgroundImageView = [[UIImageView alloc] initWithFrame:self.frame];
backgroundImageView.image = [UIImage imageNamed:#"statusBarBackground.png"];
[self addSubview:backgroundImageView];
[backgroundImageView release];
// TODO: Insert subviews (labels, imageViews, etc...)
}
return self;
}
#end
You can now, for example in a view controller in your application, create an instance of your new class and make it visible.
overlayWindow = [[ACStatusBarOverlayWindow alloc] initWithFrame:CGRectZero];
overlayWindow.hidden = NO;
Be aware of messing with the window key status by using - (void)makeKeyAndVisible or similar. If you make your main window (the UIWindow in your Application Delegate) loose key status, you will encounter problems with scrolling scrollviews to top when tapping the status bar etc.
I wrote a static library mimicing Reeders status bar overlay, you can find it here: https://github.com/myell0w/MTStatusBarOverlay
It currently supports iPhone and iPad, default and opaque black status bar styles, rotation, 3 different anymation modes, history-tracking and lots of more goodies!
Feel free to use it or send me a Pull Request to enhance it!
All answers looks like working, but in iOS6.0 I have next problems:
1/ Rotations looks bad
2/ Window (status bar is kind of Window) needed rootViewController
I'm using answer from myell0w, but rotate works not good. I've just remove one extra window and using UIWindow from AppDelegate to implement status bar.
May be this solution is ok only for one UIViewController-app...
Ive implemented by the next way:
1/ In ApplicationDelegate:
self.window.windowLevel = UIWindowLevelStatusBar + 1;
self.window.backgroundColor = [UIColor clearColor];
self.window.rootViewController = _journalController;
2/ Create custom UIView and implement all that you need inside:
For an example touchable statusbar:
#interface LoadingStatusBar : UIControl
And easily create and add to your controller view:
_loadingBar = [[LoadingStatusBar alloc] initWithFrame:topFrame];
[self addSubview:_loadingBar];
3/ Some magic when add your controller view (in initWithFrame:)
CGRect mainFrame = self.bounds;
mainFrame.origin.y = 20;
self.bounds = mainFrame;
Your controller view will has 2 views - content view and status bar view. You can show status bar, or hide it when you want.
Frame of content view will be:
_contentView.frame = CGRectMake(0, 20, self.bounds.size.width, self.bounds.size.height);
4/ And one last magic here :)
To detect touches in non touchable area I've used:
-(id)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (point.y < 20) return _loadingBar;
return [super hitTest:point withEvent:event];
}
For now it works fine on iPad/iPhone and all iOS's from 4 to 6.
Just to dismiss the "You cannot do this comments"...
I don't know how but I know it is doable. The Feed reader app called Reeder does that.
As you can see from the screenshot, Reeder puts a small dot on the top right of the screen. When you tap it. The bar will fill the whole statusbar until you tap it again to make it small.
First of all, a big thank you to #Martin Alléus for providing the code for this implementation.
I'm just posting for a problem that I faced and the solution I used, as I believe others might experience the same issue.
If the App is started while an call is in place, the status bar height will be 40 pixels and this means that the custom status bar will be initialized with that height.
But if the call is ended while you are still in the app, the status bar height will remain still 40 pixels and it will look weird.
So the solution is simple: I've used the Notification center to subscribe to the status bar frame change delegate of the app and adjust the frame:
- (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame {
//an in call toggle was done
//fire notification
[[NSNotificationCenter defaultCenter] postNotificationName:kStatusBarChangedNotification object:[NSValue valueWithCGRect:oldStatusBarFrame]];
}
And in the ACStatusBarOverlayWindow we subscribe to the notification:
-(id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// Place the window on the correct level & position
self.windowLevel = UIWindowLevelStatusBar + 1.0f;
self.frame = [UIApplication sharedApplication].statusBarFrame;
self.backgroundColor = [UIColor blackColor];
//add notification observer for in call status bar toggling
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statusBarChanged:) name:kStatusBarChangedNotification object:nil];
}
return self;
}
and our code to adjust the frame:
- (void)statusBarChanged:(NSNotification*)notification {
//adjust frame...
self.frame = [UIApplication sharedApplication].statusBarFrame;
//you should adjust also the other controls you added here
}
The kStatusBarChangedNotification is just a constant I've used for easy referrence, you can simply replace it with a string, or declare the constant globally.
How do I make a top to down or down to top animation when I click the add (plus) button on the iPhone?
If you are looking for the "Built In" API, Alex is correct. And that works perfectly well.
If you are looking to understand how do make your own animations, you have several options in Core Animation
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Introduction/Introduction.html
but a great starting point is simply the UIView animateWithDuration
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html
you should also get to know the CGAffineTransform
putting this all together, here is an example of sliding a view onto screen from the bottom right, having it fade in at the same time. (I dont really know a situation where I would want to do that, but its an example).
I put this in the app delegate so its easy to simply "paste" into a new project. Normally you would not put any views of this nature directly into the window.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UIWindow *w = [[UIWindow alloc]initWithFrame:[[UIScreen mainScreen] bounds]];;
self.window = w; //property defined in the .h file
[w release];
UIView *v = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
v.backgroundColor = [UIColor redColor];
v.center=self.window.center;
v.alpha=0;
[self.window addSubview:v];
//if you ran the code to here, you would see a square in the center of the window.
//now lets move the square off (off screen) with a transform and then animate it back (on screen)
CGAffineTransform t = CGAffineTransformMakeTranslation(160+50, 240+50); //place the view just off screen, bottom right
v.transform=t;
//if you ran the code to here, you wouldnt see anything as the squre is placed just off screen, bottomr right
[UIView animateWithDuration:.5 //make the animation last .5 seconds
delay:.3 //wait .3 seconds before performing the animation
options:UIViewAnimationOptionCurveEaseOut //slow down as it reaches its destination
animations:^
{
v.transform=CGAffineTransformIdentity; //reset any transformation
v.alpha=1.0; //fade it in
}
completion:^(BOOL finished){} //nothing to do when it completes
];
[self.window makeKeyAndVisible];
return YES;
}
I think you're thinking of presentModalViewController; you can use it to present a new screen that slides up from the bottom of the screen.
I was hoping for some help on this as I'm really stuck after trying to fix it for a few days.
Basically, my app has an OpenGL ES animation called levelsView that is displayed as soon as it has opened. Here is the code that starts my animation on the view controller:
- (void)animate
{
levelsView.animationInterval = 1.0 / 60.0;
[levelsView startAnimating];
[levelsView release];
}
The view controller also has a Switch button that lets the user change the animation. The code that displays the other animation is below:
- (IBAction) Switch: (id) sender {
SnowFallViewController* vce = [[[SnowFallViewController alloc] initWithNibName:#"SnowFallViewController"
bundle:nil] autorelease];
[self presentModalViewController:vce animated:YES];
}
Also here is the view for the code that starts the second animation on the second view controller:
- (void)viewDidLoad {
[super viewDidLoad];
CGRect rect = [[UIScreen mainScreen] bounds];
window = [[UIWindow alloc] initWithFrame:rect];
GLViewController *theController = [[GLViewController alloc] init];
self.controller = theController;
[theController release];
GLView *glView = [[GLView alloc] initWithFrame:rect];
[window addSubview:glView];
glView.controller = controller;
glView.animationInterval = 1.0 / kRenderingFrequency;
[glView startAnimation];
[glView release];
[window makeKeyAndVisible];
}
Basically, the problem I'm having is that I cannot get both the animations to work in the same application i.e. when the application is loaded up the first animation works but when the user clicks on the Switch button they only get a blank screen instead of the second animation.
One thing I've noticed is that the second animation will work if I do not start the first animation i.e. if I got in my animation code and delete
[levelsView startAnimating]; then the second animation will work(but obviously the first one will not).
Anyone with any insight on how I can fix this so I can get both animations to work?
Thanks,
Dave
Assuming you're using a CADisplayLink inside your GLView to run your animation loop, it seems from experience that they have mechanisms inside CADisplayLink to make sure that it doesn't just keep issuing calls upon calls if you're not keeping up with the refresh rate. It's more than possible that logic confuses itself a little if you have multiple CADisplayLinks attached at once.
I'd strongly suggest you add:
- (void)viewDidAppear:(BOOL)animated
{
[glView startAnimation];
}
- (void)viewWillDisappear:(BOOL)animated
{
[glView stopAnimation];
}
Those tie into the built-in mechanisms surrounding presenting and dismissing a view controller — the view controller is being told in the first instance when its view did appear (ie, the transition in is finished) and in the second when its view is about to disappear (ie, just before the transition outward begins). By stopping and starting animation on your GL view based on whether your controller is visible you'll save a lot of processing and prevent your disparate OpenGL views from fighting with each other for rendering times.
I have a UIView and a UIController view. My is standard a 320x460 view. In applicationDidFinishLaunching I do:
[window addSubview:[controller view]];
The weird thing is that the UIView goes under the status bar (like there's missing outlet). However, if I rotate iPhone to the side and then back, it shows up ok.
Is this an expected behavior (I bet I can fix it by setting offset) or am I doing smth wrong?
I ran into this issue when displaying a UIViewController via presentModalViewController.
You can get around it by manually resizing the controller's view after the view has appeared:
- (void) viewDidAppear: (BOOL) animated {
//manually adjust the frame of the main view to prevent it from appearing under the status bar.
UIApplication *app = [UIApplication sharedApplication];
if(!app.statusBarHidden) {
[self.view setFrame:CGRectMake(0.0,app.statusBarFrame.size.height, self.view.bounds.size.width, self.view.bounds.size.height - app.statusBarFrame.size.height)];
}
}
I think your problem is that when you add a view to a window, you need to be aware of the state of the status bar and compensate for it:
if showing the status bar :
[controller view].frame = CGRectMake(0, **20**, 320, 460);
else
[controller view].frame = CGRectMake(0, 0, 320, **480**);
this is why IB shows you a dummy status bar.
I add this issue today. It turned out that I had "Wants Full Screen" checked in the ViewController's Attribute inspector.
Turning off "Wants Full Screen" resolved the problem.
Finally, I got to this solution. Works well for both iPhone & iPad:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Allocate view controller and load nib file
if (isIPhone) {
self.mainViewController = [[[tfdMainViewController_iPhone alloc] initWithNibName:#"tfdMainViewController_iPhone" bundle:nil] autorelease];
} else {
self.mainViewController = [[[tfdMainViewController_iPad alloc] initWithNibName:#"tfdMainViewController_iPad" bundle:nil] autorelease];
}
// Offset correction (iPhone bug?)
CGRect r = self.mainViewController.view.frame;
r = CGRectOffset(r, 0, [UIApplication sharedApplication].statusBarFrame.size.height);
[self.mainViewController.view setFrame:r];
[window addSubview: self.mainViewController.view];
[self.window makeKeyAndVisible];
return YES;
}
P.S. For some reason view has correct height:
480 (screen height in iPhone Portrait mode) - 20 (status bar) = 460,
but failed to set vertical offset. It is pretty strange behavior, looks like bug.
Fixes to the window didn't work for me as I had a modal view. A UIModalPresentationCurrentContext modal view. OK, here's what worked for me. I've been searching the web up and down before getting this to work.
I'm unable to move the view.frame from the parent. However in the viewWillAppear I'm able to move it down:
- (void)viewWillAppear:(BOOL)animated
{
// Move down to compensate for statusbar
CGRect frame = parentView.navCon.view.frame;
frame.origin.y = [UIApplication sharedApplication].statusBarFrame.size.height;
parentView.navCon.view.frame = frame;
}
If you are using Interface Builder's "Simulated User Interface Elements", then you also need to make sure that you have set the flag for "Resize View From NIB" in your MainWindow nib.
This appears to be a bug in iOS 5. One fix would be to use wantsFullScreenLayout in whatever view controller needs to present modally and manually layout the view always below the status bar.
http://www.cocoanetics.com/2012/06/radar-view-frame-inconsistency-using-presentviewcontroller-wantsfullscreenlayout-yn/
http://openradar.appspot.com/radar?id=1758406
Are you manually setting the application bar hidden property AFTER adding the subview? I don't imagine this is the case, but if it's set to none when you first load the view it will layout as if there isn't one, and if you then set the status bar to not hidden it will pop up on top of your view.
A possible solution is to use [[controller view] setNeedsLayout]; after adding the subview, or possibly [window layoutSubviews];. I've never had a lot of success using those to fix layout problems, but since it works after a rotation it's worth a shot.
Even me too got the same issue. When we are using some coding for device orientation we have wrote some coding in app delegate or in our view controller. There we need to change the condition to use the orientation return YES or NO. That solved our issue.
I prefer to use UINavigationController to wrap the UIViewController you, after that, set the NavigationBarHidden to the UINavigationController. It's perfect solution cause UINavigationController do handle the height of status bar in iOS 7.
Here is my code and my screen capture.
UINavigationController *wrapNavController = [[UINavigationController alloc] initWithRootViewController:yourViewController] ;
[wrapNavController setNavigationBarHidden:YES] ;
The Xcode 6, iOS7/8 solution is in to uncheck the "Under Top Bars" checkmark in the "Extend Edges" section of the View Controller section of the Attributes Inspector.
For Xamarin.iOS it would be:
if (UIApplication.SharedApplication.StatusBarHidden == false)
{
var statusBarHeight = UIApplication.SharedApplication.StatusBarFrame.Height;
View.Frame = new CoreGraphics.CGRect(0, statusBarHeight, View.Frame.Width, View.Frame.Height - statusBarHeight);
}