I want to show a status bar in the bottom of the iPhone like the one in Gmail account that is appear to indicate that it is checking mail. I have tried the following solution in this thread
Adding view on StatusBar in iPhone
but the status bar didn't appear, then i used the same code without any modification to show it in the top of the default status bar and it also didn't appear.
i have tried also another solution using MTStatusBarOverlay, when i tried to change its frame to be at the bottom i got a black rectangl in the middle of the screen
any help?
here is the code
// new class i have created
#interface BottomStatusBarOverlay : UIWindow
#end
#implementation BottomStatusBarOverlay
- (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];
self.alpha = 1;
self.hidden = NO;
// 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:#"statusBarBackgroundGrey.png"] stretchableImageWithLeftCapWidth:2.0f topCapHeight:0.0f];
[self addSubview:backgroundImageView];
}
return self;
}
#end
// usage in my view controller in a button action
#implementation MainViewController
-(IBAction)showBottomStatusbar:(id)sender {
BottomStatusBarOverlay *bottomStatusBarOverlay = [[BottomStatusBarOverlay alloc] init];
bottomStatusBarOverlay.hidden = NO;
}
The code you posted shows that your showBottomStatusbar method creates a BottomStatusBarOverlay instance, but you never actually add it as a subview to anything.
I don't use the Gmail app on iPhone. So, I'm not sure what it looks like or how it functions. However, I have create a notification bar in the past that seems similar to what you described. It animates on to the bottom of the screen, shows a message for three seconds, and then slides back off. I accomplished this by adding the bar to the application's window, which will ensure it overlays any view the application is currently showing. You could, however, add the bar to any view that is currently active, if you don't need a global bar within your app. Here's how you get the app's window reference:
UIApplication* app = [UIApplication sharedApplication];
UIWindow* appWin = app.delegate.window;
To animate, you can use animateWithDuration, like so:
[UIView animateWithDuration:0.3
animations:^ {
// However you want to animate on to the screen.
// This will slide it up from the bottom, assuming the
// view's start position was below the screen.
view.frame = CGRectMake(0,
winHeight - viewHeight,
winWidth,
viewHeight);
}
completion:^(BOOL finished) {
// Schedule a timer to call a dismiss method after
// a set period of time, which would probably perform
// an animation off the screen.
dismissTimer = [NSTimer
scheduledTimerWithTimeInterval:3
target:globalMessage
selector:#selector(dismiss)
userInfo:nil
repeats:NO];
}];
Hope this helps.
Related
iOS Developers will surely knows about the issue about status bar and the famous "slide/hamburger/drawer". The issue is well explained here: http://uxmag.com/articles/adapting-ui-to-ios-7-the-side-menu
I'm using MMDrawerController library and it has a nice hack that lets us to create a dummy status bar just above the container view controller. Unfortunately this doesn't work really good. What's the news? The news is that I stumbled upon an app (Tinder) that perfectly solve this mind blowing issue. I've created a gif that perfectly shows what Tinder does.
You need to wait a few seconds for seeing the gif because there's a bug in it and I don't know how to get rid of. Just wait one/two seconds and you will able to see the gif correctly.
Anyway, what Tinder does? When the user taps on the top left menu button and begin to swipe right the status bar fades out neatly. And when the view is revert to the original position the status bar will show up again.
I am both happy and a bit sad for this because this means that a way must be to do it but I really don't know how to implement it (perhaps hacking MMDrawerController). Any help will be so much appreciated.
IMPORTANT
Please pay attention to the fact that the method setStatusBarHidden: will completely hide the status bar, this means that the entire view is with a height -20px. This is obviously not the solution because as you can see from the gif the view is not stretched.
Your main problem is with MMDrawerController. If you'll digg into it you'll find a lot of methods statusbar related such as setShowsStatusBarBackgroundView setStatusBarViewBackgroundColor and more. Something in their code pushes the view up when the statusbar is hidden.
Alternatively you can use another drawer controller or use custom code.
Here's a simple way how to accomplishe this:
ViewControllerA:
-(BOOL)prefersStatusBarHidden
{
return _hidden;
}
- (void)statusHide
{
[UIView animateWithDuration:0.4 animations:^() {[self setNeedsStatusBarAppearanceUpdate];
}completion:^(BOOL finished){}];
}
ViewControllerB: (Container in ViewControllerA)
- (IBAction)move:(UIButton *)sender
{
parent = (ViewController*)self.parentViewController;
parent.hidden = !parent.hidden;
CGRect frame = parent.blueContainer.frame;
if(parent.hidden)
{
frame.origin.x = 150;
}
else
{
frame.origin.x = 0;
}
[UIView animateWithDuration:1 animations:^() {parent.blueContainer.frame = frame;}completion:^(BOOL finished){}];
[parent statusHide];
}
For iOS 6 compatieblty use:
[[UIApplication sharedApplication] setStatusBarHidden:_hidden withAnimation:UIStatusBarAnimationFade];
The table view and other subviews will stay in their location and won't be pushed up.
Edit:
Adding a NavigationBar:
UINavigationController will alter the height of its UINavigationBar to
either 44 points or 64 points, depending on a rather strange and
undocumented set of constraints. If the UINavigationController detects
that the top of its view’s frame is visually contiguous with its
UIWindow’s top, then it draws its navigation bar with a height of 64
points. If its view’s top is not contiguous with the UIWindow’s top
(even if off by only one point), then it draws its navigation bar in
the “traditional” way with a height of 44 points. This logic is
performed by UINavigationController even if it is several children
down inside the view controller hierarchy of your application. There
is no way to prevent this behavior.
Taken from here
You could very simply subclass UINavigationController and create your own navbar to avoid this annoyness.
i don't know if it will sove your problem but i got almost the same effect using the SWRevealViewController project. In the appDelegate I've set the delegate method from this class to do this:
- (void)revealController:(SWRevealViewController *)revealController willMoveToPosition:(FrontViewPosition)position {
#ifdef DEBUG
NSArray *teste = #[#"FrontViewPositionLeftSideMostRemoved",#"FrontViewPositionLeftSideMost",#"FrontViewPositionLeftSide",#"FrontViewPositionLeft",#"FrontViewPositionRight",#"FrontViewPositionRightMost",#"FrontViewPositionRightMostRemoved"];
NSLog(#"%# %d", teste[position], position);
#endif
if (position == FrontViewPositionRight)
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
UINavigationController *frontViewController = (id)revealController.frontViewController;
frontViewController.navigationBar.centerY += (position == FrontViewPositionRight) ? 20 : 0; // 20 == statusbar heihgt
}
- (void)revealController:(SWRevealViewController *)revealController didMoveToPosition:(FrontViewPosition)position {
if (position == FrontViewPositionLeft)
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
}
centerY is a category in the UIView which sets the center.y without dealing the boring part of setting frame variables.
Here is how you should do that in iOS 7:
#implementation ViewController
{
BOOL _hideStatusBar;
}
-(UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleDefault;
}
-(UIStatusBarAnimation)preferredStatusBarUpdateAnimation
{
return UIStatusBarAnimationFade;
}
-(BOOL)prefersStatusBarHidden
{
return _hideStatusBar;
}
-(void)setStatusBarHidden:(BOOL)hidden
{
[UIView animateWithDuration:1.0 animations:^{
_hideStatusBar = hidden;
[self setNeedsStatusBarAppearanceUpdate];
}];
}
#end
Check out the method setStatusBarHidden:withAnimation: on UIApplication. It will allow you to show or hide the status bar and the animation can be none, fade, or slide. You just need to add a call to hide the bar and one to show the bar at the correct times and decide if you like the fade as you illustrated or if the slide works better for you.
https://developer.apple.com/library/ios/DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/occ/instm/UIApplication/setStatusBarHidden:withAnimation:
You can used -setStatusBarHidden:withAnimation: if you adjust your views frame in -viewDidAppear:, then you will not see any stretch.
Note that autolayout is disabled.
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGRect frame = self.view.frame;
// adjust root view frame
frame.origin.y -= 20;
frame.size.height += 20;
[self.view setFrame:frame];
// adjust subviews y position
for (UIView *subview in [self.view subviews])
{
CGRect frame = subview.frame;
frame.origin.y += 20;
[subview setFrame:frame];
}
}
- (IBAction)sliderChanged:(id)sender
{
UISlider *s = (UISlider *)sender;
if (s.value > .5)
{
UIApplication *app = [UIApplication sharedApplication];
if (![app isStatusBarHidden])
[app setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
}
else
{
UIApplication *app = [UIApplication sharedApplication];
if ([app isStatusBarHidden])
[app setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
}
}
I'm running into a bit of a weird problem when hiding the status bar after the view had loaded. If I add the following method in the ViewDidLoad method, the status bar is completely removed from the view:
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
However, if I call this method in an IBAction or another method, the status bar still slides away but leaves a black bar the same height as itself behind.
I've thought about shifting the entire view up by 20px but is this really a fix? I don't want to just overlap a black bar incase the status bar height changes in future OS upgrades.
Hard-coding any number is always counter to future proofing. Your concerns are correct. There is a bit of a trick to properly handling the hiding of the statusBar. But all the needed information is available.
For example the UIApplication singleton has a property named statusBarFrame which is precisely what it sounds like, a CGRect of the statusBar's frame. The cool thing is that once you have called setStatusBarHidden:withAnimation: that property will give you the new frame, even before the animation completes. So really you are simply left with some basic math to adjust the view's frame.
In short your gut feeling is correct; always compute things live.
I've had good success with a category method like this. (Even when toggling in-call status bar in simulator(Command - T)):
#implementation UIApplication (nj_SmartStatusBar)
// Always designate your custom methods by prefix.
-(void)nj_setStatusBarHidden:(BOOL)hidden withAnimation:(UIStatusBarAnimation)animation{
UIWindow *window = [self.windows objectAtIndex:0];
UIViewController *rootViewController = window.rootViewController;
UIView *view = rootViewController.view;
// slight optimization to avoid unnecassary calls.
BOOL isHiddenNow = self.statusBarHidden;
if (hidden == isHiddenNow) return;
// Hide/Unhide the status bar
[self setStatusBarHidden:hidden withAnimation:animation];
// Get statusBar's frame
CGRect statusBarFrame = self.statusBarFrame;
// Establish a baseline frame.
CGRect newViewFrame = window.bounds;
// Check if statusBar's frame is worth dodging.
if (!CGRectEqualToRect(statusBarFrame, CGRectZero)){
UIInterfaceOrientation currentOrientation = rootViewController.interfaceOrientation;
if (UIInterfaceOrientationIsPortrait(currentOrientation)){
// If portrait we need to shrink height
newViewFrame.size.height -= statusBarFrame.size.height;
if (currentOrientation == UIInterfaceOrientationPortrait){
// If not upside-down move down the origin.
newViewFrame.origin.y += statusBarFrame.size.height;
}
} else { // Is landscape / Slightly trickier.
// For portrait we shink width (for status bar on side of window)
newViewFrame.size.width -= statusBarFrame.size.width;
if (currentOrientation == UIInterfaceOrientationLandscapeLeft){
// If the status bar is on the left side of the window we move the origin over.
newViewFrame.origin.x += statusBarFrame.size.width;
}
}
}
// Animate... Play with duration later...
[UIView animateWithDuration:0.35 animations:^{
view.frame = newViewFrame;
}];
}
#end
Why are you calling this in viewDidLoad?
Try it in loadView?
- (void)loadView {
[super loadView];
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
}
Yes, moving the view 20 pixels should fix your problem. The black is absence of anything to display, not an actual black bar.
As for potential status height changes, this fix will not work if that happens because the view will be moved by the height of the new status bar. If that happens, you will have to either add different offsets for different status bars, or find a completely new solution.
In viewWillAppear:
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
In viewDidAppear, you can insert:
self.view.window.rootViewController.view.frame = [UIScreen mainScreen].applicationFrame;
In viewWillDisappear:
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
I was able to fix this issue by calling:
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
as others recommended before presenting the view controller that is displaying the black bar.
For example, if I had an action that presented ViewController, I would call it like so:
- (IBAction)presentViewController:(id)sender {
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
ViewController *vc = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self presentViewController:vc animated:YES completion:nil];
}
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.
I'm writing an iPhone app that (like most apps) supports auto-rotation: You rotate your phone, and its views rotate and resize appropriately.
But I am assigning a custom view to navigationItem.titleView (the title area of the navigation bar), and I can't get that view to resize correctly when the phone rotates.
I know what you're thinking, "Just set its autoresizingMask to UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight," but it's not that simple. Of course, if I don't set my view's autoresizingMask, then my view doesn't resize; and I want it to resize.
The problem is, if I do set its autoresizingMask, then it resizes correctly as long as that view is visible; but the titleView's size gets messed up in this scenario:
Run the app, with the phone held in portrait mode. Everything looks good.
Do something that causes the app to push another view onto the navigation stack. E.g. click a table row or button that causes a call to [self.navigationController pushViewController:someOtherViewController animated:YES].
While viewing the child controller, rotate the phone to landscape.
Click the "Back" button to return to the top-level view. At this point, the title view is messed up: Although you are holding the phone in landscape mode, the title view is still sized as if you were holding it in portrait mode.
Finally, rotate the phone back to portrait mode. Now things get even worse: The title view shrinks in size (since the navigation bar got smaller), but since it was already too small, now it is much too small.
If you want to reproduce this yourself, follow these steps (this is a bit of work):
Make an app using Xcode's "Navigation-based Application" wizard.
Set it up so that the top-level table view has rows that, when you click them, push a detail view onto the navigation stack.
Include this code in both the top-level view controller and the detail view controller:
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
Include this code in only the top-level view controller:
- (void)viewDidLoad {
[super viewDidLoad];
// Create "Back" button
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Master"
style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
// Create title view
UILabel* titleView = [[[UILabel alloc] initWithFrame:CGRectMake(0,0,500,38)] autorelease];
titleView.textAlignment = UITextAlignmentCenter;
titleView.text = #"Watch this title view";
// If I leave the following line turned on, then resizing of the title view
// messes up if I:
//
// 1. Start at the master view (which uses this title view) in portrait
// 2. Navigate to the detail view
// 3. Rotate the phone to landscape
// 4. Navigate back to the master view
// 5. Rotate the phone back to portrait
//
// On the other hand, if I remove the following line, then I get a different
// problem: The title view doesn't resize as I want it to when I:
//
// 1. Start at the master view (which uses this title view) in portrait
// 2. Rotate the phone to landscape
titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.navigationItem.titleView = titleView;
}
Finally, follow my repro steps.
So ... am I doing something wrong? Is there a way to make my titleView always resize correctly?
You should also set the contentMode of the UIImageView to get the titleView properly displayed in landscape and/or portrait mode :
imgView.contentMode=UIViewContentModeScaleAspectFit;
The whole sequence: (self is a UIViewController instance)
UIImageView* imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"myCustomTitle.png"]];
imgView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imgView.contentMode=UIViewContentModeScaleAspectFit;
self.navigationItem.titleView = imgView;
[imgView release];
I had something similar - but it was returning (popping) to root view controller. Ultimately, I went with the following for popping:
[[self navigationController] setNavigationBarHidden:YES animated:NO];
[[self navigationController] popViewControllerAnimated:YES];
[[self navigationController] setNavigationBarHidden:NO animated:NO];
And it worked. There may have been a better way but - after all the hours I'd already spent on this issue - this was good enough for me.
I dealt with this same issue by keeping track of the customView's initial frame, then toggling between that and a scaled CGRect of the initial frame in a -setLandscape method on a UIButton subclass. I used the UIButton subclass as navigationItem.titleView and navigationItem.rightBarButtonItem.
In UIButton subclass -
- (void)setLandscape:(BOOL)value
{
isLandscape = value;
CGFloat navbarPortraitHeight = 44;
CGFloat navbarLandscapeHeight = 32;
CGRect initialFrame = // your initial frame
CGFloat scaleFactor = floorf((navbarLandscapeHeight/navbarPortraitHeight) * 100) / 100;
if (isLandscape) {
self.frame = CGRectApplyAffineTransform(initialFrame, CGAffineTransformMakeScale(scaleFactor, scaleFactor));
} else {
self.frame = initialFrame;
}
}
Then in the InterfaceOrientation delegates I invoked the -setLandscape method on the customViews to change their sizes.
In UIViewController -
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self updateNavbarButtonsToDeviceOrientation];;
}
- (void)updateNavbarButtonsToDeviceOrientation
{
ResizeButton *rightButton = (ResizeButton *)self.navigationItem.rightBarButtonItem.customView;
ResizeButton *titleView = (ResizeButton *)self.navigationItem.titleView;
if (self.interfaceOrientation == UIDeviceOrientationPortrait || self.interfaceOrientation == UIDeviceOrientationPortraitUpsideDown) {
[rightButton setLandscape:NO];
[titleView setLandscape:NO];
} else {
[rightButton setLandscape:YES];
[titleView setLandscape:YES];
}
}
(Answering my own question)
I got this working by manually keeping track of the titleView's margins (its distance from the edges of the navigtion bar) -- saving when the view disappears, and restoring when the view reappears.
The idea is, we aren't restoring the titleView to the exact size it had previously; rather, we are restoring it so that it has the same margins it had previously. That way, if the phone has rotated, the titleView will have a new, appropriate size.
Here is my code:
In my view controller's .h file:
#interface MyViewController ...
{
CGRect titleSuperviewBounds;
UIEdgeInsets titleViewMargins;
}
In my view controller's .m file:
/**
* Helper function: Given a parent view's bounds and a child view's frame,
* calculate the margins of the child view.
*/
- (UIEdgeInsets) calcMarginsFromParentBounds:(CGRect)parentBounds
childFrame:(CGRect)childFrame {
UIEdgeInsets margins;
margins.left = childFrame.origin.x;
margins.top = childFrame.origin.y;
margins.right = parentBounds.size.width -
(childFrame.origin.x + childFrame.size.width);
margins.bottom = parentBounds.size.height -
(childFrame.origin.y + childFrame.size.height);
return margins;
}
- (void)viewDidUnload {
[super viewDidUnload];
titleSuperviewBounds = CGRectZero;
titleViewMargins = UIEdgeInsetsZero;
}
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Keep track of bounds information, so that if the user changes the
// phone's orientation while we are in a different view, then when we
// return to this view, we can fix the titleView's size.
titleSuperviewBounds = self.navigationItem.titleView.superview.bounds;
CGRect titleViewFrame = self.navigationItem.titleView.frame;
titleViewMargins = [self calcMarginsFromParentBounds:titleSuperviewBounds
childFrame:titleViewFrame];
}
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Check for the case where the user went into a different view, then
// changed the phone's orientation, then returned to this view. In that
// case, our titleView probably has the wrong size, and we need to fix it.
if (titleSuperviewBounds.size.width > 0) {
CGRect newSuperviewBounds =
self.navigationItem.titleView.superview.bounds;
if (newSuperviewBounds.size.width > 0 &&
!CGRectEqualToRect(titleSuperviewBounds, newSuperviewBounds))
{
CGRect newFrame = UIEdgeInsetsInsetRect(newSuperviewBounds,
titleViewMargins);
newFrame.size.height =
self.navigationItem.titleView.frame.size.height;
newFrame.origin.y = floor((newSuperviewBounds.size.height -
self.navigationItem.titleView.frame.size.height) / 2);
self.navigationItem.titleView.frame = newFrame;
}
}
}
For IOS5 onwards, as this is an old question...This is how I accomplished the same issue with the title text not aligning properly.
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:2 forBarMetrics:UIBarMetricsLandscapePhone];
Tested on ios5/6 sims works fine.
This is what I did:
self.viewTitle.frame = self.navigationController.navigationBar.frame;
self.navigationItem.titleView = self.viewTitle;
The viewTitle is a view created in the xib, it takes the size of the navigationBar and after it has been added the titleView adjust the size to leave room to the back button. Rotations seem to work fine.
I had had same problem, but I seem to get workaround with following code.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIView *urlField = self.navigationItem.leftBarButtonItem.customView;
CGRect frame = urlField.frame;
frame.size.width = 1000;
urlField.frame = frame;
}
In my case, the custom view is a UITextField, but I hope this will help you.
I'm trying to do something that shouldn't be that complicated, but I can't figure it out.
I have a UIViewController displaying a UITableView. I want to present a context menu when the user press on a row. I want this to be a semi-transparent view with labels and buttons.
I could use an AlertView, but I want full control on the format of the labels and buttons and will like to use Interface Builder.
So I created my small view 250x290, set the alpha to .75 and create a view controller with the outlets to handle the different user events.
Now I want to present it.
If I use presentModalViewController two (undesired) things happen
1) the view covers all of the screen (but the status bar).
2) It is semi-transparent, but what I see "behind" it its not the parent view but the applications root view.
Ive tried adding it as a subview, but nothing happens, so Im not doing something right:
RestaurantContextVC* modalViewController = [[[RestaurantContextVC alloc] initWithNibName:#"RestaurantContextView" bundle:nil] autorelease];
[self.view addSubview:modalViewController.view];
Is it possible to do what I want?
Thanks in advance.
Gonso
I'm coding similar thing. My approach include.....
Not using dismissModalViewControllerAnimated and presentModalViewController:animated.
Design a customized full sized view in IB. In its viewDidLoad message body, set the background color to clearColor, so that space on the view not covered by controllers are transparent.
I put a UIImageView under the controllers of the floating view. The UIImageView contains a photoshoped image, which has rounded corners and the background is set to transparent. This image view serves as the container.
I uses CoreAnimation to present/dismiss the floating view in the modal view style: (the FloatingViewController.m)
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor clearColor]];
[UIView beginAnimations:nil context:nil];
[self.view setFrame:CGRectMake(0, 480, 320, 480)];
[UIView setAnimationDuration:0.75f];
[self.view setFrame:CGRectMake(0, 0, 320, 480)];
[UIView commitAnimations];
}
wangii
Thats pretty much the solution I found.
I load the view with loadNibNamed and then just add it on top with addSubView, like this:
//Show a view on top of current view with a wait indicator. This prevents all user interactions.
-(void) showWaitView{
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"WaitView" owner:self options:nil];
#ifdef __IPHONE_2_1
waitView = [ nibViews objectAtIndex: 0];
#else
waitView = [ nibViews objectAtIndex: 1];
#endif
CGFloat x = self.view.center.x - (waitView.frame.size.width / 2);
CGFloat y = self.view.center.y - (waitView.frame.size.height / 2);
[waitView setFrame:CGRectMake(x,y,waitView.bounds.size.width,waitView.bounds.size.height)];
[self.view addSubview:waitView];
}
Could you elaborate on points 3 and 4?
What I did to give the view the round rect aspect is put it inside a round rect button.
This code will actually allow you to have a small floating view, but if the view is smaller that its parent, the user could interact with the visible part of the parent.
In the end I create my view with the same size, but kept the code just in case.
Gonso
I would strongly consider using a navigation controller to slide in your subview instead of overlaying it. This is the expected model and any small benefit you may think you'll get by doing it your own way will be greatly offset by the principle of (least) surprise.
If you really really have to do it this way, I believe the trick is to add the first table view as a subview of a transparent "holding" view that the view controller maintains. Then add your new sub view as another subview of that.
Again, if you really want to do this, instead of adding a transparent "holding" view, since this pop-up is essentially modal, I would make it a subview directly of the window.
You might want to put in a transparent black shield behind it to prevent touches on the background and focus input on the popup.
But seriously, consider either popping a controller on the stack or using that alert view. Unless you've hired a $$ designer, it's probably not going to look appropriate on the iPhone.
What I did was create a UIViewController on top of my UINavigation controller in my app delegate and made it a property of a singleton object for convenience:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//--- create root navigation controller
self.window.rootViewController = self.navigationController;
//--- create view controller for popups:
popupViewController = [[BaseViewController alloc] init];
popupViewController.view.backgroundColor = [UIColor clearColor];
popupViewController.view.hidden = true; //for rendering optimisation
[self.window addSubview:popupViewController.view];
[AppState sharedInstance].popupViewController = self.popupViewController;
//--- make all visible:
[self.window makeKeyAndVisible];
return YES;
}
At any point in my app, I can then call e.g.
MyViewController * myVC = [[UIViewController alloc] init];
//... set up viewcontroller and its view...
// add the view of the created view controller to the popup view:
[AppState sharedInstance].popupViewController.view.hidden = false;
[[AppState sharedInstance].popupViewController.view addSubview:myVC.view];
The BaseViewController used on the top just inherits from UIViewController and sets up a full-screen view:
//----- in BaseViewController implementation
- (void)loadView {
//------- create root view:
CGRect frame = [[AppState sharedInstance] getScreenFrame];
rootView = [[VCView alloc] initWithFrame:frame];
rootView.backgroundColor = [UIColor whiteColor];
self.view = rootView;
}