How to set an image of navigationbar every where in the app - objective-c

I'm building an app with storyboard. Now I want on every screen in my app the same topBar/navigationbar. I want to set an image on this navigationbar. After some searching, I found this code.
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect
{
UIImage *image = [UIImage imageNamed: #"topbar.jpg"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
I've put it right above my implemantation in my appdelagate.M file. But it is not working.
Also does anybody knows if this is working in IOS 5.1 as well?
Kind regards.

Look at UIAppearance. It allows you to change attributes across entire UI classes. For example, I have this in an app:
UIImage *img = [UIImage imageNamed: #"NavBarBackground.png"];
[[UINavigationBar appearance] setBackgroundImage:img forBarMetrics:UIBarMetricsDefault];
UIAppearance was introduced in iOS 5. The "old" way to do this was to override -drawRect: using a category for the class in question (e.g. UINavigationBar), as you did. ("Swizzling" -- exchanging your method for an SDK-provided method -- not required, by the way.)
I found, when iOS5 came out, that the drawRect trick didn't work, so I had to use UIAppearance conditionally by enclosing the above code in an if statement like so:
if ([[UINavigationBar class] respondsToSelector:#selector(appearance)]) {
...
}
Why conditionally? We continued to support iOS4, so iOS4 users would experience a crash if we didn't conditionalize this UIAppearance stuff. And so, we had both this and the drawRect solution in the app!
If you don't have to support iOS4, you should definitely look into UIAppearance to achieve your goals here.

I used to swizzle drawRect for this purpose, you can perform swizzling at +initialize with method_exchangeImplementations (runtime.h). Now the tricky part is to call the original drawRect, as the methods are swizzled you'll need to use the custom drawing method name to call the original one. Like if you do the drawing with, you'll have [super customDrawRect:rect]; at the end
- (void)customDrawRect:(CGRect)rect {
...
[super customDrawRect:rect];
}
#import <objc/runtime.h>
#implementation UINavigationBar (CustomImage)
+ (void)initialize {
Method originalDrawRectMethod = class_getInstanceMethod([self class], #selector(drawRect:));
Method customDrawRectMethod = class_getInstanceMethod([self class], #selector(customDrawRect:));
method_exchangeImplementations(originalDrawRectMethod, customDrawRectMethod);
}
- (void)customDrawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"topbar.jpg"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
// original drawRect call
[self customDrawRect:rect];
}
#end

Related

UITableViewCell drawInRect iOS7

Hi I am trying to draw strings in my UITableViewCell in iOS 7 with the following code
-(void)drawRect:(CGRect)rect{
[super drawRect:rect];
CGRect playerNameRect = CGRectMake(0, kCellY, kPlayerNameSpace, kCellHeight);
NSDictionary*dictonary = [NSDictionary
dictionaryWithObjectsAndKeys:
[UIColor hmDarkGreyColor], NSForegroundColorAttributeName,
kFont, NSFontAttributeName,
nil];
[self.playerName drawInRect:playerNameRect withAttributes:dictonary];
}
However I can not get anything to appear... self.playerName is not nil, and the playerNameRect is correct.
I was previously using the following code to do the same thing but was recently deprecated in iOS 7
[self.playerName drawInRect:playerNameRect withFont:kFont lineBreakMode:NSLineBreakByTruncatingTail alignment:NSTextAlignmentCenter];
What is also strange is I can not get anything to draw in drawRect on a UITableViewCell... The deprecated code works when I am drawingRect on just a UIView.
You shouldn't use UITableViewCell's drawRect method to perform custom drawing. The proper way to do it is to create a custom UIView and add it as a subview of your cell (as a subview of the contentView property). You can add the drawing code to this custom view and everything will work fine.
Hope this helps!
Check out these posts too:
Table View Cell custom drawing 1
Table View Cell custom drawing 2
Table View Cell custom drawing 3
As others said, don't use UITableViewCell's drawRect selector directly. By doing that, you're relying on implementation details of UITableViewCell, and Apple made no guarantee that such behaviour won't break in future versions, just as it did in iOS 7... Instead, create a custom UIView subclass, and add it as a subview to the UITableViewCell's contentView, like this:
#implementation CustomTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.contentView addSubview:[[CustomContentView alloc]initWithFrame:self.contentView.bounds]];
}
return self;
}
#end
And the CustomContentView:
#implementation CustomContentView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
NSDictionary * attributes = #{
NSFontAttributeName : [UIFont fontWithName:#"Helvetica-bold" size:12],
NSForegroundColorAttributeName : [UIColor blackColor]
};
[#"I <3 iOS 7" drawInRect:rect withAttributes:attributes];
}
#end
Works like charm!
Try setting cell.backgroundColor = [UIColor clearColor] in init.
While I agree with the accepted answer, here's my take on it for the records:
If you don't need any of the builtin UITableViewCell functionality (swiping, removing, reordering, ...) and just use it as a container to draw your custom stuff, then you might want to consider removing all of the cells subviews in tableview:willDisplayCell:ForRowAtIndexPath. This will make your drawing be visible again and will get you maximum performance (since you get rid of the subviews you don't need).

Where to set popover size?

I have a UIViewController called HomeViewController and it has a model that contains an array of data. HomeViewController also has a button that when pressed will show a UITableViewController that display's the model's array of data.
My question is, what is the standard/best way to set the popover's size? I only want the popover to be tall enough to display the data, but no taller. What is common practice? I assume that I need to set contentSizeForViewInPopover at some point but I'm not sure where...In the viewDidLoad method of the popover class? In the prepareForSegue method?
Here's the code I have so far: (Note that DataPresentingViewController is my popover view controller class`)
//In HomeViewController
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.destinationViewController isKindOfClass:[DataPresentingViewController class]])
{
DataPresentingViewController *dest = (DataPresentingViewController *) segue.destinationViewController;
dest.mySavedData = self.myModel.mySavedData;
}
}
I know that I could just set the contentSizeForViewInPopover here in the prepareForSegue method; however, this seems like something the popover class should deal with.
As of iOS7 contentSizeForViewInPopover is deprecated. You should use UIViewController's preferredContentSize property instead.
Hope this helps!
set contentSizeForViewInPopover in ViewDidLoad method in HomeViewController
contentSizeForViewInPopover is deprecated in iOS 7
The property: popoverContentSize of UIPopoverController represents the size of the content view that is managed by the view controller in the contentViewController property of UIPopoverController.
Reference
The advantage is that it is available in iOS 3.2 and later, so you don't need to check device version everytime, just replace contentSizeForViewInPopover method with your UIPopOverController object instance.
Have you tried:
setPopoverContentSize:<#(CGSize)#> animated:<#(BOOL)#>
in your segue logic block. Useful if you want the popover size to be variable based upon a data set, or view placement or whatever.
// self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0); //Deprecated in ios7
self.preferredContentSize = CGSizeMake(320.0, 600.0); //used instead
A little known trick to sizing your UIPopoverViewController to match the height of your UITableView is to use the tableView's rectForSection method to give you the height. Use the height in your viewController's contentSizeForViewInPopover like this:
- (CGSize)contentSizeForViewInPopover {
// Currently no way to obtain the width dynamically before viewWillAppear.
CGFloat width = 200.0;
CGRect rect = [self.tableView rectForSection:[self.tableView numberOfSections] - 1];
CGFloat height = CGRectGetMaxY(rect);
return (CGSize){width, height};
}
Try the following code:
- (IBAction)setData:(id)sender
{
UIViewController *popoverContent=[[UIViewController alloc] init];
UITableView *tableView=[[UITableView alloc] initWithFrame:CGRectMake(265, 680, 0, 0) style:UITableViewStylePlain];
UIView *popoverView=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 300)];
popoverView.backgroundColor=[UIColor whiteColor];
popoverContent.view=popoverView;
popoverContent.contentSizeForViewInPopover=CGSizeMake(200, 420);//setting the size
popoverContent.view=tableView;
tableView.delegate=self;
tableView.dataSource=self;
self.popoverController=[[UIPopoverController alloc] initWithContentViewController:popoverContent];
[self.popoverController presentPopoverFromRect:CGRectMake(400, 675, 0, 0) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
}
It looks like you want to do it programmatically, but in the storyboard, before you hook up a view controller to a segue, under the attributes inspector there is a "popover, use explicit size option." Maybe you can set the size that would work the best for your App first and not worry about the size with using code. Hope this helps, let us know how it goes.

Objects return null after presentModalViewController

After showing a camera, my objects return null:
Here's how I show my UIImagePickerController (I initialize it before):
[self presentModalViewController:pickerOne animated:NO];
I add a object in my scrollView like this,
UIImageView *overlay = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"black_overlay.png"]];
[overlay setFrame:CGRectMake(((columnCount-1)*100)+((columnCount-1)*5)+5 ,((rowCount-1)*100)+((rowCount-1)*5)+11, 100, 100)];
[overlay.layer setCornerRadius:8.0];
[overlay.layer setMasksToBounds:YES];
[overlay setTag:12];
[theImageViewer addSubview:overlay];
And access it like this (after the camera has been closed):
UIImageView *overlayImage = (UIImageView*)[theImageViewer viewWithTag:[[arrayOverlays objectAtIndex:[arrayIds indexOfObject:imageThumbnail]] intValue]];
NSLog(#"OverlayImage: %#",[arrayOverlays objectAtIndex:[arrayIds indexOfObject:imageThumbnail]]);
And I get: Real overlayImage, (null)
Please help! Thanks.
The pickerOne must be a property inside your current ViewController.
Your current ViewController must conform to protocol of UIImagePickerControllerDelegate.
In the imagePickerController:didFinishPickingMediaWithInfo: implementation from the delegate you'll get the image object and dismiss the modalViewController.
That should do the trick.
Edit: just found a tutorial that explains this in detail:
http://iphone.zcentric.com/2008/08/28/using-a-uiimagepickercontroller/
Note:imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)img editingInfo:(NSDictionary *)editInfo is deprecated in iOS 3.0
I realized I was calling a action from a different class right after I dismissed the view. I simply made it a NSNotification and works fine.

UINavigationBar Background Image Problem

i have set my navigationbar background with this code in my App delegate:
#implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
UIImage *backgroundImage = [UIImage imageNamed: #"Nav-rightpane.png"];
CGContextDrawImage(UIGraphicsGetCurrentContext(),
CGRectMake(0, 0, self.frame.size.width, self.frame.size.height),
backgroundImage.CGImage);
}
#end
This works perfect but now i must change the background to the default at UIPopoverController. Any idea for this?
Thanks
Do not use this solution. Simply delete your UINavigationBar Category and implement your own UINavigationController.
#interface XNavigationController : UINavigationController{}
and change override viewDidLoad method in implementation
- (void)viewDidLoad {
[super viewDidLoad];
UIImage *img = [UIImage imageNamed: #"Nav-rightpane.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:img];
[imageView setFrame:self.navigationBar.bounds];
[imageView setContentMode:UIViewContentModeScaleToFill];
[self.navigationBar addSubview:imageView];
[imageView release];
}
so use this navigationController when you want to change the background of navigation bar.
The correct solution pre-iOS5, as recommended by Apple engineers is to not use categories and to not use method swizzling. This is confirmed to break on iOS 5 which is very close to being released.
Instead, you should create a subclass of UINavigationBar, override the drawRect: method, and then use Interface Builder to define your navigation controller. Interface Builder will let you change the classname for the navigation bar to your custom subclass:
If you want to avoid Interface Builder, you can create a temporary XIB and load an instance of the navigation controller from it, then use NSKeyedArchiver to archive it to a file. Then use xxd -i <filename> on the command line to generate C-code for the raw bytes of the navigation controller. Then paste that into a source file and unarchive it when you want an instance.
It seems like a lot of work, but the WWDC videos and engineers on the forums have repeatedly stated that this is the only safe and reliable way to do what you want.
In iOS 5 this all becomes a whole lot easier.

Subviews not showing up in UIView class

I'm trying to lay out some images in code using addSubview, and they are not showing up.
I created a class (myUIView) that subclasses UIView, and then changed the class of the nib file in IB to be myUIView.
Then I put in the following code, but am still getting a blank grey screen.
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
[self setupSubviews];
}
return self;
}
- (void)setupSubviews
{
self.backgroundColor = [UIColor blackColor];
UIImageView *black = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"black.png"]];
black.center = self.center;
black.opaque = YES;
[self addSubview:black];
[black release];
}
yes, just implement initWithCoder.
initWithFrame is called when a UIView is created dynamically, from code.
a view that is loaded from a .nib file is always instantiated using initWithCoder, the coder takes care of reading the settings from the .nib file
i took the habit to do the initialization in a separate method, implementing both initWithCode and initWithFrame (and my own initialization methods when required)
try implementing initWithCoder: sometimes I've had trouble with IB and initWithFrame:
or at least add a logging call to see if your init method is executed