It feels like this should be fairly simple but nothing i've tried so far has worked. In a nutshell, I want to add a fixed image just below the navigation bar in a UITableViewController that i create programmatically. In other words, I want the image to stay just below the navigation bar even as the user scrolls up and down the table view (it's basically a custom drop-shadow for the navigation bar).
The closest I've gotten is the code below (in the UITableViewController's init method), which adds the image but doesn't keep it from moving when the user scrolls.
// Add nav bar drop shadow
UIImage *dropShadowImage = [UIImage imageNamed:#"NavBarDropShadow.png"];
UIImageView *dropShadowView = [[UIImageView alloc] initWithImage:dropShadowImage];
[self.view addSubview:dropShadowView];
Is there an easy way to add an add an image to the screen programmatically, position it wherever you like, and have it stay there even as the user scrolls? Thanks for any and all input!
EDIT: IOS5 has a better way to do this. Please check out the new UIAppearance protocol.
Adding this block of code to your code will allow you to draw your shadow on all UINavigationBars in the app. This is a better solution than adding the shadow as a UIImageView:
#implementation UINavigationBar (ShadowBar)
- (void)drawRect:(CGRect)rect {
//draw the shadow ui nav bar
UIImage *image = [UIImage imageNamed: #"UINavBarWithShadow.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
- (void)layoutSubviews {
self.frame = CGRectMake(0, 0, self.frame.size.width, 300);
}
#end
To make the UINavigationBar higher and thus not clipping your content, override the layoutSubviews and set the frame you need (the code above assumes your header is 300 points high). layoutSubviews does nothing by default, but is "lazy" called before lay-outing the view.
For more info about this custom size/look overrides that apply to UIView (and any other subclass) have a look here
You can make a subclass or a category on UINavigationBar, and have it add the image in the init or drawRect methods. If you think about it, you're trying to add a shadow to the navigation bar, not to the UITableView, so it makes sense to modify the navbar, not the table.
You are adding your dropShadowView to self.view that in your case is the view of an UITableViewController. It means that your self.view is an UITableView so when you scroll up and down you scroll the dropShadowView as well because is inside the tableView.
Try to write a custom UIViewController and add two subviews: one is the dropShadowView and the other one is your table.
Have a look at this similar question I answered a while back. It should do exactly what you want with little customization.
Transparent View at the Top of a UITableView
Dont make a UITableView the main view ie. the view outlet, set that to a UIView that contains a UITableView, then make your controller a subclass of UIViewController, and make it conform to UITableViewDataSource and UITableViewDelegate. in your nib, set up your view so that you have a UIImageView at the top and a UITableView below. and set the delegate and datasource to your file's owner.
Related
i am facing a new problem.
I have custom navigation controller in my application. I have to add an image to navigationbar and i used this code in my AppDelegate-
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"top-red.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, 44)];
}
#end.
This code is working fine for me, but when i use MFMailComposer in my application and call it on a button event i see that the navigationbar of MFMailcomposer is also changed to my custom navigationbar which i don't want to change.
Any Ideas!!!
By adding a category to the UINavigationBar, you are adding a method to all instances of UINavigationBar.
Since you are overriding drawRect: that means that whenever any navigation bar wants to draw itself it is using your method rather than the standard drawRect:
Rather than do this, you should just add the image to your navigation bars where you want them , rather than change it globally as you have done here.
I am currently using this code in my App Delegate to set a custom background image for the Navigation bar:
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"MyNavigationBar.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
It works great, and for all the views in my App I set the Navigation bar title to show no text (so it won't cover up the background image).
One of the views presents a modal view controller (an EKEventEditViewController to be exact), and the title in the Navigation bar says "Add Event", which shows up on top of my custom image.
I have tried several ways to change the title (to no avail), but would rather prevent the custom Navigation bar background image from showing up only in this modal view.
Does anyone know of a way to do this while still using the stated method for setting the Navigation bar background image?
please, never ever do this...
You are replacing the drawRect: method in UINavigationBar, very brittle and likely to cause you (and your users) headaches.
Your background will always show up in all instances of UINavigationBar because you have replaced the implementation of drawRect:
There is no way to replace the title of the EKEventEditViewController. Of course there is always a way, but its even worse than replacing the implementation of drawRect:
Instead, please make a subclass of UINavigationBar and use that in the places you require a custom nav bar.
I am trying to scroll the content without scrolling the image in the background using UIScrollView.
I am using IB and setting the FileOwner View to point to the Scroll View (Image View is a child of the Scroll view). I have made the image height to be 960 pixels.
I have also set scrolling content size in the vierController that owns this UIView
(void)viewDidLoad {
UIScrollView *tempScrollView = (UIScrollView *)self.view;
tempScrollView.contentSize=CGSizeMake(320, 960);
}
My problem is that the Image only appears moves along with the content.
I have tried taking out the settings in viewDidLoad, but the scrolling cease to function.
I have also tried changing the location of the image and have it placed under VIEW instead of Scoll View (by the way Scroll View is a child of VIEW), but that resulted in the app breaking (termination error).
Any advice would be appreciated.
The easiest (and correct way) is to set an background image to your UIScrollView
UIImage *img = [UIImage imageNamed:#"image.png"];
[scrollView setBackgroundColor:[UIColor colorWithPatternImage:img]];
The easiest way is to set an UIImageView behind your UIScrollView(UIImageView and UIScrollView under UIView) and make your UIScrollView background color as clear color as mentioned by Janson. By this way you can scroll the content without scrolling the image in the background using UIScrollView.
Thanks. This was extremely helpful.
The one thing that I would add (pardon if it is obvious, but for people new to the platform (i.e., me), it took a bit of playing around).
I put the code from "unset"'s example after where I setup the contentSize=CGSizeMake in the viewDidLoad of the UIViewController , i.e.,:
// Setup the Scroll View
UIScrollView *tempScrollView=(UIScrollView *)self.view;
tempScrollView.contentSize=CGSizeMake(320, 720);
// Set Stationary Background, so that while the user scroll the background is
// fixed.
UIImage *img = [UIImage imageNamed:#"bg-body.jpg"];
[tempScrollView setBackgroundColor:[UIColor colorWithPatternImage:img]];
Then, I went into the ViewController .xib file and on the UIView that is inside of the UIScrollView, I set the background color to "Clear Color".
I sure that there is a way to do this through code at the same time as setting the background of the UIScrollView, but at least that is how you can do it through IB.
Jason
The design I've been given for an iPhone app I'm working on calls for a 1px red line at the bottom of the navigation bar. I know how to draw a line with Quartz and -drawRect, but how do I get access to the UINavigationBar's view? Or would it work to draw the line in the app's main view, on top of everything else?
Duncan Wilcox's answer below works to get the line drawn, but then buttons on the bar become impossible to press.
If I do [self.navigationItem.titleView.superview sendSubviewToBack:titleView] then buttons that are created in nib files work, but not ones that are dynamically added.
I guess the answer depends on where you want the red line, as part of the navigation bar or just under it. That would be only 1 pixel difference, but designers are demanding these days :)
If you want it below the navigation bar you definitely have to put it in all of the app's views where the line is needed.
There's a surprisingly simple way to alter the look of the navigation bar, I used it for skinning the entire navbar look but it will probably work for even only some parts of the navbar if you make the image partially transparent.
Basically we want to skin the UINavigationItem of a UIViewController that's in your navigation hierarchy.
create a 320x44 image containing your custom navbar look
add a UIImageView to the XIB where the UINavigationItem is
link the titleView property of the UINavigationItem to the UIImageView
This almost works, except that the title view will be resized in wierd ways. To fix that I have a UIImageView subclass that keeps the image properly set:
#implementation TitleViewHack
- (void)setFrame:(CGRect)frame
{
frame.size.width = 320;
if(frame.origin.x > 0 && frame.origin.x < 20)
frame.origin.x = 0;
[super setFrame:frame];
}
#end
So instead of using a UIImageView above you'll use this TitleViewHack class.
This is what I've settled upon:
// UINavigationBar subclass
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"background_navbar.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
I've got a button that I'm adding as a subview of a table view's tableHeaderView. The button appears fine, and tap-and-holding on it works intermittently - for the most part, though, it's unresponsive. I've tried adding it as a subview of the table itself; the effect is about the same. I thought the problem might be with the scroll view's touch interception, but disabling scrolling on the table has no effect either.
Am I doing something wrong? Has anyone else encountered this?
edit - to clarify, I'm talking about the main table header, not a section header, in a grouped-style table; think basically modeled after the "Contact" screen.
I had the same problem. In my case I had a container view instantiated in IB (that was applied as the table view header in code), with a UIImageView occupying the entire frame of that container. The misbehaving button resided in the image view.
Turns out I needed to have sizing struts in IB set as follows...
Container View: exterior anchors all on, interior resizing - all off
Sub Image View: all struts on (interior and exterior)
I had several different table views, all using header views. Some would respond to touch events correctly, some were flaky. This solved my problem
I had a similar problem - a textfield and button inside a view set as the table header view which would not respond to touch events. setAutoResizing programmatically worked for me.
My controller extends UITableViewController, and viewDidLoad looks like this:
- (void)viewDidLoad
{
[super viewDidLoad];
MYCustomWidget *headerView = [[[NSBundle mainBundle]
loadNibNamed:#"MYCustomWidgetView" owner:self options:nil]
objectAtIndex:0];
[headerView setAutoresizingMask:UIViewAutoresizingNone];
self.tableView.tableHeaderView = headerView;
}
('MYCustomWidget' extends UIView, and 'MYCustomWidgetView' is a XIB file).
I completely disagree with Wisequark -- there's absolutely nothing wrong with putting a button in the tableHeaderView, and including one would not risk your app being rejected from the app store. The tableHeaderView is designed to be an arbitrary view containing whatever elements you choose.
As far as your issue, it could be that you've got a view obscuring your button, or, it may simply be a bug that should be reported to Apple.
Strangely enough, but the table header view is apparently resized incorrectly.
I use auto layout, so autoresizing mask was not an option for me. After inspecting my view hierarchy:
and noticed that my custom header view had incorrect height, so only less then half of it was tappable (see highlighted view):
Manual updating of its height fixed the problem:
- (void)viewDidLayoutSubviews {
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = 116.0;
self.tableView.tableHeaderView.frame = frame;
}
Also, the table view header height can become invalid after the orientation is changed. This problem also can be fixed with the provided solution.
My situation was similar to Danny Hall's (the table header view was a UIImageView, and there was a UIButton which was a subview of the UIImageView). In my case, the problem appears to have been caused by the button being a subview of the image view. What worked for me was creating a UIView "containing" view, such that both the image view as well as the button were subviews of the "containing" view. strange.
tableHeaderView has 0 height while it is processing draw in UITableView
use this UIView subclass to set the strong constant height and ignore UITableView processing
#import <UIKit/UIKit.h>
#interface CustomHeaderCell : UIView
#end
//-----
#import "CustomHeaderCell.h"
#implementation CustomHeaderCell
-(void)setFrame:(CGRect)frame {
frame.size.height = 43; // !!! constant height
[super setFrame:frame];
}
#end
I have the same problem UIButtons actions not working in UITableView's header view. First i tried setAutoresizingMask to .None which not works then after reading the answers of #Davyd and #Alexey i realise that i did not set the height of headers view then i set it like:-
self.tablevwMain.tableHeaderView?.frame = CGRect(x: 0, y: 0, width: width_view, height: your_height)
And all UIButton'sinside UITableView's header view works correctly.
For me UIControl like UIButtons on headers only worked if I add it to the cell's contentView
addSubview(stackView) //Does not work
contentView.addSubview(stackView) //Works
Don't forget to set the footer height in:
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
You should consider that this is not the intended sue of the headerView and that an implementation such as that might result in rejection from the AppStore as a result of a HIG violation. Given that the dimensions of a header are intended to be small, it is probably better to consider a restructuring of your view. Having said that, there is no easy way to do it short of hand detecting touch events and determining the geometry yourself, then executing a selector based on the geometry - in short, rolling your own button class.