iPad UINavigationController Custom Bar Background Image - iphone

I am trying to create a UINavigationController with a background Image...
I have the following code at the top of the class where I implement the UINavigationController.
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
UIImage *img = [UIImage imageNamed:#"header.jpg"];
[img drawInRect:CGRectMake(0, 0, self.frame.size.width,self.frame.size.height)];
}
#end
Then, inside my #implementation of my controller in the "viewDidLoad" function, I have the following...
MainViewController *controller = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:controller];
self.navController = nav;
[nav release];
I know I am close, because everything is working almost perfectly, except the image is 150px in height, and it's being squished down to a smaller size (doing a log of self.frame.size.height is giving me 44.0000) but is pushed down a certain number of pixels from the top...
I know I am close, but if anyone could help me out, it would be muchly appreciated.
Thank you,
--d

You really need to actually make your image the same size as your navigation bar. Don't try to do this unless you really have art that was made for a UINavigationBar.
This code will work, so long as the dimensions are correct:
- (void)drawRect:(CGRect)rect {
[img drawInRect:rect];
}
To support different orientations with differently-sized navigation bars, just send -[UIApplication statusBarOrientation] (I've heard from a lot of people that -[UIDevice orientation] doesn't work as expected, but I haven't tried it).

Related

viewDidAppear height is 656 in iPhone 5

I am getting a height of 656 in my viewDidAppear on a view controller. This is not the correct height that I am expecting from an iPhone 5. Any idea why this might be happening? I have a UIView that I added as a subview of this UIViewController and I set the struts such that it will always stick to the bottom of the UIViewController. However because the height is 656, I can no longer see this UIView. ANy idea why?
Here's my code:
- (void) viewDidAppear:(BOOL) animated
{
[super viewDidAppear:animated];
NSLog(#"%f", self.view.bounds.size.height);
}
This is how I am initializing the VC:
SavedViewController *savedStoriesVC = [[SavedViewController alloc] initWithNibName:#"SavedViewController" bundle:nil];
savedStoriesVC.managedObjectContext = managedObjectContext;
savedStoriesVC.delegate = self;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:savedStoriesVC];
navController.navigationBarHidden = YES;
I just had this same problem. I was able to "fix" it by turning off all of the struts and springs. But then, of course, rotations didn't work.

UIPopoverController and UINavigationController cuts corners

I have a problem with the display of my popover. After initWithContentViewController: and presentPopoverFromBarButtonItem:permittedArrowDirections:animated: it cuts corners of the navigation bar. How should I fix it?? Thanks.
This is the code I'm using
NavContr *nav = [NavContr new];
nav.navigationBar.backgroundColor = [UIColor redColor];
UIPopoverController *tempPop = [[UIPopoverController alloc] initWithContentViewController:nav];
[tempPop presentPopoverFromBarButtonItem:mainButtonItem permittedArrowDirections:UIPopoverArrowDirectionUp animated:NO];
EDIT: I have resolved this problem:
+ (void)configure:(UINavigationController *)navController {
UINavigationBar *navigationBar = navController.navigationBar;
UIView *contentView = nil;
for (UIView *view in navController.view.subviews) {
if ([[NSString stringWithFormat:#"%#", [view class]] isEqualToString:#"UILayoutContainerView"])
contentView = view;
}
// setting frame to navigation bar and content view
[navigationBar setFrame:CGRectMake(navigationBar.frame.origin.x, 0, navigationBar.frame.size.width, navigationBar.frame.size.height)];
[contentView setFrame:CGRectMake(contentView.frame.origin.x, 0, contentView.frame.size.width, contentView.frame.size.height + navigationBar.frame.size.height)];
[navController.view bringSubviewToFront:contentView];
for (UIView *customView in contentView.subviews)
customView.frame = CGRectMake(customView.frame.origin.x, customView.frame.origin.y + navigationBar.frame.size.height, customView.frame.size.width, customView.frame.size.height);
[contentView addSubview:navigationBar];
[contentView bringSubviewToFront:navigationBar];
}
This is probably because you have no root view controller, or are otherwise fiddling with the navigation controller in ways it was not meant to be played with. This is how you ought to be setting up the popover:
MyCustomViewController *viewController = [[UIViewController alloc] initWithNibName:#"MyCustomViewController" bundle:nil]; //or storyboard or whatever
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:viewController]; //you should have a root view controller before displaying the popover
tintColor = [UIColor redColor];
UIPopoverController *tempPop = [[UIPopoverController alloc] initWithContentViewController:nav];
[tempPop presentPopoverFromBarButtonItem:mainButtonItem permittedArrowDirections:UIPopoverArrowDirectionUp animated:NO];
There are a few very important things going on here:
Your navigation controller should have a root view controller before you display it.
This code is using a standard UINavigationController instance. According to the documentation, you should not subclass UINavigationController, nor should you try and reinvent the wheel. Apple has created a complex and comprehensive framework, UIKit, that you can use to build amazing apps. If you try and step outside the box, you'll be creating an awful lot of work for yourself without any appreciable benefit.
This is using the tintColor property of the UINavigationBar class. If the tint is insufficient for your UI, you can also set the background image manually (refer to the docs).
If you want to make a popover with a navigation controller, use the built-in UINavigationController class. Don't subclass it and don't reinvent it. To customize the appearance of the navigationBar, use the UI_APPEARANCE_SELECTOR methods in the UINavigationBar class.
I get the solution before add CALayer the UIPopOverController shows like
after adding below lines in table view class i get the following UIPopOverController
#import <QuartzCore/QuartzCore.h>
CALayer *imageLayer2 = self.tableView.layer;
[imageLayer2 setCornerRadius:-20];
[imageLayer2 setBorderWidth:1];
Try it in your project may be it works!!
Thanx
I have tried & replicate the issue you are facing, made some R&D. It's due to the line of code below :
nav.navigationBar.backgroundColor = [UIColor redColor];
While you set the background color of the navigation bar it will behave weird due the native shape of the pop up. Try and remove the below line, you will definitely have issue resolved.
If you are specifying the Rect where the popover appears, we've found that using decimals can result in weird distortions like that. Be sure you're using whole number for origin and size.

Help! Adding image to UIViewController Transition! :S

I currently have several view controllers and transitions set up throughout my app using:
ViewController2 *controller2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
controller2.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:controller2 animated:YES];
[controller2 release]; controller2 = nil;
What I really want is when the vertical transition is made, I want an image of bubbles to travel up the screen with the transition. Is there anyway of adding an Image to these transitions. If not I would like to know how to create this effect as I have seen it in apps before.
Thanks in Advance,
Adam
Simple way would be to add a UIImageView the size of the view being animated, with your bubbles image set to it as a subview of that view. You can then remove it in the viewDidUnload or where appropriate in your code.

iPhone: Weird space at the top of UINavigationController

I'm having a strange problem with adding a UINavigationController to my iPhone application. I add the controller as follows:
myViewController *viewController = [[myViewController alloc] initWithNibName:#"myView" bundle:nil];
myNavigationViewController *navigationController = [[myNavigationViewController alloc] initWithRootViewController:viewController];
UIView *finalView = myeNavigationViewController.view;
[self.view addSubview:finalView];
All seems to work as planned except I get a weird white space at the top of my view between the status bar and the UINavigationController title bar.
alt text http://www.andrewskinner.name/problem.png
I've searched online but don't really know what to search for. Has anyone else had this problem? Can you point me in the direction of some help?
Thanks in advance.
What does the line
UIView *finalView = myeNavigationViewController.view;
add to the code? It's redundant as you can add the view directly without assigning it to a UIView first - plus it's incorrect as it references the myNavigationController and not navigationController..
I tend to do this
myViewController *viewController = [[myViewController alloc] initWithNibName:#"myView" bundle:nil];
myNavigationViewController *navigationController = [[myNavigationViewController alloc] initWithRootViewController:viewController];
[navigationController.view setFrame: [self.view bounds]];
navigationController.delegate = self;
[self.view addSubview:[navigationController view]];
Setting the frame to the bounds also removes the white space at the top you were asking about.
Check out the answers in this question:
Not sure why UIView is being nudged up by around 10px
The issue is that UINavigationController ideally should be the direct subView of UIWindow. It will position and size right by itself. When you add UINavigationController into another custom view of a UIWindow subview, you need to take care of the position and size of this custom view by taking into account whether the status bar is shown or not in the UIWindow.
My suggestion is to make the custom view as a subclass of UINavigationController:
mySubClass_NavigationController*nav=[[mySubClass_NavigationController alloc] initWithRootViewController:viewController ];
[myUIWindow addSubview:nav.view];
and inside the mySubClass_NavigationController, you can do all the customization that you are doing now in your self (whatever that controller is).
I struggled with this for a while too using very similar code to the op's and also had a white bar above my navigation controller.
My problem occurred when adding the UINavigationController as a view in a UITabController. The space in my case was caused by the UINavigationBar part of the UINavigationController taking into account the status bar and it was actually overlapping part of the view that I was trying to show in the UINavigationController.
This is the code I ended up with in loadView in one of my UITabBarController view controllers.
SomeUITableViewController *screenList = [[SomeUITableViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc]
initWithRootViewController:screenList];
CGRect frame = [[navController navigationBar] frame];
frame.origin.y = 0; // Was 20, set to 0 to not take into account the status bar.
[[navController navigationBar] setFrame:frame];
[self setView:[navController view]];
There's some more information at http://discussions.apple.com/message.jspa?messageID=7890362.
There is an obscure property in IB called "Hides Bottom Bar on Push". Just check it. It solved the problem for me.
Maybe you have somehow gotten yourself two UIViews,
each with a status bar. Check the xib.

Custom UINavigationBar Background

I've been trying to set up a custom background for the whole of my NavigationBar (not just the titleView) but have been struggling.
I found this thread
http://discussions.apple.com/thread.jspa?threadID=1649012&tstart=0
But am not sure how to implement the code snippet that is given. Is the code implemented as a new class? Also where do I instatiate the UINavigationController as I have an application built with the NavigationView template so it is not done in my root controller as per the example
Uddhav and leflaw are right. This code works nicely:
#interface CustomNavigationBar : UINavigationBar
#end
#implementation CustomNavigationBar
-(void) drawRect:(CGRect)rect
{
UIImage *image = [UIImage imageNamed: #"myNavBarImage"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
// this can go anywhere
+(UINavigationController*) myCustomNavigationController
{
MyViewController *vc = [[[MyViewController alloc] init] autorelease];
UINavigationController *nav = [[[NSBundle mainBundle] loadNibNamed:#"CustomNavigationController" owner:self options:nil] objectAtIndex:0];
nav.viewControllers = [NSArray arrayWithObject:vc];
return nav;
}
You have to create CustomNavigationController.xib and put a UINavigationController in it and change the navigationBar class to "CustomNavigationBar".
You must use the 'appearance' proxy to change the background and other styling properties of controls such as UINavigationBar, UIToolBar etc. in iOS 5.xx. However, these are not available for iOS 4.xx so for backwards compatibility, you need a hybrid solution.
If you want to support both iOS 4.xx and iOS 5.xx devices (i.e. your DeploymentTarget is 4.xx), you must be careful in wrapping the call to the appearance proxy by checking at runtime if the 'appearance' selector is present or not.
You can do so by:
//Customize the look of the UINavBar for iOS5 devices
if ([[UINavigationBar class]respondsToSelector:#selector(appearance)]) {
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"NavigationBar.png"] forBarMetrics:UIBarMetricsDefault];
}
You should also leave the iOS 4.xx workaround that you may have implemented. If you have implemented the drawRect workaround for iOS 4.xx devices, as mentioned by #ludwigschubert, you should leave that in:
#implementation UINavigationBar (BackgroundImage)
//This overridden implementation will patch up the NavBar with a custom Image instead of the title
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"NavigationBar.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
This will get the NavBar look the same in both iOS 4 and iOS 5 devices.
You just have to overload drawRect like that :
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"NavigationBar.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
Implementing a category is not advisable. iOS5 may provide relief for this issue. But for old APIs, you can -
Subclass UINavigationBar to say CustomNavBar and implement the custom drawRect from Lithium's answer.
For all IB based UINavigationControllers, provide CustomNavBar as custom class for their UINavigationBar.
For all code based UINavigationControllers. Create a XIB with a UINavigationController and do step two. Then provide a factory method in code that loads the UINavigationController from the nib and provide an IBOutlet.
Eg.
[[NSBundle mainBundle] loadNibNamed:#"CustomNavigationController" owner:self options:nil];
UINavigationController *navController = self.customNavigationController;
navController.viewControllers = [NSArray arrayWithObject:controller]
You can also override the drawLayer:inContext: method in a UINavigationBar category class. Inside the drawLayer:inContext: method, you can draw the background image you want to use.
- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
if ([self isMemberOfClass:[UINavigationBar class]] == NO) {
return;
}
UIImage *image = (self.frame.size.width > 320) ?
[UINavigationBar bgImageLandscape] : [UINavigationBar bgImagePortrait];
CGContextClip(context);
CGContextTranslateCTM(context, 0, image.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), image.CGImage);
}
And as a complete demo Xcode project on customizing the appearance of UINavigationBar this and this might be helpful.
Implementing a category won't work in iOS5, you should use Uddhav Kambli's advice for using CustomNavbar on iOS ≤ 5.
I just found this blog entry, describing this topic very simple: http://web0.at/blog/?p=38
it helped me a lot, they use the "drawRect" method to get the customisation of the background.
To all those who are having trouble with UINavigationBar custom backgrounds in iOS5, do this in the corresponding viewDidLoad methods:
#if defined(__IPHONE_5_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_5_0
if ([self.navigationController.navigationBar respondsToSelector:#selector( setBackgroundImage:forBarMetrics:)]){
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"TitleBar"] forBarMetrics:UIBarMetricsDefault];
}
#endif
Notice that in my case, the background image was named "TitleBar". You can put whatever your custom background image name is.
The problem you'll have is that if you use a navigation controller, the title of each page will overlay your custom navbar. If your navbar contains a logo or the name of your app, this is obviously unacceptable.
You could set the title of each view in your navigation stack to blank, but some views force a title that you can't do anything about (like the photo picker). So you might want to create an alternate navbar image with the same color or design as your logo navbar, but with a blank area to make room for overlaid titles.
To switch navbar images at will, add a property to your app delegate to hold the name of the navbar image and replace the first line of the first example above with these two:
YourAppDelegate* theApp = (YourAppDelegate*)[[UIApplication sharedApplication] delegate];
UIImage* image = [UIImage imageNamed:theApp.navBarName];
Then in the first view controller that you'll push onto the navigation stack, do something like this:
- (void)viewWillAppear:(BOOL)animated
{
YourAppDelegate* theApp = (YourAppDelegate*)[[UIApplication sharedApplication] delegate];
theApp.navBarName = #"navBar_plain";
}
Then in the root view controller, do the same thing but specify your logo-bearing navbar image, so it gets restored when the user navigates back to it and there is no conflicting title.
Another approach is to Use UINavigationController's delegate.
It doesn't require subclassing/overwriting the UINavigationBar class:
/*
in the place where you init the navigationController:
fixer = [[AENavigationControllerDelegate alloc] init];
navigationController.delegate = fixer;
*/
#interface AENavigationControllerDelegate : NSObject <UINavigationControllerDelegate>
#end
#implementation AENavigationControllerDelegate
#define bgImageTag 143
- (void)navigationController:(UINavigationController *)navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
//this is for the future for implementing with the appearance api:
if ([[navigationController navigationBar] respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)])
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[navigationController navigationBar] setBackgroundImage:[UIImage imageNamed:#"header-logo-bg.png"]
forBarMetrics:UIBarMetricsDefault];
});
}
else
{
UIImageView* imageView = (UIImageView*)[navigationController.navigationBar viewWithTag:bgImageTag];
if(!imageView)
{
UIImage *image = [UIImage imageNamed:#"header-logo-bg.png"];
imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
imageView.tag = bgImageTag;
}
[navigationController.navigationBar insertSubview:imageView atIndex:0];
}
}
#end
https://gist.github.com/1184147
In iOS5, zPosition value (of UINavigationBar's most depth layer) is changed. So if you change that zPosition, the old way works.
eg.
UINavigationBar *_bar = navigationController.navigationBar;
// Insert ImageView
UIImage *_img = [UIImage imageNamed:#"navibar.png"];
UIImageView *_imgv = [[[UIImageView alloc] initWithImage:_img] autorelease];
_imgv.frame = _bar.bounds;
UIView *v = [[_bar subviews] objectAtIndex:0];
v.layer.zPosition = -FLT_MAX;
_imgv.layer.zPosition = -FLT_MAX+1;
[_bar insertSubview:_imgv atIndex:1];
This script handle view's layer, so You should import QuartzCore.
Here is an alternative solution that lets you use your own custom subclass of UINavigationBar:
https://gist.github.com/1253807
As Apple itself has said, it is not correct to override methods in Categories. So the best way to customize the background of UINavigarionBar is subclassing and override -(void)drawInRect: method.
#implementation AppNavigationBar
- (void)drawRect:(CGRect)rect
{
UIImage *patternImage = [UIImage imageNamed:#"image_name.png"];
[patternImage drawInRect:rect];
}
To use this customized UINavigationBar it should be set as navigationBar property of your UINavigationBarController. As you know this property is readonly. So what should be done is:
- (void)viewDidLoad
{
[super viewDidLoad];
AppNavigationBar *nav = [AppNavigationBar new];
[self setValue:nav forKey:#"navigationBar"];
}
It works for both iOS 5 and 4.3.
You can subclass UINavigationBar and enable it like this, since categories for drawRect won't work in iOS5 anymore
navigationController = [[((^ {
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:[NSKeyedArchiver archivedDataWithRootObject:navigationController]];
[unarchiver setClass:[SAPHUINavigationBar class] forClassName:#"UINavigationBar"];
[unarchiver setClass:[UIViewController class] forClassName:NSStringFromClass([navigationController.topViewController class])];
return unarchiver;
})()) decodeObjectForKey:#"root"] initWithRootViewController:navigationController.topViewController];
For a static view (no animation at all), I use the default iOS setBackgroundImage
But when I have a view that's animated (resize most likely), I create a custom UIImageView and add it to the navigationBar so that I have more flexibility over it
The thing is if you just add it, it will get on top of the buttons and the titleView, so I manually save a copy of most of subviews, remove them from parent view, add my imageView and than add all the subviews back
This works for me
UIImageView *navBackground = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"navigationBackgroundSample.jpg"]];
UIView *tempTitleView = [[self.navigationBar.subviews objectAtIndex:1] autorelease];
[[self.navigationBar.subviews objectAtIndex:1] removeFromSuperview];
[self.navigationBar addSubview:navBackground];
[self.navigationBar addSubview:tempTitleView];
self.navigationBar.clipsToBounds = YES;
[navBackground release];
In this case, I don't have buttons and I found out that my titleView is at index 1, if you have buttons, they should be around somewhere in the subviews array of navigationBar
I don't know what's at index 0, I don't know if this can work around the case you have text title neither...