Custom NavigationBar - ios

I have a Navigation Controller as the root of my app and I am using the appearance proxy to customise the look on iOS 5, but for iOS 4 I was hoping to use a category to override drawRect:, this was fine, except that all the Navigation bars were affected as you would expect from a category.
I don't want to tamper with the "system" popups, such as the Mail composer, or the SMS composer, I want their bars to stay blue and system-like.
I Tried to create my own UINavigationController with it's xib and change the class of the NavigationBar to my custom sub lass of UINavigationBar. But the results are not taking affect at all on screen.
I am aware of the following post but couldn't get any solutions to run as expected.
How to subclass UINavigationBar for a UINavigationController programmatically?
My first attempt which does work but uses an undocumented setNavigationBar: method:
_myNavigationController = [[UINavigationController alloc] initWithRootViewController:someVC];
if([[BT_NavigationController class] respondsToSelector:#selector(appearance)]){
// some iOS 5 magic in here !
[_myNavigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"topbar.png"];
}else{
// Probably looking at app store refusal
CustomBar * bar = [[CustomBar alloc] init];
// [_myNavigationController setNavigationBar: bar];
[bar release];
}
[parentView presentModalViewController:_myNavigationController animated:YES];
To avoid that I created a UINavigationController, which I also had a xib for, reassigning the class of the navigation bar to my custom class, but placing breakpoints in drawRect: method, I can see that this isn't being called.
Why would that be, it seems that my code is not loading the nib, and therefore not realising the nab bar should be my custom class and not the UINavigationBar.
Any tips would be helpful, thanks.

If your modification is just about adding an image, you can simply insert it from the view controllers you want to customize:
UIImageView *bgImageView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"topbar.png"]] autorelease];
[self.navigationController.navigationBar addSubview:bgImageView];
You have to tweak a little based on what other elements you have in your UINavigationBar for your UIImageView to be at the lowest index of the subviews but still above the background. Worked for us.
Good luck.

Related

self.navigationItem.titleView does not work with tabbed application template

I am new to developing for iOS, but I am completely stumped with this.
Steps:
In Xcode, create a new tabbed application for iPhone.
Go into first subview and drag a Navigation Bar to the view.
Go into viewDidLoad and add this (assuming you have dropped logo.png into the project structure):
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"logo.png"]];
Render the view - it does not work. No custom image replaces the default "Title" text.
I don't understand. Why does this not work? What do I have to do to make it work? Is there something fundamentally different I need to be doing or a concept I am not grasping fully here?
UPDATE
I have figured out that the code above works. You just need to embed your view inside a navigation controller. Click on the first tabbed view, then do Editor > Embed In > Navigation Controller. The code will then work, and you can continue moving forward. Just embed each tab in a navigation controller using the method above and you should be good to go!
The code you have will work if your controller is embedded in a navigation controller, but if you add a navigation bar manually, you need to make an IBOutlet to it (bar in my example), and get its navigation item,
- (void)viewDidLoad {
[super viewDidLoad];
UINavigationItem *item = self.bar.items[0];
item.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"logo.png"]];
}
May be the problem you are facing is because of not setting the frame. I face similar problem sometimes. Try this:
UIImageView *customTitleView = [[UIImageView alloc]initWithFrame:CGRectMake((320-210)/2, 0, 210, 50)];
customTitleView.backgroundColor = [UIColor redColor];
self.navigationItem.titleView = customTitleView;
Hope this helps. :)

Set background image for entire iOS app

I'm trying to set a background image for the entire app following this suggestions: set background image for entire iPhone / iPad app
But in iOS 7 (don't know about other versions) it doesn't work well at all.
I've created a simple repository so you can understand better what's is going on.
There are some glitches in the transitions.
When you tap on a row in the first view, the second view is pushed into the navigation controller but there's a weird effect. It seems that the rows transparency played into this.
Also the other problem is when you turn back to the previous view controller there's a subtle shadow of the view controller that is popped from the navigation stack. As I stated before you can get what I mean by running the simple Xcode project.
Repo: https://github.com/socksz/FixedBackgroundImage
Any ideas? I've already tried to set the background image for each controller but it isn't what I want because in that way the image "overlaps" the previous background image and it's not the desired effect.
Hope I explained well.
EDIT 1
It seems that the problem occurs because of the way iOS 7 manages the transitions between two view controllers. In you are in the second view controller and try to turn to the previous controller with the swipe gesture you can see that as you begin the gesture the first controller appears below the second controller (the controller you're seeing) and, since the UITableViewCells have transparent backgrounds, you already see the first controller. Actually I'm afraid that there's not a solution. What a pity that I cannot have a fixed background image without setting the background image on each controller.
I had a requirement in an iPhone app to set the background image of a page based on a user's preferences. The way I dealt with it was to add a UIImageView with the background image as a sub-view of the view, like so -
UIImageView *bgImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background-image"]];
bgImageView.frame = self.view.bounds;
[self.view addSubview:bgImageView];
[self.view sendSubviewToBack:bgImageView];
I cloned your Github repository and added the above piece of code in viewDidLoad of both the view controllers. I also added the following line of code in the same method -
self.tableView.opaque = NO;
I commented out the code in didFinishLaunchingWithOptions where you set the background color. With these changes, the artifacts while navigating between view controllers are gone. I tested with iPhone Retina (3.5-inch) as well as iPhone Retina (4-inch) simulators.
The reason why the artifacts are seen while navigating to and from the ViewController in the storyboard require some investigations. My suggestion may or may not work for your requirements, but, you can try this as a solution.
P.S. The method requires some tweaks to autolayout constraints.
You Just have to write only one line in
appdelegate.m file's applicationdidFinishLaunchingWithOptions: method
[self.window setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"MainBackground.png"]]];
and put below line in every screen's viewDidLoad method
[self.view setBackgroundColor:[UIColor clearColor]];
I did not find a way to put this globally. However, and you will probably find this useful for static/fixed images (instead of the moving images you get when you set the backgroundColor property), Use the backgroundView property for every screen.
self.tableView.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background.jpg"]];
I did it myself by creating a UtilTableViewController which does all theme and custom things I need it to do, putting this code there, then subclassing all my views. It's not a globally set image, but I only have to set it once and all of my TableViews will use it.
Put this code in your appdelegate.m file applicationDidFinishLaunching method.
UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:rootViewController];
windowBackground=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"background_window.png"]];
windowBackground.frame=CGRectMake(0, 0, 320, 568);
[window addSubview:windowBackground];
[windowBackground release];
window.frame = CGRectMake(0, 0, window.frame.size.width,568);
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
Add this code in every viewController class viewDidLoad method.
self.view.backgroundColor = [UIColor clearColor];
Late post...
If you are using a NavigationController you might try overriding the TopViewController "get" portion to automatically set the BackGroundColor to your image. Appologies, we use Xamarin which converts from C# to objective C for us (not sure of the specific syntax). In C# it will look something like this within your NavigationController class.
public override UIViewController TopViewController
{
get
{
if (base.TopViewController.View.BackgroundColor != "Your Image")
{
base.TopViewController.View.BackgroundColor = "Your Image";
}
return base.TopViewController;
}
}
Write this code in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method
UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"YourImageName.png"]];
self.window.backgroundColor = background;
you can set the background image through below code ... wew can put this code in viewdidload method in viewcontroller.m file
[self.window setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"bg.png"]]];

UINavigationController's nav bar seems invisible, but title and buttons are still visible

I've run into a weird situation. This is all done in code - no Interface Builder.
I'm creating a UIViewController and adding some content to it:
UIViewController* popoverViewController = [[[UIViewController alloc] init] autorelease];
UIView* popoverContentView = [[[UIView alloc] init] autorelease];
popoverContentView.backgroundColor = [UIColor blackColor];
// Add some stuff to popoverContentView
popoverViewController.view = popoverContentView;
I then create a UINavigationController, set its root view controller to the UIViewController from above, and add a title and a button to the navigationItem:
UINavigationController* popoverNav = [[[UINavigationController alloc] initWithRootViewController:popoverViewController] autorelease];
popoverViewController.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismissPopover)] autorelease];
[popoverViewController.navigationItem setTitle:#"MY TITLE"];
Then I set up a UIPopoverController with the UINavigationController in it and present it:
self.popoverController = [[[UIPopoverController alloc] initWithContentViewController:popoverNav] autorelease];
self.popoverController.delegate = self;
[self.popoverController setPopoverContentSize:CGSizeMake(320, 216) animated:NO];
[self.popoverController presentPopoverFromRect:self.cell.frame inView:self.popoverParentView permittedArrowDirections:UIPopoverArrowDirectionUp | UIPopoverArrowDirectionDown animated:YES];
The problem is that everything appears correctly with one exception: the navigation bar is invisible, but the title text and the bar button still show up and work correctly. I've tried messing with the bar's hidden and tintColor properties and changed the size of the popover, but nothing changes.
I'm sure I'm missing something obvious, but I can't see it.
I have other examples of similar things in my project's codebase, but those appear correctly. Any ideas as to why this is happening or how I could fix it?
EDIT
Not sure if the following will help, but I'm hoping it will provide some clues as to what's really going on here to someone who's seen something like this before.
I pushed a new (blank) view controller onto popoverNav just to see what would happen. It pushes and animates perfectly. Everything looks right except that the nav bar is still transparent and the bar button items are pushed to the top of the view.
Perhaps not the main cause of the problem, but it looks like the UIDatePicker is clipped by the CGSize you have setup. As far as I can remember, the default height for the picker is 216 (which you have) but you haven't taken into consideration the required height for the UINavigationBar.
I'm not sure why the background of the view looks to be some kind of textured grey unless this is set in your UIViewController but it doesn't appear that way from the code you pasted. This certainly isn't a default texture for iOS though.
Perhaps try and make your CGSize a little larger to accommodate the contents of the view as it looks as though the "Done" button is hugging the top right corner where there should be a fair few points worth of spacing around this by default.
Also, is there a reason why you're creating a new view and assigning it as the view property of your view controller?
UIView* popoverContentView = [[[UIView alloc] init] autorelease];
popoverContentView.backgroundColor = [UIColor blackColor];
// Add some stuff to popoverContentView
popoverViewController.view = popoverContentView;
Why not add the content as subviews of the view instead?

UINavigationController nav bar overlaps contained TableView

I have a third party control that wants me to put a view inside of it. I'm trying to get a UINavigationController containing a series of table views inside of it, but when adding the controls the navigation bar overlaps the tableview by about half a row, which looks dumb.
Here's the code. I'm using the ArcGIS Server iOS SDK to put the navigation controller in a callout box on the map:
IdentifyResultsViewController *idWindow = [[IdentifyResultsViewController alloc] init];
idWindow.results = results;
UINavigationController *nvc = [[UINavigationController alloc] initWithRootViewController:idWindow];
map.callout.customView = nvc.view;
nvc.view.frame = CGRectMake(0, 0, 275, 400);
[map showCalloutAtPoint:self.mapPoint];
Is this a common problem using the UINavigationViewController, or should I look to the third party control?
I actually just had a similar problem with a third party control obstructing my navigation bar. I tried to look into the control but I'm not versed enough to unhide the navigation bar.
What I did may be something you can do too: instead of using the built in UINavigationBar, I kind of built one myself by just putting in a UIView at the top of the page and adding custom buttons to it that performed the functions that I wanted in the bar. This gives you a little more wiggle room around that third party control if you can't find what's causing the issue.
Hope it helps!
I solved this using some simple reordering of code - instead of using initWithRootViewController, I created the navigation view controller, set it's frame manually, and then pushed the view controller on to it:
IdentifyResultsViewController *idWindow = [[IdentifyResultsViewController alloc] init];
idWindow.results = [self filterResults:results];
UINavigationController *nvc = [[UINavigationController alloc] init];
nvc.view.frame = CGRectMake(0, 0, 275, 400);
[nvc pushViewController:idWindow animated:NO];
map.callout.customView = nvc.view;
[map showCalloutAtPoint:self.mapPoint];

UINavigationController has extra status bar gap at top

This looked simple enough when I set it up, but I can't explain why this gap is present between the status bar and the navigation bar. Also, the contained view looks like it may be properly aligned, and it's just the nav bar that is shifted down. The gap looks like the size of the status bar, so I expect that has something to do with it, but I don't know what.
Here is the code for setting up the navigation controller:
- (void)viewDidLoad
{
[super viewDidLoad];
advancedVC = [[AdvancedSearchFormVC alloc] initWithNibName:#"AdvancedSearchForm" bundle:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:advancedVC];
nav.navigationBar.tintColor = [UIColor defaultNavBarTint];
nav.navigationBar.topItem.title = NSLocalizedString(#"SearchTitle", nil);
UIBarButtonItem *searchButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"SearchButton", nil) style:UIBarButtonItemStylePlain target:self action:#selector(refreshPropertyList:)];
nav.navigationBar.topItem.rightBarButtonItem = searchButton;
self.view = nav.view;
}
The rootViewController uses a view from a xib file, where I have simulated the status bar, the navigation bar, and the tab bar.
The problem is indeed that the navigation controller always expects to leave room for the status bar, which is the 20 pixel gap. I searched around for a while before I found this solution which works:
//nav is assumed to be a subclass or instance of UINavigationController
nav.view.frame = CGRectOffset(nav.view.frame, 0.0, -20.0);
//you can then add the navigation's view as a subview to something else
I originally found an answer which did this offset to the navigationbar's view, but it didn't work. It works when you do it to the navigation controllers actual view.
I use this technique to add a navigation controller from another nib to an empty view of my main nib, so I can position it anywhere within the main screen as a subview. By using an empty view as a placeholder and positioning frame on my main nib, I create a separate nib and class to manage the navigation, which manages other nibs used to handle their screens. This way I can solve the classic "how do I add a banner, image, or custom views above my navigation controller" while having a navigation controller as a subview...in iOS 5 to be specific.
It's also worth mentioning that I use the app delegate to store and access all the other controllers, so they are retained by a persistant instance which I can access from any class. Create and synthesise some properties in the app delegate of all your controllers, and in viewDidLoad create instances. That way I can reference all the controllers used in my app later anywhere by adding:
//this shows how to store your navigation controllers in the app delegate
//assumes you've added 2 properties (UINavigationController*)"navController" and (UIViewController*)"rootController" in your app delegate
//...don't forget to add #import "AppDelegate.h" to the top of the file
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[app.navController pushViewController: app.rootController animated:YES];
//now apply the offset trick to remove the status gap
app.navController.view.frame = CGRectOffset(app.navController.view.frame, 0.0, -20.0);
I had the same problem before. The code I used to add UINavigationBar to UIViewController:
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:self];
[self.view addSubview:nc.view];
Solution:
Check the box "Wants Full Screen" with Attributes inspector of your UIViewController.
You can try to set the attribute Under Top Bars unchecked from Attributes section of UIViewController.
As we all know by now, the 20 pixel shift is to provide space for the Status bar on the top.
But infact, the view controllers coordinate system is kept in place and only the navigation bar frame is shifted down by 20 pixels. This makes the navigation bar to actually overlap the top 20 pixels of the view.
Logging the navigation bars frame origin, it will show (0.0, 20.0)
So the solution is to simply reposition the navigation bar's origin to (0.0, 0.0) in ViewWillAppear.
self.navigationController.navigationBar.frame = CGRectMake(0.0, 0.0, self.navigationController.navigationBar.frame.size.width, self.navigationController.navigationBar.frame.size.height);
Since you're adding advancedVC as a subview of self.view, it is being added inside the frame of self.view which I'm guessing is already compensating for the status bar.
You can probably easily fix this by adding this line:
nav.view.frame = self.view.frame;
Just above this line:
self.view = nav.view;
-
Other Thoughts
I'm not privy to your entire setup, but self.view may not be needed at all. Simply make your advancedVC instance the rootViewController of the UIWindow instance contained in your App Delegate.
The problem was resolved by fixing the way the navigation controller was inserted. Instead of inserting it into a view that had been put onto the tabbar controller, the navigaiton controller should have been put directly onto the navigation controller.
advancedSearchFormVC = [[AdvancedSearchFormVC alloc] initWithNibName:#"AdvancedSearchForm" bundle:nil];
UINavigationController *searchNavController = [[UINavigationController alloc] initWithRootViewController:advancedSearchFormVC];
This is just one controller that is on the tabbar controller, replacing the advancedSearchFormVC at the same time. Then this nav controller was added to the array of controllers that got put onto the tabbar controller.
Sorry for any trouble, but this was one of those problems I can look directly at and not see it. I should have seen this earlier, because I had another nav controller already on the tabbar controller, and it was set up the same way.
Thanks for your assistance.
The problem is that UINavigationController.view should be added to the top view.
Just find the top one and it will be working great.

Resources