I'm trying to show a badge on some UITabBarItems that are configured with an image with UIImageRenderingModeAlwaysOriginal. I'm doing this because the desired effect is something like a raised UITabBarItem a-la Instagram. This is the code I'm using to test this (it's in a UITabBarController category):
- (void) replaceImageForTab:(int)itemIndex
defaultImage:(UIImage *)defaultImage
selectedImage:(UIImage *)selectedImage
title:(NSString *)title
{
NSArray *viewControllers = [self viewControllers];
UIViewController *vc = [viewControllers objectAtIndex:itemIndex];
[vc.tabBarItem setImage:[defaultImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
[vc.tabBarItem setSelectedImage:[selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
[vc.tabBarItem setTitle:title];
[vc.tabBarItem setBadgeValue:#"1"];
}
However, the badges images are shown clipped, like shown in here:
I think is because the badge is being added within the UITabBar's UIView, and it is clipping its contents. But I can't modify that value since that's a private property. What other options do I have here?
I haven't found a way to change the offset in the stock badges, so I had to roll out my own solution. It's not pretty, but it works (this is, again, on a UITabBarController category):
- (void) v_setBadgeValue:(NSString *)badgeValue inTab:(NSInteger)tabIndex
{
NSArray *viewControllers = [self viewControllers];
UIViewController *vc = [viewControllers objectAtIndex:tabIndex];
UITabBarItem *tabBarItem = vc.tabBarItem;
UIImage *normalImage = tabBarItem.image;
UIImage *selectedImage = tabBarItem.selectedImage;
UIImage *badgedNormalImage = [[self class] addBadgeWithValue:badgeValue toImage:normalImage];
UIImage *badgedSelectedImage = [[self class] addBadgeWithValue:badgeValue toImage:selectedImage];
[tabBarItem setImage:[badgedNormalImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
[tabBarItem setSelectedImage:[badgedSelectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
}
+ (UIImage *) addBadgeWithValue:(NSString *)badgeValue toImage:(UIImage *)image
{
//Here you have to create your badge view. Let's just use a red square for now
UIView *badgeView = [[UIView alloc] initWithFrame:CGRectMake(16, 3, 14, 14)];
[badgeView setBackgroundColor:[UIColor redColor]];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[imageView addSubview:badgeView];
UIGraphicsBeginImageContextWithOptions((imageView.bounds.size), NO, [[UIScreen mainScreen] scale]);
[imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
Now you're in complete control of the badges offset and position, that's important if you, like me, have a very strange UITabBar to work with. This is the result:
Related
I have self view with image view:
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds];
[self setBluredImageView:imageView];
[self addSubview:imageView];
- (UIImage *)takeSnapshotOfView:(UIView *)view
{
CGFloat reductionFactor = 1;
UIGraphicsBeginImageContext(CGSizeMake(view.frame.size.width/reductionFactor, view.frame.size.height/reductionFactor));
[view drawViewHierarchyInRect:CGRectMake(0, 0, view.frame.size.width/reductionFactor, view.frame.size.height/reductionFactor) afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
In method when I want to show my self view over other view I make this:
- (void)showMe; {
AppDelegate* app = [AppDelegate shared];
[app.window addSubview:self];
UIImage *image = [self blurWithImageEffects:[self takeSnapshotOfView:app.window]];
[[self bluredImageView] setImage:image];
[UIView animateWithDuration:0.4 animations:^{
[self setAlpha:1.0];
}];
}
So as you can see I want to blur "graphic context" that based on main window view. At first time when I present my self view it works perfect, but then, something like blurred image multiply each other.
So this is image when I just first show my view:
When I present my view few times the blurred image looks like this:
So as you can see each time blurred screenshot is different, but I use the same method for getting screenshot and don't update content of the view controller or other ui parts.
Some methods and image categories found here.
You've created a loop of blurring. When you take the screenshot of the view a second time, the bluredImageView is also in the screenshot. That is why you see the effect multiplied. Try removing it and only capturing the context without the effect, then adding it back
- (UIImage *)takeSnapshotOfView:(UIView *)view
{
//Remove the blured image before taking another screenshot.
[self bluredImageView] removeFromSuperview];
CGFloat reductionFactor = 1;
UIGraphicsBeginImageContext(CGSizeMake(view.frame.size.width/reductionFactor, view.frame.size.height/reductionFactor));
[view drawViewHierarchyInRect:CGRectMake(0, 0, view.frame.size.width/reductionFactor, view.frame.size.height/reductionFactor) afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//Add it back now that the effect is done
[self addSubview:[self bluredImageView];
return image;
}
I am setting the SelectionIndicatorImage as a stretchable image (to accommodate for various device widths) via UITabBar appearance:
UIImage *selectedImage = [[UIImage imageNamed:#"SelectedTab"] stretchableImageWithLeftCapWidth:0 topCapHeight:0];
[[UITabBar appearance] setSelectionIndicatorImage:selectedImage];
As a result I get a 2pt padding on the screen edges. I am basically trying to use selection indicator as a background for currently selected UITabBarItem.
http://i.stack.imgur.com/qFHEk.png
What would be an easy way to solve this?
If you want to use UITabBarController in your app with selection and unselection feature then you should this code
[[self.tabBarController tabBar] setBackgroundImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"bg_tabBar" ofType:#"png"]]];
[[self.tabBarController tabBar] setSelectionIndicatorImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"bg_tabItem_selected" ofType:#"png"]]];
NSArray *arrTabItems = [[self.tabBarController tabBar] items];
UITabBarItem *tabBarItem = [arrTabItems objectAtIndex:0];
[tabBarItem setFinishedSelectedImage:[UIImage imageNamed:#"img_selected.png"] withFinishedUnselectedImage:[UIImage imageNamed:#"img_unselected.png"]];
[tabBarItem setImageInsets:UIEdgeInsetsMake(5, 5, 0, 5)];
I've tried something similar. Ended up this way:
Subclass UITabBarController and add property:
#property(nonatomic, readonly) UIImageView *imageView;
Setup your image as you want:
- (void)viewDidLoad {
[super viewDidLoad];
_imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"tabbaritem"]];
[self.tabBar addSubview:self.imageView];
}
Place your subview in correct position:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// get all UITabBarButton views
NSMutableArray *tabViews = [NSMutableArray new];
for (UIView *view in self.tabBar.subviews) {
if ([view isKindOfClass:[UIControl class]]) {
[tabViews addObject:[NSValue valueWithCGRect:view.frame]];
}
}
// sort them from left to right
NSArray *sortedArray = [tabViews sortedArrayUsingComparator:^NSComparisonResult(NSValue *firstValue, NSValue *secondValue) {
CGRect firstRect = [firstValue CGRectValue];
CGRect secondRect = [secondValue CGRectValue];
return CGRectGetMinX(firstRect) > CGRectGetMinX(secondRect);
}];
// place your imageView on selected index button frame
CGRect frame = self.tabBar.bounds;
CGSize imageSize = CGSizeMake(CGRectGetWidth(frame) / self.tabBar.items.count, self.imageView.image.size.height);
CGRect selectedRect = sortedArray.count > self.selectedIndex ? [sortedArray[self.selectedIndex] CGRectValue] : CGRectZero;
[self.imageView setFrame:CGRectIntegral(CGRectMake(CGRectGetMinX(selectedRect), CGRectGetMaxY(frame) - imageSize.height,
CGRectGetWidth(selectedRect), imageSize.height))];
}
Then you only need to refresh layout on each tab change:
- (void)setSelectedIndex:(NSUInteger)selectedIndex {
[super setSelectedIndex:selectedIndex];
[self.view setNeedsLayout];
}
- (void)setSelectedViewController:(UIViewController *)selectedViewController {
[super setSelectedViewController:selectedViewController];
[self.view setNeedsLayout];
}
Old answer how to remove padding (this is what I firstly understood as your question, I'm leaving it as a reference for other people).
In iOS 7+ UITabBar has following property:
/*
Set the itemSpacing to a positive value to be used between tab bar items
when they are positioned as a centered group.
Default of 0 or values less than 0 will be interpreted as a system-defined spacing.
*/
#property(nonatomic) CGFloat itemSpacing NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;
This should enable you to get rid of padding between items.
I have added an ImageView as a subview of my TabBar (that has three NavigationControllers). When i tap on any of the tabs of the tabBarController, then the Image on the imageView changes accordingly (the image shows that particular tab selected and others unselected).
However, the image always shows an extra line on the tabBar. It looks like it crosses the limit of the tabBar. The dimensions of my image is 320x64 pixels (for non-retina iPhone) and 640x128 pixels (for retina iPhone).
Here's how i am declaring instance var for the image view and the tabBarController.
#interface HomePageViewController ()<UITabBarControllerDelegate>
{
UIImageView* tabBarView;
UITabBarController *tabBarController;
}
-(UITabBarController *) configureTheTabBarControllerWithNavControllerAtIndex:(NSInteger)index
{
UINavigationController *customerCareNavController;
UINavigationController *accAndContactsNavController;
UINavigationController *purchaseOrderNavController;
CustomerCareViewController *custCareVC;
PurchaeOrderViewController *POController;
AccountsAndContactsViewController *accAndContactsController;
custCareVC = [[CustomerCareViewController alloc] initWithNibName:#"CustomerCareViewController_iPhone" bundle:NULL];
POController = [[PurchaeOrderViewController alloc] initWithNibName:#"PurchaeOrderViewController_iPhone" bundle:NULL];
accAndContactsController = [[AccountsAndContactsViewController alloc] initWithNibName:#"AccountsAndContactsViewController_iPhone" bundle:NULL];
customerCareNavController = [[UINavigationController alloc] initWithRootViewController:custCareVC];
purchaseOrderNavController = [[UINavigationController alloc] initWithRootViewController:POController];
accAndContactsNavController = [[UINavigationController alloc] initWithRootViewController:accAndContactsController];
tabBarController = [[UITabBarController alloc] init];
tabBarController.viewControllers = [NSArray arrayWithObjects:customerCareNavController,accAndContactsNavController,purchaseOrderNavController, nil];
switch (index) {
case 0:
tabBarController.selectedIndex = 0;
break;
case 1:
tabBarController.selectedIndex = 1;
break;
case 2:
tabBarController.selectedIndex = 2;
break;
}
tabBarView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"tab_mypeople.png"]];
tabBarView.frame = CGRectMake(0, -15, 320, 64);
[tabBarController.tabBar addSubview:tabBarView];
tabBarController.delegate = self;
[self selectTabBarIndex:index];
[self presentViewController:tabBarController animated:YES completion:NULL];
return tabBarController;
}
-(void)tabBarController:(UITabBarController *)TBController didSelectViewController:(UIViewController *)viewController
{
NSUInteger indexSelected = [[TBController viewControllers] indexOfObject:viewController];
[self selectTabBarIndex:indexSelected];
}
- (void) selectTabBarIndex:(NSInteger)index
{
switch (index)
{
case 0:
tabBarView.image=[UIImage imageNamed:#"tab_myCalendar.png"];
break;
case 1:
tabBarView.image=[UIImage imageNamed:#"tab_myDetails.png"];
break;
case 2:
tabBarView.image=[UIImage imageNamed:#"TabBarItem_PO.png"];
break;
}
}
Please see the screenshot..
Setting the barStyle as black gives me the following result
The line has faded a little, but is still visible..
Hey i tried something and it works
tabBarView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"tab_mypeople.png"]];
tabBarView.frame = CGRectMake(0, 0, 320, tabBarController.tabBar.frame.size.height);
However, the image shows a little stretched..
Write this for no stretching: This will work like a charm..!!
tabBarController.tabBar.backgroundImage = [UIImage new];
tabBarController.tabBar.shadowImage = [UIImage new];
tabBarView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"tab_mypeople.png"]];
tabBarView.frame = CGRectMake(0, -15, 320, 64);
[tabBarController.tabBar addSubView:tabBarView];
Below code will remove line from tabbar...
if ([[[UIDevice currentDevice]systemVersion]floatValue]>=7.0) {
tabbarController.tabBar.barStyle=UIBarStyleBlackOpaque;
}
Instead of adding the image in an image view as a subview, add the image as the background image of the tab bar, and make the shadow image be an empty image (the line is the shadow image).
tabBarController.tabBar.backgroundImage = [UIImage imageNamed:#"tab_mypeople.png"];
tabBarController.tabBar.shadowImage = [UIImage new];
If, for some reason, you still need to use a subview rather then a background image, you can continue to do what you have in your question, but set both the background image and the shadow image to empty images (you can't set a custom shadow image without also setting a custom background image). This will get rid of that shadow image line.
tabBarController.tabBar.backgroundImage = [UIImage new];
tabBarController.tabBar.shadowImage = [UIImage new];
tabBarView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"tab_mypeople.png"]];
tabBarView.frame = CGRectMake(0, -15, 320, 64);
[tabBarController.tabBar addSubview:tabBarView];
write this in app delegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
UIImage *tabBackground = [[UIImage imageNamed:#"tab_bg"]resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
[[UITabBar appearance] setBackgroundImage:tabBackground];
[[UITabBar appearance] setSelectionIndicatorImage:[UIImage imageNamed:#"tab_select_indicator"]];
i have two ViewControllers, when i tap a cell i need to open other viewController and see image (image with names 1, 2, 3,...._full.jpg), so i write:
DetailViewController *dvc = [[DetailViewController alloc] init];
[dvc updateImage:[NSString stringWithFormat:#"%d", indexPath.row]];
[self.navigationController pushViewController:dvc animated:YES];
in my dvc
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
self.image = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 748)];
[self.image setTag:1];
[self.view addSubview:self.image];
}
method what i called
-(void)updateImage:(NSString*)imageName
{
[(UIImageView*)[self.view viewWithTag:1] setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%#_full.jpg", imageName]]];
}
smth like self.image.image = [UIImage imageNamed:[NSString stringWithFormat:#"%#_full.jpg", imageName]]; didn't work for me.
so, these code works fine in my IOS 5,6,7 emulators, but when i compile it on my IPad 2, IOS 5.1 my image is not updating, all i see is gray background.
What am i doing wrong?
You run
[dvc updateImage:[NSString stringWithFormat:#"%d", indexPath.row]];
After that viewDidLoad is called and your image is reseted.
Instead that pass image name as a string
[dvc setImageName:[NSString stringWithFormat:#"%d", indexPath.row]];
and in DetailViewController.m in viewDidLoad call
[self updateImage:[NSString stringWithFormat:#"%#_full.jpg", self.imageName]];
Remember to add #property imageName to your DetailViewController.h file.
omg, XCode is joking, it's all because of my images where not ".jpg" they where ".JPG"!. I'm angry...
i have 3 navigation controller and i want to change each background using different image. I was implement a category that extends UINavigationBar like this :
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed:#"background.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];}
#end
but it make every navigation bar have a same background image. And then i try to implement code like this :
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.navigationBar.tintColor = [UIColor blackColor];
UIImageView *backGroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background.png"]];
[self.navigationController.navigationBar insertSubview:backGroundView atIndex:0];
[backGroundView release];
}
in every controller but each background just show the tintColor, not the image...what should I do???
and how if i want to do that too in tabbarController??
On each of your views, implement the following in your viewWillAppear: method
#import <QuartzCore/QuartzCore.h>
...
- (void)viewWillAppear:(BOOL)animated
{
self.navigationController.navigationBar.layer.contents = (id)[UIImage imageNamed:#"yourImageBgHere"].CGImage;
}
Cheers,
Rog