i am using UIPresentationController for custom modal presentation for menu
this part is fine , let say i have clicked 'feedback' i am presenting another controller on top of it .
the problem is after dismissing feedback controller the size of menu view become full
my presentation controller class is below
#import "MRMenuPresentationController.h"
#implementation MRMenuPresentationController
#synthesize dimmingView;
-(void)presentationTransitionWillBegin
{
UITapGestureRecognizer * theTapGesture = [UITapGestureRecognizer new];
theTapGesture.numberOfTapsRequired = 1;
theTapGesture.numberOfTouchesRequired = 1;
[theTapGesture addTarget:self action:#selector(handleTap:)];
dimmingView = [UIView new];
[dimmingView addGestureRecognizer:theTapGesture];
dimmingView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.7];
self.dimmingView.frame = self.containerView.bounds;
self.dimmingView.alpha = 0.0;
[self.containerView addSubview:dimmingView];
[self.containerView addSubview:self.presentedView];
// Fade in the dimming view alongside the transition
id<UIViewControllerTransitionCoordinator> transitionCoordinator = self.presentingViewController.transitionCoordinator;
[transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
self.dimmingView.alpha = 1.0;
} completion:nil];
}
-(void)presentationTransitionDidEnd:(BOOL)completed
{
if (!completed) {
[self.dimmingView removeFromSuperview];
}
}
-(CGRect)frameOfPresentedViewInContainerView
{
CGRect frame = self.containerView.bounds;
frame.size.width = frame.size.width - 50 ;
return frame;
}
-(void)dismissalTransitionWillBegin
{
id<UIViewControllerTransitionCoordinator> transitionCoordinator = self.presentingViewController.transitionCoordinator;
[transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
self.dimmingView.alpha = 0.0;
} completion:nil];
}
-(void)dismissalTransitionDidEnd:(BOOL)completed
{
if (completed) {
[self.dimmingView removeFromSuperview];
}
}
please tell what i am missing
Thanks in advance
Related
I am new in iOS and I am facing a problem regarding to create slide out menu.
I searched and found the example but all are for storyboard and which I found in xib are called on AppDelegate Class.
I need to create a Slide out menu in xib which should be call after login.
Thanks in Advance!
The best example of side bar menu is SWRevealViewController. This library is easy to use and its easy to understand.
This below work for me well...try in your code.
_parentView is your custom slide out view and _rightView (UIButton )is used to dismiss these custom slide out view. you just initialize these two property
#property (strong,nonatomic) UIView *parentView;
#property (strong,nonatomic) UIButton *rightView;
-(void)slideView
{
if(![_parentView isDescendantOfView:app.navController.view])
{
[self showMenu];
}
else
{
[self hideMenu];
}
}
-(void)showMenu
{
int width=[[UIScreen mainScreen]bounds].size.width;
int height=[[UIScreen mainScreen]bounds].size.height;
_parentView =[[UIView alloc]initWithFrame:CGRectMake(0-(width-70), 0, width-70, self.navigationController.view.frame.size.height)];
[_parentView setBackgroundColor:[UIColor lightTextColor]];
[app.navController.view addSubview:_parentView];
_rightView=[[UIButton alloc]initWithFrame:CGRectMake(0,80, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-80)];
_rightView.alpha=0.7;
[_rightView addTarget:self action:#selector(hideMenu) forControlEvents:UIControlEventTouchUpInside];
if(_parentView.frame.origin.x <0)
{
[self performSelector:#selector(showSideMenu) withObject:nil afterDelay:0.2];
}
}
-(void)showSideMenu{
if(_parentView.frame.origin.x <0)
{
[UIView animateWithDuration:0.5 animations:^{
[UIView animateWithDuration:0.7 animations:^{
[self.view addSubview:_rightView];
_rightView.backgroundColor=[UIColor blackColor];
}];
CGRect frame = _parentView.frame;
frame.origin.x =0;
_parentView.frame = frame;
CGRect rightFrame=self.view.frame;
rightFrame.origin.x=_parentView.frame.size.width;
self.view.frame=rightFrame;
CGRect fram1=app.navController.navigationBar.frame;
fram1 .origin.x =_parentView.frame.size.width;
app.navController.navigationBar.frame =fram1 ;
} completion:^(BOOL finished) {
}];
}
}
-(void)hideMenu
{
if(_parentView.frame.origin.x == 0)
{
[UIView animateWithDuration:0.3 animations:^{
CGRect frame = _parentView.frame;
frame.origin.x=-_parentView.frame.size.width;
_parentView.frame = frame;
CGRect fram=self.view.frame;
fram .origin.x=0;
self.view.frame =fram ;
CGRect fram1=app.navController.navigationBar.frame;
fram1 .origin.x =0;
app.navController.navigationBar.frame =fram1 ;
} completion:^(BOOL finished) {
[_rightView removeFromSuperview];
[_parentView removeFromSuperview];
}];
}
}
Thank you...!!!
PUSH FROM NORMAL VIEW CONTROLLER TO SW VIEW CONTROLLER
UIStoryboard*Storyboard=[AppDelegate storyBoardType];
SidebarViewController *sidebarmemu =(SidebarViewController*)[Storyboard instantiateViewControllerWithIdentifier:#"SidebarController"];
tbTBC*tabbarController=(tbTBC*)[Storyboard instantiateViewControllerWithIdentifier:#"tbTBCId"];
SWRevealViewController *revealController;
UINavigationController *frontNavigationController;
UINavigationController *rearNavigationController;
frontNavigationController =[[UINavigationController alloc]initWithRootViewController:tabbarController];
rearNavigationController =[[UINavigationController alloc]initWithRootViewController:sidebarmemu];
revealController = [[SWRevealViewController alloc] initWithRearViewController:rearNavigationController frontViewController:frontNavigationController];
revealController.delegate = self;
self.swcontroller =revealController;
self.view.window.rootViewController =self.swcontroller;
WITH THE HELP OF THIS CODE YOU CAN GET BOTH TAB BAR AS WELL AS SIDE BAR
I have a Custom Transition, I apply it in a ViewController which complies with UIViewControllerTransitioningDelegate protocol. My transition works ok. But when i change UIViewControllerTransitioningDelegate protocol to UINavigationControllerDelegate protocol, it works strangely.
The function i want to realize
Strange presentation with only one piece of toView
MyTransitioning:
#import "MyTransitioning.h"
#define PARTS_COUNT 4
#interface MyTransitioning()
#property (nonatomic) CGFloat partWidth;
#property (nonatomic) CGFloat partHeight;
#end
#implementation MyTransitioning
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 1.0;
}
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController* fromCtrl = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController* toCtrl = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView* fromView = fromCtrl.view;
UIView* toView = toCtrl.view;
UIView* container = [transitionContext containerView];
CATransform3D transform = container.layer.transform;
transform.m34 = -0.01;
[container.layer setSublayerTransform:transform];
NSArray<UIView*>* fromViewParts = [self spliteView:fromView];
NSArray<UIView*>* toViewParts = [self spliteView:toView];
[self addViews:toViewParts ToContainer:container isFromView:NO];
[self addViews:fromViewParts ToContainer:container isFromView:YES];
[fromView removeFromSuperview];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^
{
[self strechViews:toViewParts];
[self compressViews:fromViewParts ToLeft:YES];
} completion:^(BOOL finished)
{
if (!finished)
{
NSLog(#"not finish");
}
[self removeViewsfromSuper:fromViewParts];
[self removeViewsfromSuper:toViewParts];
[container addSubview:toView];
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
-(NSArray*)spliteView:(UIView*)view
{
self.partWidth = view.bounds.size.width / PARTS_COUNT;
self.partHeight = view.bounds.size.height;
CGRect region = CGRectMake(0, 0, self.partWidth, self.partHeight);
NSMutableArray<UIView*>* viewParts = [NSMutableArray array];
for (int i = 0; i < PARTS_COUNT; i++)
{
region.origin.x =self.partWidth * i;
UIView* partView = [view resizableSnapshotViewFromRect:region afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
[viewParts addObject:partView];
partView.frame = region;
}
return viewParts;
}
-(void)addViews:(NSArray<UIView*>*)views ToContainer:(UIView*)container isFromView:(BOOL)isFromView
{
isFromView ? [self strechViews:views] : [self compressViews:views ToLeft:NO];
for (UIView* view in views)
{
[container addSubview:view];
}
}
-(void)compressViews:(NSArray<UIView*>*)views ToLeft:(BOOL)toLeft
{
CGFloat x = toLeft ? 0 : self.partWidth * 4;
int isClockWise = -1;
CGPoint position = CGPointMake(x, self.partHeight / 2);
for (UIView* view in views) {
isClockWise *= -1;
view.layer.anchorPoint = CGPointMake(0.5 - isClockWise * 0.5, 0.5);
view.layer.position = position;
view.layer.transform = CATransform3DMakeRotation(M_PI_2 * isClockWise, 0, 1, 0);
}
}
-(void)strechViews:(NSArray<UIView*>*)views
{
int isClockWise = -1;
for (UIView* view in views)
{
isClockWise *= -1;
view.layer.anchorPoint = CGPointMake(0.5 - isClockWise * 0.5, 0.5);
NSLog(#"%#",NSStringFromCGPoint(view.layer.anchorPoint));
CGPoint position = view.layer.position;
position.x = self.partWidth * [views indexOfObject:view] + (isClockWise == 1 ? 0 : self.partWidth);
view.layer.position = position;
view.layer.transform = CATransform3DIdentity;
NSLog(#"%#",NSStringFromCGRect(view.frame));
}
}
-(void)removeViewsfromSuper:(NSArray<UIView*>*)views
{
for (UIView* view in views)
{
[view removeFromSuperview];
}
}
#end
ViewController:
#import "ViewController.h"
#import "MyTransitioning.h"
#import "SecondController.h"
#interface ViewController ()<UINavigationControllerDelegate>
//#interface ViewController ()<UIViewControllerTransitioningDelegate>
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//-(id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
//{
// return [MyTransitioning new];
//}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
self.navigationController.delegate = self;
// SecondController* ctrl = [segue destinationViewController];
// ctrl.transitioningDelegate = self;
}
-(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
if (operation == UINavigationControllerOperationPush) {
return [MyTransitioning new];
}
return nil;
}
#end
If your question is difference between PresentViewController and Push,pop view controller then,
PushViewController
Push View controller is a property of the Navigation controller. When you want to move forward from one controller to other you use this.
PopViewController
Pop view controller is also a property of the navigation controller. When you want to go backward in navigation stack you this.
PresentViewController / Model View controller
Present view controller is not a property of the navigation controller. To perform either push/pop your controller should be subclassed with UINavigationController either directly or indirectly. Whereas to present view controller you don't need to have reference of navigation controller, directly you call [self presentViewController..]
I have one viewcontroler with one button.When i press that one pop up viewcontroller will show up.And when i touch anywhere outside it dismiss the viewcontroller .And return to my main viewcontroller.
Note
I used my popup through one viewcontroller and i use identifier name .Then i show pop up using that storyboard identifier and declared in my viewcontroller.m in btnSelectDatePressed
Needed
in that pop up view controller i have one button when user press also it should dismiss the viewcontroller and should return to my main viewcontroller.My example image is like this sample image
This is my viewcontroller.m file
#import "ViewController.h"
#import "UIViewController+ENPopUp.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)btnSelectDatePressed:(id)sender
{
UIViewController *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"PopUp"];
vc.view.frame = CGRectMake(0, 0, 309.0f, 531.0f);
[self presentPopUpViewController:vc];
}
This is my popup method declared in #import "UIViewController+ENPopUp.h"
#import <UIKit/UIKit.h>
#interface UIViewController (ENPopUp)
#property (nonatomic, retain) UIViewController *en_popupViewController;
- (void)presentPopUpViewController:(UIViewController *)popupViewController;
- (void)presentPopUpViewController:(UIViewController *)popupViewController completion:(void (^)(void))completionBlock;
- (void)dismissPopUpViewController;
- (void)dismissPopUpViewControllerWithcompletion:(void (^)(void))completionBlock;
//- (IBAction)disMe:(id)sender;
#end
This is my popup method declared in `#import "UIViewController+ENPopUp.m"`
#import "UIViewController+ENPopUp.h"
#import "JWBlurView.h"
#import <objc/runtime.h>
static void * ENPopupViewControllerPropertyKey = &ENPopupViewControllerPropertyKey;
static CGFloat const kAnimationDuration = .4f;
static CGFloat const kRotationAngle = 70.f;
static NSInteger const kENPopUpOverlayViewTag = 351301;
static NSInteger const kENPopUpViewTag = 351302;
static NSInteger const kENPopUpBluredViewTag = 351303;
#implementation UIViewController (ENPopUp)
#pragma mark - Public Methods
- (void)presentPopUpViewController:(UIViewController *)popupViewController
{
[self presentPopUpViewController:popupViewController completion:nil];
}
- (void)presentPopUpViewController:(UIViewController *)popupViewController completion:(void (^)(void))completionBlock
{
self.en_popupViewController = popupViewController;
[self presentPopUpView:popupViewController.view completion:completionBlock];
}
- (void)dismissPopUpViewController
{
[self dismissPopUpViewControllerWithcompletion:nil];
}
- (void)dismissPopUpViewControllerWithcompletion:(void (^)(void))completionBlock
{
UIView *sourceView = [self topView];
JWBlurView *blurView = (JWBlurView *)[sourceView viewWithTag:kENPopUpBluredViewTag];
UIView *popupView = [sourceView viewWithTag:kENPopUpViewTag];
UIView *overlayView = [sourceView viewWithTag:kENPopUpOverlayViewTag];
[self performDismissAnimationInSourceView:sourceView withBlurView:blurView popupView:popupView overlayView:overlayView completion:completionBlock];
}
//- (IBAction)disMe:(id)sender {
// UIViewController *vc = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"Pop"];
// [self dismissViewControllerAnimated:YES completion:Nil];
//}
#pragma mark - Getters & Setters
- (UIViewController *)en_popupViewController
{
return objc_getAssociatedObject(self, ENPopupViewControllerPropertyKey);
}
- (void)setEn_popupViewController:(UIViewController *)en_popupViewController
{
objc_setAssociatedObject(self, ENPopupViewControllerPropertyKey, en_popupViewController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark - View Handling
- (void)presentPopUpView:(UIView *)popUpView completion:(void (^)(void))completionBlock
{
UIView *sourceView = [self topView];
// Check if source view controller is not in destination
if ([sourceView.subviews containsObject:popUpView]) return;
// Add overlay
UIView *overlayView = [[UIView alloc] initWithFrame:sourceView.bounds];
overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
overlayView.tag = kENPopUpOverlayViewTag;
overlayView.backgroundColor = [UIColor clearColor];
// Add Blured View
JWBlurView *bluredView = [[JWBlurView alloc] initWithFrame:overlayView.bounds];
bluredView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
bluredView.tag = kENPopUpBluredViewTag;
[bluredView setBlurAlpha:.0f];
[bluredView setAlpha:.0f];
[bluredView setBlurColor:[UIColor clearColor]];
bluredView.backgroundColor = [UIColor clearColor];
[overlayView addSubview:bluredView];
// Make the background clickable
UIButton * dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
dismissButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
dismissButton.backgroundColor = [UIColor clearColor];
dismissButton.frame = sourceView.bounds;
[overlayView addSubview:dismissButton];
[dismissButton addTarget:self action:#selector(dismissPopUpViewController)
forControlEvents:UIControlEventTouchUpInside];
// Customize popUpView
popUpView.layer.cornerRadius = 3.5f;
popUpView.layer.masksToBounds = YES;
popUpView.layer.zPosition = 99;
popUpView.tag = kENPopUpViewTag;
popUpView.center = overlayView.center;
[popUpView setNeedsLayout];
[popUpView setNeedsDisplay];
[overlayView addSubview:popUpView];
[sourceView addSubview:overlayView];
[self setAnimationStateFrom:popUpView];
[self performAppearAnimationWithBlurView:bluredView popupView:popUpView completion:completionBlock];
}
#pragma mark - Animation
- (void)setAnimationStateFrom:(UIView *)view
{
CALayer *layer = view.layer;
layer.transform = [self transform3d];
}
- (CATransform3D)transform3d
{
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DTranslate(transform, 0, 200.f, 0);
transform.m34 = 1.0/800.0;
transform = CATransform3DRotate(transform, kRotationAngle*M_PI/180.f, 1.f, .0f, .0f);
CATransform3D scale = CATransform3DMakeScale(.7f, .7f, .7f);
return CATransform3DConcat(transform, scale);
}
- (void)performAppearAnimationWithBlurView:(JWBlurView *)blurView popupView:(UIView *)popupView completion:(void (^)(void))completionBlock
{
CATransform3D transform;
transform = CATransform3DIdentity;
[UIView animateWithDuration:kAnimationDuration
animations:^ {
[self.en_popupViewController viewWillAppear:NO];
[blurView setAlpha:1.f];
popupView.layer.transform = transform;
}
completion:^(BOOL finished) {
[self.en_popupViewController viewDidAppear:NO];
if (completionBlock != nil) {
completionBlock();
}
}];
}
- (void)performDismissAnimationInSourceView:(UIView *)sourceView
withBlurView:(JWBlurView *)blurView
popupView:(UIView *)popupView
overlayView:(UIView *)overlayView
completion:(void (^)(void))completionBlock
{
CATransform3D transform = [self transform3d];
[UIView animateWithDuration:kAnimationDuration
animations:^ {
[self.en_popupViewController viewWillDisappear:NO];
[blurView setAlpha:0.f];
popupView.layer.transform = transform;
}
completion:^(BOOL finished) {
[popupView removeFromSuperview];
[blurView removeFromSuperview];
[overlayView removeFromSuperview];
[self.en_popupViewController viewDidDisappear:NO];
self.en_popupViewController = nil;
if (completionBlock != nil) {
completionBlock();
}
}];
}
#pragma mark - Getters
- (UIView*)topView {
UIViewController *recentView = self;
while (recentView.parentViewController != nil) {
recentView = recentView.parentViewController;
}
return recentView.view;
}
#end
I am beginner in ios.Some one please give some solution for my problem.Thanks in advance !
I changed the code of UIViewController+ENPopUp.m for have a visible button.
In code existe a button with screen size for dismiss view. I changed this button and now he is visible.
In presentPopUpView UIViewController+ENPopUp.m where have this comment " // Make the background clickable" I change the code to this:
CGFloat buttonSize = 10;
UIButton * dismissButton = [[UIButton alloc] init];
dismissButton.backgroundColor = [UIColor redColor];
dismissButton.frame = CGRectMake((self.view.frame.size.width/2) - (buttonSize/2),(self.view.frame.size.height/2) + (self.view.frame.size.height/4) , buttonSize, buttonSize);
[overlayView addSubview:dismissButton];
[dismissButton addTarget:self action:#selector(dismissPopUpViewController)
forControlEvents:UIControlEventTouchUpInside];
This create a red button 10x10 like in figure:
Now, you can change the color and the size of button for what you want.
I hope this can help you.
I've made a subview to record sound which looks like this:
http://i.stack.imgur.com/EBdEV.png
After the bars faded, it looks like this:
http://i.imgur.com/sDAp4nk.png
I couldn't figure out why. This is the controller. Is there something in the storyboard I could turn on/off?
Thanks for help :)
#import "SubviewController.h"
#implementation SubviewController
#synthesize delegate;
- (void)viewWillAppear:(BOOL)inAnimated {
[super viewWillAppear:inAnimated];
if([self.delegate respondsToSelector:#selector(subviewControllerWillAppear:)]) {
[self.delegate subviewControllerWillAppear:self];
}
}
- (void)viewWillDisappear:(BOOL)inAnimated {
if([self.delegate respondsToSelector:#selector(subviewControllerWillDisappear:)]) {
[self.delegate subviewControllerWillDisappear:self];
}
[super viewWillDisappear:inAnimated];
}
- (void)presentFromViewController:(UIViewController *)inViewController animated:(BOOL)inAnimated {
CGRect theBounds = inViewController.view.bounds;
UIView *theView = self.view;
UIView *theBackgroundView = [[UIView alloc] initWithFrame:theBounds];
CGRect theFrame = theView.frame;
theFrame.origin.x = (CGRectGetWidth(theBounds) - CGRectGetWidth(theFrame)) / 2.0;
theFrame.origin.y = (CGRectGetHeight(theBounds) - CGRectGetHeight(theFrame)) / 2.0;
theView.frame = theFrame;
[inViewController addChildViewController:self];
theBackgroundView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.8];
theBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
theBackgroundView.alpha = 0.0;
[theBackgroundView addSubview:theView];
[inViewController.view addSubview:theBackgroundView];
[UIView animateWithDuration:inAnimated ? 0.75 : 0.0
animations:^{
theBackgroundView.alpha = 1.0;
}
completion:^(BOOL inFinished) {
[self didMoveToParentViewController:inViewController];
}];
}
- (void)dismissAnimated:(BOOL)inAnimated {
UIView *theView = self.view;
UIView *theBackgroundView = theView.superview;
[self willMoveToParentViewController:nil];
[UIView animateWithDuration:inAnimated ? 0.75 : 0.0
animations:^{
theBackgroundView.alpha = 0.0;
}
completion:^(BOOL inFinished) {
[theBackgroundView removeFromSuperview];
[theView removeFromSuperview];
[self removeFromParentViewController];
}];
}
#end
#implementation SubviewSegue
- (void)perform {
[self.destinationViewController presentFromViewController:self.sourceViewController animated:YES];
}
#end
Check your constraints. You probably have one that has a minimum height between 2 subviews and its pushing that toolbar down or something.
Also, a lot of weird things happen when you animate frames while autolayout is on so be careful about that.
I have a requirement to show a status bar at certain times at the bottom of my application. I can easily put this at the bottom of my application's main view, but whenever I push a view controller on top of this (either modally or not) it hides this status bar.
Is there any way I can add a status bar like this, and have it be outside the bounds of my application itself? Ideally I'd like this to work like the call-in-progress status bar on the iPhone - when this bar appears, the app is pushed down, and a call to [[UIScreen mainScreen] applicationFrame] returns the correct size (i.e. it accounts for the presence of this status bar when calculating the height available for the app).
I wanted to do this, too, so I tried View Controller Containment. I'm still trying it out, so I'm not willing to give this a ringing endorsement, but it might be something you'd want to try playing around with yourself if you're in iOS5. But it appears to give you a status bar that will appear or disappear from the bottom of the screen.
This is a view controller that will open another view controller, but if there is status text to show, it pops up from the bottom of the screen and stays there until you get rid of it. I've only done a little testing so far, but it looks like this handles pushViewController/popViewController, but maybe not modal views.
My header looks like:
// StatusBarViewController.h
//
// Created by Robert Ryan on 7/8/12.
#import <UIKit/UIKit.h>
#interface StatusBarViewController : UIViewController
#property (strong, nonatomic) UIViewController *appController;
- (void)setStatus:(NSString *)text;
#end
My implementation file (this is ARC) looks like:
// StatusBarViewController.m
//
// Created by Robert Ryan on 7/8/12.
#import "StatusBarViewController.h"
#interface StatusBarViewController ()
{
BOOL _statusHidden;
UIView *_appView;
UILabel *_statusLabel;
}
#end
#implementation StatusBarViewController
#synthesize appController = _appController;
- (void)dealloc
{
_appView = nil;
_statusLabel = nil;
[self setAppController:nil]; // usually I don't like setters in dealloc, but this does some special stuff
}
- (void)createControlsWithStatusHidden
{
// create default app view that takes up whole screen
CGRect frame = self.view.frame;
frame.origin = CGPointMake(0.0, 0.0);
_appView = [[UIView alloc] initWithFrame:frame];
_appView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_appView.clipsToBounds = YES;
[self.view addSubview:_appView];
// create status label that is just off screen below the app view
_statusLabel = [[UILabel alloc] init];
_statusLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:12.0];
_statusLabel.backgroundColor = [UIColor darkGrayColor];
_statusLabel.textColor = [UIColor whiteColor];
CGSize size = [#"Hey!" sizeWithFont:_statusLabel.font]; // test size of box with random text
_statusLabel.frame = CGRectMake(0.0, frame.size.height, frame.size.width, size.height);
_statusLabel.textAlignment = UITextAlignmentCenter;
_statusLabel.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
[self.view addSubview:_statusLabel];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self createControlsWithStatusHidden];
_statusHidden = YES;
// I'm instantiating from storyboard. If you're using NIBs, just create your controller controller using initWithNib and then set our appController accordingly.
self.appController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainNavigator"];
}
- (void)setAppController:(UIViewController *)controller
{
if (controller)
{
controller.view.frame = CGRectMake(0.0, 0.0, _appView.frame.size.width, _appView.frame.size.height);
[self addChildViewController:controller];
[controller didMoveToParentViewController:self];
if (self.appController)
{
// if we have both a new controller and and old one, then let's transition, cleaning up the old one upon completion
[self transitionFromViewController:self.appController
toViewController:controller
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionCurveEaseInOut
animations:nil
completion:^(BOOL finished){
if (self.appController)
{
[self.appController willMoveToParentViewController:nil];
[self.appController removeFromParentViewController];
}
}];
}
else
{
// if we have no previous controller (i.e. this is our first rodeo), then just add it to the view
[_appView addSubview:controller.view];
}
}
else
{
// no new controller, so we're just removing any old on if it was there
if (self.appController)
{
// if there was an old controller, remove it's view, and remove it from the view controller hierarchy
[self.appController.view removeFromSuperview];
[self.appController willMoveToParentViewController:nil];
[self.appController removeFromParentViewController];
}
}
_appController = controller;
}
- (void)hideStatusWithCompletion:(void (^)(BOOL finished))completion
{
[UIView animateWithDuration:0.25
animations:^{
CGRect labelFrame = _statusLabel.frame;
labelFrame.origin.y += labelFrame.size.height;
_statusLabel.frame = labelFrame;
CGRect appFrame = _appView.frame;
appFrame.size.height += labelFrame.size.height;
_appView.frame = appFrame;
}
completion:completion];
}
- (void)unhideStatusWithCompletion:(void (^)(BOOL finished))completion
{
[UIView animateWithDuration:0.25
animations:^{
CGRect labelFrame = _statusLabel.frame;
labelFrame.origin.y -= labelFrame.size.height;
_statusLabel.frame = labelFrame;
CGRect appFrame = _appView.frame;
appFrame.size.height -= labelFrame.size.height;
_appView.frame = appFrame;
}
completion:completion];
}
- (void)setStatus:(NSString *)text
{
BOOL hasText = (text && [text length] > 0);
if (hasText)
{
if (!_statusHidden)
{
// if we have text, but status is already shown, then hide it and unhide it with new value
[self hideStatusWithCompletion:^(BOOL finished){
_statusLabel.text = text;
[self unhideStatusWithCompletion:nil];
}];
}
else
{
// if we have text, but no status is currently shown, then just unhide it
_statusLabel.text = text;
[self unhideStatusWithCompletion:nil];
}
_statusHidden = NO;
}
else
{
if (!_statusHidden)
{
// if we don't have text, but status bar is shown, then just hide it
[self hideStatusWithCompletion:^(BOOL finished){
_statusLabel.text = text;
}];
_statusHidden = YES;
}
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
And then, any view controller that wants to update the status message would use a method kind of like:
- (void)setStatus:(NSString *)text
{
UIViewController *controller = [UIApplication sharedApplication].delegate.window.rootViewController;
if ([controller isKindOfClass:[StatusBarViewController class]])
{
[(StatusBarViewController *)controller setStatus:text];
}
}