UIBarButtonItem with UIImage too wide - ios

I have a UINavigationBar and a UIToolbar (in separate views) which contain buttons and flexible space. The UIBarButtonItems' background is set like this:
bgImage = [[UIImage imageNamed:#"MyBackgroundImage.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
[self setBackgroundImage:bgImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
Within the UINavigationBar, the items look just fine, optimal size and all.
BUT, within the UIToolbar, the items are always stretched to at least the width of bgImage (100 points in this case):
Any ideas why, or how to solve that? Please tell me if you need more info.

This appears to be a bug in UIToolbar. I gave it a try and the only "fix" that worked for me was to manually set the width of the UIBarButtonItem:
barButtonItem.width = 40f;
This will work fine for buttons with images, but not for text buttons as the size of the text may vary due to localization.

You can use following method for get/create image size as you want.
Use following method with specific hight and width with image
+ (UIImage*)resizeImage:(UIImage*)image withWidth:(int)width withHeight:(int)height
{
CGSize newSize = CGSizeMake(width, height);
float widthRatio = newSize.width/image.size.width;
float heightRatio = newSize.height/image.size.height;
if(widthRatio > heightRatio)
{
newSize=CGSizeMake(image.size.width*heightRatio,image.size.height*heightRatio);
}
else
{
newSize=CGSizeMake(image.size.width*widthRatio,image.size.height*widthRatio);
}
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
This method return NewImage, with specific size that you specify.
And use this Image for add on UIBarButtonItem.

Create your own button with image according to your requirement
// UIBarButtonItem+CutomBackground.h
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image title:(NSString *)buttonTitle target:(id)target action:(SEL)action;
// UIBarButtonItem+CutomBackground.m
#define TEXT_MARGIN 8.0f
#define ARROW_MARGIN 12.0f
#define FONT_SIZE 13.0f
#define IMAGE_HEIGHT 31.0f
+ (UIBarButtonItem*)barItemWithImage:(UIImage*)image title:(NSString *)buttonTitle target:(id)target action:(SEL)action
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *buttonImage = [image stretchableImageWithLeftCapWidth:15 topCapHeight:0];
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[button setBackgroundImage:buttonImage forState:UIControlStateNormal];
[button setTitle:buttonTitle forState:UIControlStateNormal];
[button setContentHorizontalAlignment:UIControlContentHorizontalAlignmentRight];
[button setContentEdgeInsets:UIEdgeInsetsMake(0.0f,0.0f,0.0f,TEXT_MARGIN)];
[button.titleLabel setFont:[UIFont fontWithName:#"Helvetica-Bold" size:FONT_SIZE]];
[button.titleLabel setShadowOffset:CGSizeMake(0.0f,-1.0f)];
CGRect buttonFrame = [button frame];
buttonFrame.size.width = [buttonTitle sizeWithFont:[button.titleLabel font]].width+ARROW_MARGIN+TEXT_MARGIN;
buttonFrame.size.height = IMAGE_HEIGHT;
[button setFrame:buttonFrame];
return [[self alloc] initWithCustomView:button];
}

Related

UIButton: set background of image view (without setting an image)

How can I change the background color of the image view on a UIButton but without setting an image? (so that I have for example a red square next to the button text).
When I simply set mybutton.imageView.backgroundColor = UIColor.red and set the constraints of the image view to 28x28px. I do not see the red square.
Thanks.
To clarify: I want to set the background color of only the image view on of the UIButton - I do not want to color the whole button!
You tagged your question as both Swift and Objective-C, so...
In Swift, you can use this extension to create a "blank" image with a specific color:
public extension UIImage {
public convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
}
Then, to add a "red square next to the button text":
let btnImage = UIImage(color: .red, size: CGSize(width: 28, height: 28))
btn.setImage(btnImage, for: .normal)
If you need to do this in Obj-C, it's the same process:
- (UIImage *)imageWithColor:(UIColor *)color andSize:(CGSize)sz {
CGRect rect = CGRectMake(0.0f, 0.0f, sz.width, sz.height);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
and
UIImage *btnImage = [self imageWithColor:[UIColor redColor] andSize:CGSizeMake(28.0, 28.0)];
[_btn setImage:btnImage forState:UIControlStateNormal];
Note: make sure your UIButton type is set to Custom, or the image will be "tinted".
Use this code.
UIButton *button =[[UIButton alloc]initWithFrame:CGRectMake(85, 290,110,20)];
button.layer.cornerRadius = ROUND_BUTTON_WIDTH_HEIGHT/2.0f;
button.backgroundColor = [UIColor redColor];
button.titleLabel.textAlignment = NSTextAlignmentCenter;
button.titleLabel.font = [UIFont systemFontOfSize:11.0];
[button setTitle:#"Download" forState:UIControlStateNormal];
[button addTarget:self action:#selector(downloadBtnClicked:)forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
You should make your own custom UIButton. Another solution would be to try tweaking the default one using the IB available options.
UIButton Image + Text IOS
UIButton *_button = [UIButton buttonWithType:UIButtonTypeCustom];
[_button setFrame:CGRectMake(0.f, 0.f, 128.f, 128.f)]; // SET the values for your wishes
[_button setCenter:CGPointMake(128.f, 128.f)]; // SET the values for your wishes
[_button setClipsToBounds:false];
[_button setBackgroundImage:[UIImage imageNamed:#"jquery-mobile-icon.png"] forState:UIControlStateNormal]; // SET the image name for your wishes
[_button setTitle:#"Button" forState:UIControlStateNormal];
[_button.titleLabel setFont:[UIFont systemFontOfSize:24.f]];
[_button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; // SET the colour for your wishes
[_button setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted]; // SET the colour for your wishes
[_button setTitleEdgeInsets:UIEdgeInsetsMake(0.f, 0.f, -110.f, 0.f)]; // SET the values for your wishes
[_button addTarget:self action:#selector(buttonTouchedUpInside:) forControlEvents:UIControlEventTouchUpInside]; // you can ADD the action to the button as well like

How to change alpha of UIButton while the buttons alpha also set

As seen on the picture I have 2 buttons with 0.5 alpha and 1 alpha. I want to change the alpha of the title in the first picture to 1. Is this possible?
So far I tried these which did not work:
button.titleLabel.alpha = 1;
[button setTitleColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:1] forState:UIControlStateNormal];
UIButton *button = (UIButton *)view;
if(self.currentArray[index][2] != NULL) //before it was if (button == nil)
{
NSString *name = [[NSString alloc] initWithString:self.currentArray[index][2]];
UIImage *image = [UIImage imageNamed:self.currentArray[index][5]];
button = [UIButton buttonWithType:UIButtonTypeCustom];
int extraLeftInset = 0;
if ([self.currentArray[index][6] isEqualToString:#"true"]) {
//show it
}else if([self.currentArray[index][7] isEqualToString:#"true"]){
}else{
UIImage *locked = [UIImage imageNamed:#"fruitify_locked.png"];
button.imageEdgeInsets = UIEdgeInsetsMake(15, 15, 15, 15);
[button setImage:locked forState:UIControlStateNormal];
extraLeftInset = - 256; //size of locked
button.backgroundColor = [UIColor clearColor];
button.alpha = 0.5;
}
button.frame = CGRectMake(0.0, 0.0, 56, 56);
button.tag = index;
[button setBackgroundImage:image forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
UIEdgeInsets buttonInset = UIEdgeInsetsMake(30, -2 + extraLeftInset, -20, -2);
[button setTitleEdgeInsets:buttonInset];
[button setTitle:name forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize: 8];
button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
}
One way to do this would be to adjust the alpha value of your image. That can be done by passing your image to this method,
-(UIImage *)image:(UIImage *) image withAdjustedAlpha:(CGFloat) newAlpha{
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0);
[image drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:newAlpha];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
The alpha setting affects a view and all it's subviews. If you set the alpha on a button, every part of the button will have that alpha value.
You have a couple of possible options.
You could create a custom subclass of UIButton that managed a UILabel that was a sibling view of the button in the view hierarchy. That would get messy in several ways however. The normal button title methods wouldn't work, and you would have to introduce your own control logic to remove the button label from the superview if the button was removed
You might also try manipulating the alpha property of the button's image view. (The buttons' imageView property is read-only, but you CAN still make changes to the attributes of the image view in that property. You would need to test to make sure that the button doesn't mess up the image view's alpha property when it swaps images for the different button states (highlighted, selected, etc.) You would probably also need to set the opaque property on the button to NO, and perhaps on the image view as well. (You'll need to experiment.)
BTW, I like you trick of offsetting the button title using setTitleEdgeInsets. I hadn't seen that before.

UIButton title not showing when I setImage

The most common fix to this issue is "Set as background image, not normal image"
In my case I put background images on all my buttons, if it is a locked object, I add the image of a lock on top of the background image. The image looks as expected, but in that case it does not show the title of the image.
UIButton *button = (UIButton *)view;
if(self.currentArray[index][2] != NULL) //before it was if (button == nil)
{
NSString *name = [[NSString alloc] initWithString:self.currentArray[index][2]];
UIImage *image = [UIImage imageNamed:self.currentArray[index][5]];
button = [UIButton buttonWithType:UIButtonTypeCustom];
if ([self.currentArray[index][6] isEqualToString:#"true"]) {
//show it
}else{
//-------------------------
//--THE PROBLEM HAPPENS IF THIS PIECE OF CODE IS EXECUTED, SPECIFICALLY setImage
//-------------------------
button.alpha = 0.5;
UIImage *locked = [UIImage imageNamed:#"locked.png"];
button.imageEdgeInsets = UIEdgeInsetsMake(15, 15, 15, 15);
[button setImage:locked forState:UIControlStateNormal];
}
button.frame = CGRectMake(0.0, 0.0, 56, 56);
button.tag = index;
[button setBackgroundImage:image forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
UIEdgeInsets buttonInset = UIEdgeInsetsMake(30, -30, -25, -30);
[button setTitleEdgeInsets:buttonInset];
[button setTitle:name forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize: 8];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
}
Here is the images without lock and with lock:
The answer is here.
Quoting: "It appears that when you put an image in the button. the text is shoved off to the right. Use the edge settings to bring it back over the image."
What you have to do is play around with the title inset in order to bring the title label back to the right position.
I would suggest having something like an extraLeftInset variable that defaults to 0 and that is set to the right value in the else branch.
The answer above suggest to use this formula [Edge inset for Title] Left = -(imageWidth * 2), so the value should be -512 since you say that the image normally is 256. It would be nicer to evaluate it at runtime inspecting the size of the image though.
Hope this helps :)
CGSize imageSize = button.imageView.image.size;
button.titleEdgeInsets = UIEdgeInsetsMake(
0.0, - imageSize.width, 0.0, 0.0);
left side negative padding for titleLabel. Padding value must be equal to the image width

How to set background for UIButtons with dynamic width?

I have 3 button images named edit_category_left_up,edit_category_middle_up and edit_category_right_up
I have written code to create buttons with dynamic width now I need to set background of these button like this image below using the above 3 images
This is my current code to create dynamic buttons
-(void) addDynamicButtonsForCategories
{
_adcImage.hidden=YES;
_makeADCLabel.hidden=YES;
float x=0; float y=0; float frameHeight=_subADCView.frame.size.height;
NSString *titleString=#"";
for(int i = 0; i < 15; i++)
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
if (i<1)
{
[btn setFrame:CGRectMake(0, 0, 39, 39)];
[btn setImage:[UIImage imageNamed:#"addcategory_up.png"] forState:UIControlStateNormal];
[btn setImage:[UIImage imageNamed:#"addcategory_down.png"] forState:UIControlStateHighlighted];
x = x + btn.frame.size.width+10; y=y+btn.frame.origin.y;
[btn setTitle:titleString forState: UIControlStateNormal];
[btn setTag:i];
}
else
{
titleString = [titleString stringByAppendingString:[NSString stringWithFormat:#"%d",i]]; // get button title
CGSize fontSize = [titleString sizeWithFont:[UIFont systemFontOfSize:12.0]];
CGRect currentFrame = btn.frame;
CGRect buttonFrame = CGRectMake(x, y, fontSize.width + 22.0, 40);
if ((buttonFrame.origin.x+buttonFrame.size.width) >_subADCView.frame.size.width)
{
x=0;y=_subADCView.frame.size.height;
_subADCView.frame=CGRectMake(_subADCView.frame.origin.x, _subADCView.frame.origin.y, _subADCView.frame.size.width, _subADCView.frame.size.height+frameHeight);
buttonFrame = CGRectMake(x, y, fontSize.width+ 22.0, 40);
}
x = x + fontSize.width + 35.0; y=y+currentFrame.origin.y;
[btn setFrame:buttonFrame];
//I need to set the button image here
[btn setImage:[[UIImage imageNamed:#"edit_category_middle_up.png"] stretchableImageWithLeftCapWidth:10 topCapHeight:0] forState:UIControlStateNormal];
[btn setImage:[[UIImage imageNamed:#"edit_category_middle_down.png"] stretchableImageWithLeftCapWidth:10 topCapHeight:0] forState:UIControlStateHighlighted];
[btn setTitle:titleString forState: UIControlStateNormal];
[btn setTag:i];
lastButtonPosition=btn.frame.origin.y+btn.frame.size.height;
}
[self.subADCView addSubview:btn];
}
[self readjustSubviews];
}
You only need the background in a single image if you use the following technique.
UIImage *backgroundImage = [[UIImage imageNamed:#"MyImageTitle"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, leftInset, 0, rightInset)];
where leftInset is the width of unstretchable content from the left side of the image and rightInset is the width of unstretchable content from the right side of the image.
This will create an image that only stretches content inside the defined insets. This is the best way to solve this problem.
Alternatively, if you cannot possibly combine your images then you can do it like this.
UIImage *leftImage = [UIImage imageNamed:#"edit_category_left_up"];
UIImage *middleImage = [UIImage imageNamed:#"edit_category_middle_up"];
UIImage *rightImage = [UIImage imageNamed:#"edit_category_right_up"];
UIImageView *leftImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, leftImage.size.width, btn.frame.size.height)];
[leftImageView setImage:leftImage];
UIImageView *middleImageView = [[UIImageView alloc] initWithFrame:CGRectMake(leftImageView.frame.size.width, 0, btn.frame.size.width - (leftImage.size.width + rightImage.size.width), btn.frame.size.height)];
[middleImageView setImage:middleImage];
UIImageView *rightImageView = [[UIImageView alloc] initWithFrame:CGRectMake(middleImageView.frame.origin.x + middleImageView.frame.size.width, 0, rightImage.size.width, btn.frame.size.height)];
[rightImageView setImage:rightImage];
[btn addSubview:leftImageView];
[btn addSubview:middleImageView];
[btn addSubview:rightImageView];
You might need to play around with the layer the subviews are inserted at. To do that you can use one of these methods:
[btn insertSubview:view aboveSubview:otherView];
[btn insertSubview:view atIndex:someIndex];
[btn insertSubview:view belowSubview:otherView];
Try this code to combine three images
-(UIImage*)combineImages
{
UIImage *image1 = [UIImage imageNamed:#"test.png"];
UIImage *image2 = [UIImage imageNamed:#"yyu.png"];
UIImage *image3=[UIImage imageNamed:#"test.png"];
//self.imageView.image=image3;
CGSize size = CGSizeMake(image1.size.width+image2.size.width+image3.size.width, image1.size.height );
UIGraphicsBeginImageContext(size);
[image1 drawInRect:CGRectMake(0,0,image1.size.width, size.height)];
[image2 drawInRect:CGRectMake(image1.size.width,0,image2.size.width, size.height)];
[image3 drawInRect:CGRectMake(image1.size.width+image2.size.width, 0,image3.size.width,size.height)];
UIImage *finalImage =UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return finalImage;
}
Are you writing for iOS7 only?
If so make a single "stretchable" image and import it into the project using an Asset Catalog.
From here you can then specify the properties of the stretchable image (i.e. cap insets etc...) in the catalog.
Then to set the background image correctly you just use...
[button setBackgroundImage:[UIImage imageNamed:#"BackgroundImage"] forState:UIControlStateNormal];
This will take the resizable/stretchable properties from the asset catalog.
Just look into this thread once. May be this is not for what you are looking, but I hope it may be helpful for you or someone else.
Build a UIImage with multiple stretchable images

Set background image of UIBarButtonItem programmatically changes its size

I've been able to have a custom UIBarButtonItem with an embedded uibutton through story board. It's the map button.
see parameters on this screenshot, I had to use background property instead of Image.
But when I tried to customize some uibarbuttons programmatically, then the buttons get smaller. I had the same result with the map button when I was using Image property.
Here the code i'm writing for the back button,
UIImage *backButtonImage = [[UIImage imageNamed:#"Retour.png"]resizableImageWithCapInsets:UIEdgeInsetsMake(21, 21, 21, 21)] ;
backButtonImage = [backButtonImage stretchableImageWithLeftCapWidth:0 topCapHeight:0];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, backButtonImage.size.width, backButtonImage.size.height);
[button setBackgroundImage:backButtonImage forState:UIControlStateNormal];
[button setBackgroundImage:backButtonImage forState:UIControlStateHighlighted];
[button addTarget:self action:#selector(back) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *backBarButton = [[UIBarButtonItem alloc] initWithCustomView:button] ;
self.navigationItem.leftBarButtonItem = backBarButton;
I tried with or without resizableImageWithCapInsets, stretchableImageWithLeftCapWidth with the same result below (the back button should have the same size as the map button)
I tried also iOS 5 setBackButtonBackgroundImage methods but the button was not customized at all.
If you don't find an answer to your problem, I can suggest you this function :
- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
//UIGraphicsBeginImageContext(newSize);
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
It returns an image scaled to the specified size.
So you can resize your image at the desired size and then set it as a background image.
Well the problem was that I had overwritten the Retour#2x.png for retina display with Retour.png -__-'

Resources