Multiple custom backgrounds for UIToolbar - iphone

In order to create an absolute bottomed footer on top of a tableView I found that using UIToolbar for this and adding custom views for it worked fine.
My problem is that I already use this as a toolbar for a webview, and here with another background image than I need now.
By replacing the drawRect function in UIToolbar+addition.m I have a global toolbar for this that works fine in my webviews.
How can I expand this so that I can select which version(background) to use the different places?
My UIToolbar+addition.m:
#import "UINavigationBar+addition.h"
#implementation UIToolbar (Addition)
- (void) drawRect:(CGRect)rect {
UIImage *barImage = [UIImage imageNamed:#"toolbar-bg.png"];
[barImage drawInRect:rect];
}
#end

Try creating separate .h and .m files for each "version", and import the appropriate .h into the class file you'd like it to affect.

Why not just add a barImage property to your extension?
#interface UIToolbar (Addition)
#property (nonatomic, retain) UIImage *barImage;
#end
Then, in your implementation (I'm doing this assuming you're not using ARC. If you are, obviously remove the retain/release stuff):
#implementation UIToolbar (Addition)
#synthesize barImage = _barImage;
//Override barImage setter to force redraw if property set after already added to superView...
- (void)setBarImage:(UIImage *)barImage {
if (_barImage != barImage) {
UIImage *oldBarImage = [_barImage retain];
_barImage = [barImage retain];
[oldBarImage release];
//Let this UIToolbar instance know it needs to be redrawn in case you set/change the barImage property after already added to a superView...
[self setNeedsDisplay];
}
}
- (void) drawRect:(CGRect)rect {
[self.barImage drawInRect:rect];
}
//If you're not using ARC...
- (void)dealloc {
[barImage release];
[super dealloc];
}
#end
Now, all you have to do is set the barImage property after instantiating your UIToobar. e.g.:
UIToolBar *myToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)]; //Or whatever frame you want...
myToolbar.barImage = [UIImage imageNamed:#"toolbar-bg.png"];
[self.view addSubView:myToolbar];
[myToolbar release];
And, if you want to change it after it's already onscreen, you can do so by just setting the barImage property to a new UIImage.
Looks like it's been a year since this question was posted, but hopefully this might help someone.

Related

How to add a UIImage with UIGesture to most views in an application.

I want to make an Advertising banner in my app. A bit like iAd's.
I was going to make it by having a UIImage on the view then assigning the banner image. I would then add a touch gesture so the user could click it and go to another view in my app. I Know That I can do this on one view quite easily but I want this to be on most views in the app. Whats the best way for adding the banner to more than one view with out writing the same code more that once?
The below design shows the sort of banner im after.
Thanks
#import <UIKit/UIKit.h>
#class custom;
#protocol adDelegate
- (void)viewAd:(NSString *)adRate;
#end
#interface custom : UIView
#property (strong, nonatomic) UIImage *viewImage;
#property (assign) id <adDelegate> delegate;
#end
// Main class
#import "custom.h"
#implementation custom
#synthesize viewImage;
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = viewImage;
[self addSubview:imageView];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.delegate viewAd:#"view"];
}
You can Create a UIView Class and call it BannerView for instance.
// in the bannerView.h
#import <UIKit/UIKit.h>
#interface BannerView : UIView{
UIImageView* bannerImage;
}
#property(nonatomic,retain) UIImageView* bannerImage;
#end
//in the bannerView.m
#import "BannerView.h"
#implementation BannerView
#synthesize bannerImage;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
bannerImage=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"banner-image.png"]];
bannerImage.frame=CGRectMake(0, 0, 320, 100);
[self addSubview:bannerImage];
// add a uibutton on top of the uiimageview and assign an action for it
// better than creating an action recogniser
UIButton* actionButton=[UIButton buttonWithType:UIButtonTypeCustom];
actionButton.frame=CGRectMake(0, 0, 320, 100);
[actionButton addTarget:self action:#selector(yourAction) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:actionButton];
}
-(void) yourAction{
// do what ever here like going to an other uiviewController as you mentionned
}
#end
Now you can call this view from any View Controller this way
BannerView* banner=[[BannerView alloc] initWithFrame:CGRectMake(0, 300, 320, 100)];
[self.view addSubview:banner];
Try creating a parent class from UIView where you do all the display and handling of the banner using your UIImageView and gesture recognizers. Then whichever views need this functionality, derive them from this parent class, and override default handling in method so that you can customize the behavior in your child class.
A few suggestions:
First, why not just use a UIButton instead of a UIImage with a Gesture? All you're really doing is replicating button functionality after all...
Second, I'd probably tackle the overall problem by creating a class that includes the UIButton, like so:
#interface YourSuperViewController : UIViewController
#property (nonatomic, strong) IBOutlet UIButton *adButton;
- (IBAction)adTouched:(id)sender;
#end
In the viewDidLoad for this class, create the button, and add it to the view, and add your ad-specific logic to the adTouched action.
Then create the rest of the views in your app as an instance of YourSuperViewController. Like so:
#interface SomeOtherViewController : YourSuperViewController
Now the SomeOtherViewController will auto-magically have the ad button and respond to a touch on it properly. Done!
What everyone else has said is the best way. If you need custom functionality, subclassing is probably the way to go.
I just wanted to add one pedantic thing. Its important to remember that a UIImage is not a view. There has never been a UIImage on the screen, ever. A UIImage is a model object. It is just a collection of data. A UIImageView is a view object and as such, a UIImageView can display itself on the screen.
This might seem overly pedantic and nitpicky, but its important to have these things sorted out in our heads in order to effectively use MVC (model, view, controller)

Adding an overlay to a grid tableView

I have a tableview which each row has 4 images. I have implemented a share option which will allow the user to select multiple images. How can I add an overlay or some kind of visual effect to show that the image is selected?
I would like to add some overlay to display that an image is selected, but How would this be done without adding a new set of subview for each thumbnail? And once that is done, how would the selection of the new views be linked back to the images behind them so that they can be added to an array?
Or is there an easier way to do this?
Thanks
Depending on how you're implementing this grid view, it might make sense to track all of the selecting and deselecting at that level.
As for the overlay, the quick and dirty way is to subclass UIImageView, add a BOOL property called selected. Then you can override the setter for selected and handle showing or hiding your overlay view.
Here's how I would setup my subclass. First the interface:
#interface SelectableImageView : UIImageView
#property (nonatomic, assign, getter = isSelected) BOOL selected;
#end
and the implementation...
#interface SelectableImageView ()
#property (nonatomic, retain) UIView *overlayView;
#end
#implementation SelectableImageView
#synthesize selected;
#synthesize overlayView;
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
overlayView = [[UIView alloc] initWithFrame:frame];
overlayView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.75];
overlayView.hidden = YES;
[self addSubview:overlayView];
}
return self;
}
- (void)setSelected:(BOOL)flag
{
selected = flag;
self.overlayView.hidden = !flag;
}
- (void)dealloc
{
[overlayView release], self.overlayView = nil;
[super dealloc];
}
#end

Using TTImageView with AQGridView

I created a custom AQGridViewCell.
By using UIImageView everything works. The image appears and is clickable, but when I change the UIImageView to TTImageView I can't click on the image.
The same example as here below by just changing the imageview to UIImageView and the the setter message to an image, everything works as expected.
Here is my ImageGridCell.h
#import "AQGridViewCell.h"
#import <Three20/Three20.h>
#interface ImageGridCell : AQGridViewCell
{
TTImageView * _imageView;
NSString *urlPath;
}
#property (nonatomic, retain) NSString *urlPath;
#end
And here is my ImageGridCell.m
#import "ImageGridCell.h"
#implementation ImageGridCell
#synthesize urlPath;
- (id) initWithFrame: (CGRect) frame reuseIdentifier: (NSString *) aReuseIdentifier
{
self = [super initWithFrame: frame reuseIdentifier: aReuseIdentifier];
if ( self == nil )
return ( nil );
_imageView = [[TTImageView alloc] initWithFrame: CGRectMake(0, 0, 100, 100)];
[_imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.contentView addSubview: _imageView];
return ( self );
}
- (void) dealloc
{
[_imageView release];
[super dealloc];
}
- (CALayer *) glowSelectionLayer
{
return ( _imageView.layer );
}
- (UIImage *) image
{
return ( _imageView.image );
}
-(void)setUrlPath:(NSString *)urlpath {
_imageView.urlPath = urlpath;
}
I faced the same situation, and here is the solution I came with.
My AQGridViewCell have both a TTImageView and a UIImageView.
I use the TTImageView to load the image, and when it's loaded, I pass it to the UIImageView which displays it.
To do that, my AQGridViewCell subclass implements the TTImageViewDelegate protocol. In my PostGridViewCell.h, I have :
#interface PostGridViewCell : AQGridViewCell <TTImageViewDelegate> {
#private
TTImageView *ttImageView_;
UIImageView *imageView_;
}
// #property here …
#end
In my implementation file, in the initWithFrame:reuseIdentifier: method, I init both my ttImageView and my imageView, but I only add the imageView as a subview of my cell. And I set the ttImageView delegate property to self (so I can be notified when the image is loaded) :
- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)aReuseIdentifier {
if ((self = [super initWithFrame:frame reuseIdentifier:aReuseIdentifier])) {
// init image
ttImageView_ = [[TTImageView alloc] initWithFrame:CGRectZero];
ttImageView_.delegate = self;
imageView_ = [[UIImageView alloc] initWithFrame:CGRectZero];
[self.contentView addSubview:imageView_];
// …
}
return self;
}
When the TTImaveView has loaded the image, it calls the imageView:didLoadImage: method on the delegate (which is the PostGridViewCell). In my implementation file, I have this method :
- (void)imageView:(TTImageView*)imageView didLoadImage:(UIImage*)image {
self.imageView.image = image;
[self setNeedsLayout];
}
Does it help?
Cheers,
Thomas.
Not quite sure what you mean by "clickable" - like a UIButton? What behavior do you want to accomplish when a user taps one of the images?
I can't see the immediate problem here, but I can give you information that may help.
Foremost, TTImageView and UIImageView both descend from UIView, TTImageView does not descend from UIImageView (important to note):
UIImageView : UIView
TTImageView : TTView : UIView
We don't know what is going on behind the scenes in UIImageView, so I can't see what is different there, but the documentation seems to make clear that the UIImageView's default behavior for userInteractionEnabled is NO - which is odd, because you are saying that it is working with UIImageView, not the other way around.
Have you tried setting the userInteractionEnabled property to YES on TTImageView?
The problem here is that TTImageView has userInteractionEnabled to YES. So the touch event is being held by the TTImageView, and AQGridView expects touches in cell Views. Simply add userInteractionEnabled=NO to the TTImageView in your cell and you are ready to go.

Programmatically Displaying a UILabel from another class

I'm re-factoring my code and would like to move a whole bunch of UILabels into another class to tidy things up a bit. I'm missing one puzzle piece to be able to do so though (or maybe I'm just tired lol) Anyway here's the simplified code showing my issue. Thanks in advance to anyone who helps :)
#interface MyClass : UIView {
UILabel *classLabel;
}
#property (assign) UILabel *classLabel;
#end
#implementation MyClass
#synthesize classLabel;
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
}
return self;
}
- (void)dealloc {[super dealloc];}
#end
#interface LabelTestViewController : UIViewController {
MyClass *myClassInstance;
UILabel *myLabel;
}
#property (assign) UILabel *myLabel;
#end
#implementation LabelTestViewController
#synthesize myLabel;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// this shows a label on the screen as expected
myLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 50, 20)];
myLabel.text = #"Hello";
[self.view addSubview:myLabel];
[myLabel release];
// this doesn't show anything on the scree
myClassInstance = [MyClass new];
[myClassInstance drawRect:CGRectMake(10, 50, 50, 20)]; // I suspect I need to call a different method, just don't know which one. initWithFrame is what I used at the time of creation of the label in the previous working scenario. is there an equivalent?
myClassInstance.classLabel.text = #"Goodbye";
[self.view addSubview:myClassInstance.classLabel];
}
- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];}
- (void)viewDidUnload {}
- (void)dealloc {[super dealloc];}
#end
A couple of things:
1) You should never call drawRect directly. Instead, call setNeedsDisplay or setNeedsDisplayInRect. See the Cocoa Drawing Guide or the UIView Class Reference for more info.
2) But that may not be source of your problem. From your code, it is difficult to tell what ends up in classLabel after you are done setting it up, but I expect it's not what you need. In particular, it needs a frame. I would suggest setting a CGRect variable to myClassLabel.frame and seeing what you end up with.

Custom colors in UITabBar

Is it possible to use custom colors and background images in a UITabBar? I realize that Apple would like everyone to use the same blue and gray tab bars, but is there any way to customize this?
Second, even I were to create my own TabBar-like view controller, along with custom images, would this violate Apple's Human Interface Guidelines?
I found an answer to this at Silent Mac Design.
I implemented this way:
First make a subclass of UITabBarContoller
// CustomUITabBarController.h
#import <UIKit/UIKit.h>
#interface CustomUITabBarController: UITabBarController {
IBOutlet UITabBar *tabBar1;
}
#property(nonatomic, retain) UITabBar *tabBar1;
#end
 
// CustomUITabBarController.m
#import "CustomUITabBarController.h"
#implementation CustomUITabBarController
#synthesize tabBar1;
- (void)viewDidLoad {
[super viewDidLoad];
CGRect frame = CGRectMake(0.0, 0, self.view.bounds.size.width, 48);
UIView *v = [[UIView alloc] initWithFrame:frame];
[v setBackgroundColor:[[UIColor alloc] initWithRed:1.0
green:0.0
blue:0.0
alpha:0.1]];
[tabBar1 insertSubview:v atIndex:0];
[v release];
}
#end
And in your Nib file replace the class of your TabBar Controller with CustomUITabBarController.
FYI, from iOS 5 onwards you can customize various aspects of the UITabBar, including setting its background image using the backgroundImage property.
The new UITabBar "Customizing Appearance" properties in iOS 5 are:
backgroundImage
selectedImageTintColor
selectionIndicatorImage
tintColor
Given that Apple have introduced these methods in iOS 5, then it's possible they may be more sympathetic to attempts to customize the UITabBar for earlier OSes. This website says the Twitter app uses a custom tab bar, so that might be more reason that Apple would let such an app into the App Store, it's no guarantee though!
Use Following images ( Assuming, tabBar is having 5 Tabs as follows )
Create a new project using - "TabBar Application" template & Place following code.
Contents of AppDel.h File.
#import <UIKit/UIKit.h>
#interface cTabBarAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#property (nonatomic, retain) IBOutlet UIImageView *imgV;
#end
Contents of AppDel.m File.
#import "cTabBarAppDelegate.h"
#implementation cTabBarAppDelegate
#synthesize window=_window;
#synthesize tabBarController=_tabBarController;
#synthesize imgV = _imgV;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.tabBarController.delegate=self;
self.imgV.frame=CGRectMake(0, 425, 320, 55);
[self.tabBarController.view addSubview:self.imgV];
self.tabBarController.selectedIndex=0;
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController{
NSUInteger index=[[tabBarController viewControllers] indexOfObject:viewController];
switch (index) {
case 0:
self.imgV.image=[UIImage imageNamed:#"tBar1.png"];
break;
case 1:
self.imgV.image=[UIImage imageNamed:#"tBar2.png"];
break;
case 2:
self.imgV.image=[UIImage imageNamed:#"tBar3.png"];
break;
case 3:
self.imgV.image=[UIImage imageNamed:#"tBar4.png"];
break;
case 4:
self.imgV.image=[UIImage imageNamed:#"tBar5.png"];
break;
default:
break;
}
return YES;
}
At the beginning of ***ViewController.m add the following might help set background image of UITabBar.
#implementation UITabBar (CustomImage)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed: #"background.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
#end
If you want to use custom colors for the icons (and not just the background) instead of the default gray and blue, do it like this: http://blog.theanalogguy.be/2010/10/06/custom-colored-uitabbar-icons/
Basically, you need to create complete tabbar images (background and icons and text) for each selected tab and set your UITabBarItems to no icon and no title and insert the image into the tabbar as an UIImageView in viewWillAppear:
And Apple won't mind since we are not using any private APIs.
Since iOS 7.0, you can use -[UIImage imageWithRenderingMode:] with UIImageRenderingModeAlwaysOriginal to preserve colors:
// Preserve the colors of the tabs.
UITabBarController *controller = (UITabBarController *)((UIWindow *)[UIApplication sharedApplication].windows[0]).rootViewController;
NSArray *onIcons = #[ #"tab1-on", #"tab2-on", #"tab3-on" ];
NSArray *offIcons = #[ #"tab1-off", #"tab2-off", #"tab3-off" ];
NSArray *items = controller.tabBar.items;
for (NSUInteger i = 0; i < items.count; ++i) {
UITabBarItem *item = items[i];
item.image = [[UIImage imageNamed:offIcons[i]] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
item.selectedImage = [[UIImage imageNamed:onIcons[i]] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
}
Works like a charm.
In AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UITabBar appearance] setSelectedImageTintColor:[UIColor redColor]];
return YES;
}
As far as the UITabBar class is concerned, the icons in the bar are limited to the colours: blue for selected and grey for unselected. This is because the tab bar only uses the alpha value from the icons you supply to create the image on the bar.
The bar itself is limited to being black, as far as I can remember. I've not seen anything like the 'tint' property on UINavigationBar in the docs.
I guess you could go ahead and create your own tab bar style class and do what you want with it, but I have absolutely no idea how that fits in with Apple's HIG, or whether or not they'd challenge it during the review process.
In my experience, Apple reviewers only rejected my app if I didn't use THEIR UI elements according to the HIG. They might have a different view when it's your own UI elements you're playing with.
Here's the document that says we can't change pressed or selected appearance with our icons.
https://developer.apple.com/iphone/library/documentation/UserExperience/Conceptual/MobileHIG/IconsImages/IconsImages.html#//apple_ref/doc/uid/TP40006556-CH14-SW1
It's under the heading
Icons for Navigation Bars, Toolbars, and Tab Bars
Its possible without adding any subView.
In the class where you define the tab bar set the property of the
tabBarItem to ->>
UITabBarItem *tabBarItem1 = [[self.tabBar.tabBar items] objectAtIndex:0];
[tabBarItem1 setFinishedSelectedImage:[UIImage imageNamed:#"campaigns_hover.png"] withFinishedUnselectedImage:[UIImage imageNamed:#"campaigns.png"]];
Its a property of tabBarItem and u can change the default blue image to a custom image.
campaigns_hover.png is the selected custom image AND
campaigns.png is the custom image when not selected...
Enjoy the secret.. :)
The below code helps you to add custom colors with RGB values to ur tabBar.
self.tabBarController.tabBar.tintColor = [[UIColor alloc] initWithRed:0.00
green:0.62
blue:0.93
alpha:1.0];
You can do that without -insertSubview:atIndex, because a new UIView is not needed. You can apply a theme using QuartzCore on each view (UITabBar and it's subviews). So the UITabBar's background is added as I've described here.
Now we must apply the image on each UITabBarItem as it's background:
// UITabBar+CustomItem.h
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface UITabBar (CustomItem)
-(void)setSelectedItemBackground:(UIImage *)backgroundImage;
#end
Now the .m file:
// UITabBar+CustomItem.m
#implementation UITabBar (CustomItem)
#define kItemViewTag 445533 // <-- casual number
#define kItemViewOldTag 445599 // <-- casual number different from the above
-(void)setSelectedItemBackground:(UIImage *)backgroundImage {
UIView *oldView = [self viewWithTag:kImageViewItemTag];
oldView.layer.contents = nil; // <-- remove the previous background
oldView.tag = kItemViewOldTag; // <-- this will avoid problems
NSUInteger index = [self.items indexOfObject:self.selectedItem];
UIView *buttonView = [self.subviews objectAtIndex:index];
buttonView.tag = kItemViewTag;
buttonView.layer.contents = (id)backgroundImage.CGImage; // <-- add
// the new background
}
#end
You can also change the color of the selected images, as someone made here. But what I'm wondering is: can I change the color of the selected label? The answer is yes, as described below (the following works on ios 3.x/4.x, not iOS5+):
#implementation UITabBar (Custom)
#define kSelectedLabel 334499 // <-- casual number
-(void)changeCurrentSelectedLabelColor:(UIColor *)color {
UIView *labelOldView = [self viewWithTag:kSelectedLabel];
[labelOldView removeFromSuperview];
NSString *selectedText = self.selectedItem.title;
for(UIView *subview in self.subviews) {
if ([NSStringFromClass([subview class])
isEqualToString:#"UITabBarButton"]) {
for(UIView *itemSubview in subview.subviews) {
if ([itemSubview isKindOfClass:[UILabel class]]) {
UILabel *itemLabel = (UILabel *)itemSubview;
if([itemLabel.text isEqualToString:selectedText]) {
UILabel *selectedLabel = [[UILabel alloc]
initWithFrame:itemLabel.bounds];
selectedLabel.text = itemLabel.text;
selectedLabel.textColor = color;
selectedLabel.font = itemLabel.font;
selectedLabel.tag = kSelectedLabel;
selectedLabel.backgroundColor = [UIColor clearColor];
[itemSubview addSubview:selectedLabel];
[selectedLabel release];
}
}
}
}
}
}
#end