iPhone/iPad stretchy, resizable buttons - subclassing UIButton - iphone

I want to be able to make custom buttons which have an image background using a scale9 type background image meaning the width of the button can be dynamic. I have seen example on the web of people doing this on a per button basis but it seems to me that wouldn't it be better to create a new object which subclasses UIButton which you can then use in Interface Designer as the class for any custom button (round rect button set to custom).
Here is what I have so far.
#import <Foundation/Foundation.h>
#interface LargeButton : UIButton {
}
#end
#import "LargeButton.h"
#implementation LargeButton
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.frame = CGRectMake(0, 0, 170.0, 48.0);
// Center the text vertically and horizontally
self.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
UIImage *image = [UIImage imageNamed:#"btnBigPurple.png"];
// Make a stretchable image from the original image
UIImage *stretchImage =
[image stretchableImageWithLeftCapWidth:15.0 topCapHeight:0.0];
// Set the background to the stretchable image
[self setBackgroundImage:image forState:UIControlStateNormal];
// Make the background color clear
//self.backgroundColor = [UIColor clearColor];
}
return self;
}
#end
This doesn't however seems to work. When I run this im simulator I see the button text but the button has no background. I have placed a breakpoint and I know its running and check the console and have no errors.
Can someone help? fix this or is my way of thinking wrong?
Thanks

I fixed it myself. For those who are interested.
#import <Foundation/Foundation.h>
#interface LargeButton : UIButton {
}
#end
#import "LargeButton.h"
#implementation LargeButton
- (void)drawRect:(CGRect)rect{
UIImage *greenBalloon = [[UIImage imageNamed:#"btnBigPurple.png"] stretchableImageWithLeftCapWidth:10 topCapHeight:0];
[self setBackgroundImage:greenBalloon forState:UIControlStateNormal];
}
#end
Simple but effective.

Related

How to achieve background Image in UILabel as in pic?

How to achieve below effect??
I want that when my UIImage width is smaller then UILabel width then my image should be displayed only once as below.
I already have done some more work with UILabel and UIImage. Refer to my previous question : How to stretch Image to fill the Label Width set in Background in UILabel?.
But now i want some more fun with UILabel... :D
EDIT :
SBLabel *lbl = [[SBLabel alloc] initWithFrame:CGRectMake(0, 50, 320, 28)];
lbl.text = #"Hello World!!!...";
UIImage *img = [UIImage imageNamed:#"cn3.png"];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 50, 320, 28)];
imgView.image = img;
[lbl setNonRepeatingBackgroundImage:imgView];
[self.view addSubview:lbl];
[imgView release];
if (image.size.width < label.size.width ||image.size.height < label.size.height )
{
//rect here is frame of image
[img drawInRect:rect];
label.backgroudColor = [UIColor clearColor];
}
It seems like the most straightforward solution would be to have your UIImage sit behind your UILabel and your UILabel has a transparent background color.
Edit
Assuming you're using ARC...
SBLabel.h
#import <UIKit/UIKit.h>
#interface SBLabel : UILabel
#property (nonatomic, strong) UIImageView *nonRepeatingBackgroundImage;
#end
SBLabel.m
#import "SBLabel.h"
#implementation SBLabel
#synthesize nonRepeatingBackgroundImage;
- (void)setNonRepeatingBackgroundImage:(UIImageView *)aNonRepeatingBackgroundImage {
nonRepeatingBackgroundImage = aNonRepeatingBackgroundImage;
[self addSubview:aNonRepeatingBackgroundImage];
[self sendSubviewToBack:aNonRepeatingBackgroundImage];
[self setBackgroundColor:[UIColor clearColor]];
}
#end
You would need to call this setter method after you add the UILabel to the parent view. I have not tested but I believe it should work, might require some tweaks but hopefully it explains how to Subclass UILabel to make a custom enhancement such as this.
A subclass of UILabel could look like this (without ARC)
CustomLabel.h
#import <UIKit/UIKit.h>
#interface CustomLabel : UILabel {
UIImage *mImage;
}
#property (retain) UIImage *image;
#end
CustomLabel.m
#import "CustomLabel.h"
#implementation CustomLabel
- (void)dealloc {
self.image = nil;
[super dealloc];
}
- (void)drawRect:(CGRect)rect {
[mImage drawAtPoint:CGPointMake(0, 0)];
[super drawRect:rect];
}
- (UIImage*)image {
return mImage;
}
- (void)setImage:(UIImage *)image {
self.backgroundColor = [UIColor clearColor];
if(mImage)
[mImage release];
mImage = [image retain];
[self setNeedsDisplay];
}
#end
Usage
yourCustomLabel.image = [UIImage imageNamed:#"test.png"];

Changing the UINavigationBar background image

I've been trying to change the background image of the UINavigationBar of my application. I tried several ways. First I added to my AppDelegate class the following code:
#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
But it wasn't working. My next try was to write a CustomizedNavigationBar Class which is overriding the drawRect method. It looked like that:
CustomizedNavigationBar.h
#import <UIKit/UIKit.h>
#interface CustomizedNavigationBar : UINavigationBar
#end
CustomizedNavigationBar.m
#import "CustomizedNavigationBar.h"
#implementation CustomizedNavigationBar
- (void) drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"navigationbar.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
NSLog(#"I got called!!!!!");
}
#end
In my .xib file where the NavigationBar is defined I changed the class to the new CustomizedNavigationBar. But still it is not working..
As another test I downloaded an example project where the background image should be changed. But even with that sample code it was not working.
What am I doing wrong? I am using IOS 5. Any suggestions or other ways I could define a background image?
Thanks for your answers!
Starting in iOS 5 you should use the -setBackgroundImage:forBarMetrics: method:
[myNavbar setBackgroundImage:[UIImage imageNamed: #"UINavigationBarBackground.png"]
forBarMetrics:UIBarMetricsDefault];
And in Swift 4:
navigationBar.setBackgroundImage(UIImage(named: "UINavigationBarBackground.png"),
for: .default)
Considering all iOS versions, this seems to be accomplishing both Custom background image and Custom size of UINavigationBar:
#interface CustomNavigationBar : UINavigationBar
#end
#implementation CustomNavigationBar
-(void) drawRect:(CGRect)rect
{
UIImage *image = [UIImage imageNamed: #"navigationBar"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
//for iOS5
[self setBackgroundImage:[UIImage imageNamed: #"navigationBar"] forBarMetrics:UIBarMetricsDefault];
}
//for custom size of the UINavigationBar
- (CGSize)sizeThatFits:(CGSize)size {
CGSize newSize = CGSizeMake(320,31);
return newSize;
}
#end
I use such codes in a common place like a Utilities file.
Hope this helps.
Just try with this code.. In your implmentation (.m) file.
#import "RootViewController.h"
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"navheader.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
#implementation RootViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.title=#"You are Done.";
}
This has worked in excellent way for me.
Above Code Worked for only IOS 4. if you use the IOS 5 then use.....
[myNavbar setBackgroundImage:[UIImage imageNamed:
#"UINavigationBarBackground.png"]
forBarMetrics:UIBarMetricsDefault];
You can add UIImageview to navigation bar as follows
UINavigationBar navBar = [[UINavigationBar alloc]init];
[navBar addSubview: imageview];
[navBar release];
You can check the post :
iPhone - NavigationBar Custom Background
You can also try this for Navigationbar Background.
UINavigationBar *navigationBar = self.navigationController.navigationBar;
UIImage *image = [UIImage imageNamed: #"NavBar-Wood.png"];
[navigationBar setBackgroundImage:image forBarMetrics: UIBarMetricsDefault];
self.navigationController.navigationBar.tintColor = [UIColor brownColor];
Thanks.
Another way to set an image in navigation bar is,
UIImage* logo = [ UIImage imageNamed:#"Logo.png" ];
UIImageView* logoView = [[[UIImageView alloc] initWithImage:logo] autorelease];
self.navigationItem.titleView = logoView;
This will not change whole navigation bar background. But adds a background image centered on the navigation bar, which is sometime more intended (what I was looking for).
Well for iOS 4 there is a simple solution, like:
nvc.navigationBar.layer.contents = (id)img.CGImage;

How to set an image as Navigation Bar Background without using a drawRect category?

I would like to set an image background to the navigation bar on my iphone app. Most solutions suggest using drawRect in a category like:
#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
However, Apple does not recommend this. Any other suggestion?
Thx for helping,
Stephane
Apple strongly advise us to use subclasses rather than categories (WWDC 2011, Session 123).
Create a subclass which implements the drawRect: method and set the class of your navigation bar to your custom class:
if you're working in Interface Builder, change the class in the inspector
if you create a stand-alone navigation bar (without nav controller), instantiate your custom class
if you create a navigation controller programmatically, you could take advantage of the ObjC runtime.
Class switch at runtime:
#import <objc/runtime.h>
...
object_setClass(theNavController.navigationBar, [CustomNavigationBar class]);
You should also avoid using [UIImage imageNamed:...] each time in drawRect: as it might have an impact on performance (for animations). Cache it in an ivar:
if (!bgImage) {
bgImage = [[UIImage imageNamed:#"NavigationBar.png"] retain];
}
[bgImage drawInRect:...];
(and release it in dealloc)
Note: As iOS 5 is still under NDA, I can't mention how you could easily add a background image. Check out the docs for UINavigationBar.
Tested Code : 100 % works
in ur ViewDidLoad
UIImageView *iv=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"urNavBarImage.png"]];
self.navigationItem.titleView = iv;
[iv release];
NOTE:urNavBarImage should be exact size of Navigation Bar. Like this u can change every Viewcontroller Navigation bar.
I have created a custom category for UINavigationBar as follows
UINavigationBar+CustomImage.h
#import <UIKit/UIKit.h>
#interface UINavigationBar (CustomImage)
- (void) setBackgroundImage:(UIImage*)image;
- (void) clearBackgroundImage;
- (void) removeIfImage:(id)sender;
#end
UINavigationBar+CustomImage.m
#import "UINavigationBar+CustomImage.h"
#implementation UINavigationBar (CustomImage)
- (void) setBackgroundImage:(UIImage*)image {
if (image == NULL) return;
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.frame = CGRectMake(110,5,100,30);
[self addSubview:imageView];
[imageView release];
}
- (void) clearBackgroundImage {
NSArray *subviews = [self subviews];
for (int i=0; i<[subviews count]; i++) {
if ([[subviews objectAtIndex:i] isMemberOfClass:[UIImageView class]]) {
[[subviews objectAtIndex:i] removeFromSuperview];
}
}
}
#end
I invoke it from my UINavigationController
[[navController navigationBar] performSelectorInBackground:#selector(setBackgroundImage:) withObject:image];

UIButton border and background image

What I Want: A border indicating if a UIButton is selected or not.
Background: I've got some UIButtons using transparent images, not text. These are toggle buttons (i.e. can be on or off).
Problem: The UIButton class gives users no indication of whether a button is selected or not unless you change something else about the button. Since the image doesn't change with the state, I'd need two of every image, one normal, one selected and set one for each state of the button. This is annoying. I thought instead I'd change the background image, but this removes the pretty border on the button, I just get a rectangle of my background image, yuck.
Possible solutions I don't like:
1) Create a background that matches the UIButton border and use that for selected. I don't like this because they wont match perfectly and I'm picky.
2) Create two images for each button, essentially identical but with a different background. This seems like unnecessary work, and since this problem is coming up repeatedly, I want a solution for the future as well.
I hope somebody's figured out a decent solution to this by now. Thanks in advance.
Since UIButton has two image layers, an image and a background image, I think you could accomplish what you want by using just two background images for all your buttons. One image shows a border and the other does not. Swap the backgrounds out when the control state changed.
//
// TabBarSingleton.h
#import <Foundation/Foundation.h>
#interface TabBarSingleton : UITabBarController <UITabBarControllerDelegate>{
NSRecursiveLock *barLock;
UIButton *Button;
UIButton *favoriteButton;
}
#property(nonatomic, retain) UIButton *Button;
#property(nonatomic, retain) UIButton *favoriteButton;
- (void) ButtonPressed;
- (void) favoriteButtonPressed;
#end
///////////////////////////////////
If you want the the borders only, then you have only one choice of using two images for the two states otherwise if your purpose is to differentiate between two states then you can do it by changing alpha a little bit of the selected button this will give the effect like toggle buttons, you can also disable the selected button and enable it again when the other button is pressed.
Hope this will give you a fair idea.
//
// TabBarSingleton.m
// Created by ArunDhwaj on 9/7/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import "TabBarSingleton.h"
#implementation TabBarSingleton
#synthesize Button, favoriteButton;
- (id) init
{
if (self = [super initWithNibName: nil bundle: nil])
{
barLock = [[NSRecursiveLock alloc] init];
}
self.delegate = self;
return self;
}
+ (TabBarSingleton *) defaultBar
{
}
- (void)viewDidLoad
{
NSLog(#"TabBarSingleton: viewDidLoad");
//Hiding TabBar
self.tabBar.hidden = YES;
//Creating a UIView, its frame is same as tabBar frme
CGRect tabbarFrame = self.tabBar.frame;
UIView* customTabbarView = [[UIView alloc] initWithFrame:tabbarFrame];
UIImageView *newsFeedImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"newsfeeds_normal.png"]];
newsFeedImg.frame = CGRectOffset(newsFeedImg.frame, 0, 1);
Button = [UIButton buttonWithType:UIButtonTypeCustom];
[Button setFrame:newsFeedImg.frame];
[Button setBackgroundImage:newsFeedImg.image forState:UIControlStateNormal];
[Button setBackgroundImage:[UIImage imageNamed:#"newsfeeds_active.png"] forState:UIControlStateHighlighted];
[Button addTarget:self action:#selector(newsFeedsButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[customTabbarView addSubview:Button];
//[newsFeedImg release];
CGRect newsFeedFrame = newsFeedImg.frame;
UIImageView *favoriteImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"favorites_normal.png"]];
favoriteImg.frame = CGRectMake(newsFeedFrame.size.width, newsFeedFrame.origin.y, newsFeedFrame.size.width, newsFeedFrame.size.height);
favoriteButton = [UIButton buttonWithType:UIButtonTypeCustom];
[favoriteButton setFrame:favoriteImg.frame];
[favoriteButton setBackgroundImage:favoriteImg.image forState:UIControlStateNormal];
[favoriteButton setBackgroundImage:[UIImage imageNamed:#"favorites_active.png"] forState:UIControlStateHighlighted];
[favoriteButton addTarget:self action:#selector(favoriteButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[customTabbarView addSubview: favoriteButton];
//[favoriteImg release];
[self.view addSubview:customTabbarView ];
[self newsFeedsButtonPressed];
}
- (void) newsFeedsButtonPressed
{
NSLog(#"TabBarSingleton: newsFeedsButtonPressed");
self.selectedIndex = 0;
//Keeping Highlighted newsFeed tab
UIImageView *newsFeedImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"newsfeeds_active.png"]];
[Button setImage: newsFeedImg.image forState:UIControlStateNormal];
//Keeping normal others tab icons
UIImageView *favoriteImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"favorites_normal.png"]];
[favoriteButton setImage: favoriteImg.image forState:UIControlStateNormal];
}
- (void) favoriteButtonPressed
{
NSLog(#"TabBarSingleton: favoriteButtonPressed");
self.selectedIndex = 1;
//Keeping Highlighted newsFeed tab
UIImageView *newsFeedImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"newsfeeds_normal.png"]];
[Button setImage: newsFeedImg.image forState:UIControlStateNormal];
//Keeping normal others tab icons
UIImageView *favoriteImg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"favorites_active.png"]];
[favoriteButton setImage: favoriteImg.image forState:UIControlStateNormal];
#pragma mark UITabBarControllerDelegate
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
NSLog(#"TabBarSingleton: shouldSelectViewController");
return YES;
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSLog(#"TabBarSingleton: didSelectViewController");
}
- (void) dealloc
{
//[barLock release];
[super dealloc];
}
#end

Background image for navigation view

I am having problems with properly displaying background image of navigation view.
Here is the pic:
Here is the code:
- (id)initWithStyle:(UITableViewStyle)style {
if (self = [super initWithStyle:style]) {
UIImage *image = [UIImage imageNamed: #"bg_table_active.png"];
UIImageView *imageview = [[UIImageView alloc] initWithImage: image];
UIBarButtonItem *addButton = [[UIBarButtonItem alloc]
initWithTitle:NSLocalizedString(#"Settings", #"")
style:UIBarButtonItemStyleDone
target:self
action:#selector(GoToSettings)];
self.navigationItem.titleView = imageview;
self.navigationItem.rightBarButtonItem = addButton;
self.navigationItem.hidesBackButton = TRUE;
}
return self;
}
How can I make the picture stretch to the whole navigation view?
I do exactly this in my app. Within AppDelegate I have this code:
#implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect
{
UIImage *image = [UIImage imageNamed: #"custom_nav_bar.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
I modified Mike Rundle's version so that the a custom image can be set if necessary. I also merged in 40lb-suit-of-bees suggested changes. initImageDictionary needs to be called during initialisation:
//UINavigationBar+CustomImage.h
#import <Foundation/Foundation.h>
#interface UINavigationBar(CustomImage)
+ (void) initImageDictionary;
- (void) drawRect:(CGRect)rect;
- (void) setImage:(UIImage*)image;
#end
//UINavigationBar+CustomImage.m
#import "UINavigationBar+CustomImage.h"
//Global dictionary for recording background image
static NSMutableDictionary *navigationBarImages = NULL;
#implementation UINavigationBar(CustomImage)
//Overrider to draw a custom image
+ (void)initImageDictionary
{
if(navigationBarImages==NULL){
navigationBarImages=[[NSMutableDictionary alloc] init];
}
}
- (void)drawRect:(CGRect)rect
{
NSString *imageName=[navigationBarImages objectForKey:[NSValue valueWithNonretainedObject: self]];
if (imageName==nil) {
imageName=#"header_bg.png";
}
UIImage *image = [UIImage imageNamed: imageName];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
//Allow the setting of an image for the navigation bar
- (void)setImage:(UIImage*)image
{
[navigationBarImages setObject:image forKey:[NSValue valueWithNonretainedObject: self]];
}
#end
Mike Rundle and Casebash's code is great. I used [NSValue valueWithNonretainedObject:self] to avoid the copyWithZone error. Wrapping self in an NSValue object allows it to be copied into the navigationBarImages dictionary.
- (void)drawRect:(CGRect)rect
{
NSString *imageName=[navigationBarImages objectForKey:[NSValue valueWithNonretainedObject:self]];
...}
- (void)setImage:(NSString*)image
{
[navigationBarImages setObject:image forKey:[NSValue valueWithNonretainedObject:self]];
}
http://foobarpig.com/iphone/uinavigationbar-with-solid-color-or-image-background.html
Hope it helps..
You can use this also
if([self.navigationController.navigationBar respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)] ) {
//iOS 5 new UINavigationBar custom background
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"navbg_ForiPhone5_Imagename.png"] forBarMetrics: UIBarMetricsDefault];
} else {
[self.navigationController.navigationBar insertSubview:[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"navbg_ForOtherIphone_Imagename.png"]] atIndex:0];
}
`
http://developer.apple.com/iphone/library/featuredarticles/ViewControllerPGforiPhoneOS/UsingNavigationControllers/UsingNavigationControllers.html#//apple_ref/doc/uid/TP40007457-CH7
Looking at Figure 1 in that link - would it be better to set the backgroundImage on your navigationbar not your navigationitem?
UIImage *image = [UIImage imageNamed: #"navigator.png"];
[_homeNavigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
Unfortunately, there is no support for using custom background images in a navigation bar in iPhone OS 3.0 or any previous versions. The only way to customize the appearance is to set the style and tint color. Not perfect, I know.
In your code you are trying to stretch the title view of the navigation bar to "go under" the right button. But this is impossible since the three views of a navigation bar (back button, title, and right button) are supposed to be in the same layer and are adjusted to not overlap. This is a feature.
I know there are a number of third-party apps that change the background image but they are "hacking" the system and are using unsupported private API:s or assumptions of the internal data structures of the navigation bar. These programs will most likely fail (crash or display incorrectly) in future versions of iPhone OS.
You most likely don't want to mess with this. Accept the fact that that you cannot (yet) have a custom background image in navigation bars. It hurts, I know. But if you hack the system and your app fails in a future versions of the OS, Apple will pull the app from the app store and you will lose all revenue until you have changed the app. It's your call...