Using resizableImageWithCapInsets: image for button only works for the state set, other states show a "gap" - objective-c

When using resizableImageWithCapInsets: to create an image for a UIButton only the normal state (the state set the image with using setBackgroundImage:forState:) works. All other states show a gap instead of the drawn image. UIButton says that if no image is set for a particular state, the normal state image will be used with an overlay for disabled and selected states.
Here is the normal state:
Here is the selected state:
And here is the source image:
It clearly is using the resizable image I provided, but the image is not drawing the resized area. (You can see the left and right edges but the middle area that is to be stretched just isn't drawn).
Interestingly, stretchableImageWithLeftCapWidth:topCapHeight: does work. Now this is a deprecated method in iOS 5, but with the gap being shown in the new API, I may be stuck using it.
I do recognize that I can provide more images for each state but that defeats the purpose I'm trying to achieve of reducing memory footprint plus adds extra dependency on my graphics designer which I'd like to avoid.
// This is the gist of the code being used
UIImage* image = [UIImage imageNamed:#"button.png"];
UIEdgeInsets insets = UIEdgeInsetsMake(image.size.height/2, image.size.width/2, image.size.height/2, image.size.width/2);
image = [image resizableImageWithCapInsets:insets];
[self.button setBackgroundImage:image forState:UIControlStateNormal];
// Even doing the following results in the same behaviour
[self.button setBackgroundImage:image forState:UIControlStateSelected];

You aren't creating your insets properly for the image capping. I've reproduced your issue and corrected it by using the correct insets.
With your current code, you are creating caps of half of the image height and width - this leaves you with a "stretchable" area of 0x0 pixels - so you get nothing in the middle.
Why this isn't showing up as wrong in the normal state of the button I'm not sure - perhaps there is some optimisation built in to UIButton to fix things or auto-strectch if you don't supply a stretchable image, and this is not applied to the other states.
The caps are supposed to define the area of the image that must not be stretched. In the case of your button.png image, this is 6 pixels on the left and right sides, and 16 pixels in from the top and bottom. This isn't quite standard, you should tell your graphics designer that (at least for left-right which is the most common stretching) you should only have a 1px area in the centre, however this does not affect the outcome. If you do have a 1px stretchable area then you can standardise your code by deriving the caps from the image size as you have tried to do in your question (each cap is then (image.size.height - 1) / 2 for top/bottom, same but with width for left/right).
To get the correct images on your button, use the following code for creating the stretchable image:
UIEdgeInsets insets = UIEdgeInsetsMake(16, 6, 16, 6);
image = [image resizableImageWithCapInsets:insets];

I was experiencing problems while using resizable images on iOS5 too. It turns out that if your button is of type "RountedRect" and you manipulate the background images, the resizable images will not behave as expected. (iOS6 handles this ok, presumably by assuming your new button type and adjusting as needed.)

Related

How to create a small button with a large tappable area

I want to create a close button which will look like a circle with an x in the middle. In x code I set the button's size and width to be large so that the touchable area is larger (50 x 50 with a font of just 22).
I create a button, change the title to X and then set the following:
[self.closeButton.layer setBorderWidth:2.0f];
[self.closeButton.layer setBorderColor:[[UIColor whiteColor] CGColor]];
[self.closeButton.layer setCornerRadius:self.closeButton.bounds.size.width/2];
The circle border is too far from the X. How would I bring the borders in tighter to the X but not decrease the size of the clickable area?
assign image to a button. and make tappable area as you want. then set image location in button by setting inset value of that button as top, bottom, left, right. change the value of this according to your requirements. You can set inset value from interface builder as shown in image here. change the value and see the difference to place your image in button at exact location you want.
exactly like this image
I'd suggest exactly what Max has done. He has shown how to do this via IB, this is how you do it through code:
myButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 25, 25);// UIButton
myBarButtonItem.imageInsets = UIEdgeInsetsMake(0, 0, 25, 25);// UIBarButtonItem
EDIT:
Sorry for not reading your question properly the first time. I think you will run into the same issue with this approach : the border/cornerRadius of the button will not respect any insets. It will be drawn according to the frame of the button. Only (easy) workaround at the top of my head, is to create an image with the border and corners in it, and then set it as the image. The image will respect the insets, and you will have your desired tappable area, with the borders exactly where you want them.
There might be a more elegant workaround through subclassing, but unless you change the border width/color, or corner radius of your button at any stage, I'd suggest sticking with custom image.
EDIT 2:
Instead of using an image you might want to use this unicode character.

NSImageView resized with image size

I'm new to Obj-C/Cocoa programming and facing to a problem about NSImageView.
My goal is to create an app which allow the user to put an image on it with drag&drop.
The problem is the following : when we're dropping an image larger than the window, it resizes the window to the image size, I don't want that !
I put my NSImageView into a NSScrollView so only a certain part of the image MUST be showed and the user will move sliders to view the entire one (or simply resize the window), that's what I want..
Here is my XIB hierarchy (concerned part) :
View
NSScrollView
View
NSImageView
In the code of my NSImageView, I have (about that problem) :
[self setImageScaling:NSScaleProportionally];
[self setImage:newImage];
NOTE: The sliders seem to move the view and not the image
NOTE2: I can't resize the window to a lower size than the image
I have a horrible aversion to nib files and prefer to do things programmatically whenever possible so I'm not much help with that... but here's a little listing from a little NSScrollview test app I recently coded up that does something similar. The only non-self-contained thing mentioned in this code chunk is the window name, which refers to an NSWindow * for a window that was already created. And self here just refers to a controller object.
NSImage * myImage = [NSImage imageNamed:#"myfilename.png"];
NSRect imageRect = NSMakeRect(0.0,0.0,myImage.size.width,myImage.size.height);
self.myImageView = [[NSImageView alloc] initWithFrame:imageRect];
self.myImageView.bounds = imageRect;
self.myImageView.image = myImage;
self.myScrollView = [[NSScrollView alloc] initWithFrame:[window.contentView frame]];
self.myScrollView.hasVerticalScroller = YES;
self.myScrollView.hasHorizontalScroller = YES;
self.myScrollView.documentView = self.myImageView;
self.myScrollView.borderType = NSNoBorder;
self.myScrollView.scrollerStyle = NSScrollerStyleOverlay;
window.contentView = self.myScrollView;
I know that's perhaps not the solution you were looking for but maybe it helps you debug? In particular, I didn't do anything funny with my NSImageView besides give it a frame, a bounds rectangle, and an NSImage *, so I don't think you need to worry about the scaling parameter per se -- in fact, if you want the image to be full-sized and to scroll around it, you want your NSImageView's frame to be the same size as the image so that it doesn't HAVE to scale at all. Maybe that's your problem -- the frame is too small? It is OK for the NSImageView frame to be bigger than the window -- that's what lets you scroll around it.
To me it sounds like your views don't have the proper sizes. Because of bouncing and because your views don't clip their subviews, you can still scroll a bit, but it returns to its original position.
Check if the NSImageView is big enough to display your full image. (Check frame or bounds for width and height)
If it is not big enough do this (inside the method, where you set the new image):
self.myImageView.frame = NSMakeRect(0.0,0.0, self.myImageView.image.size.width, self.myImageView.image.size.height);
Adjust the size of your scroll views view accordingly.
self.myScrollView.view.frame = self.myImageView.frame;
Now your scrollable area should be big enough to cover the full image.
If this works for you, give Matt some credit as well, by up-voting his answer, since his answer does essentially the same thing - only code based, instead of initialising everything using a nib file - and the code I used is very similar to what he had already posted.
I suggest to put the "image view" inside of the custom view (edges to its superview).
The image view's constraints to the equal edges, except the bottom constraint, which to the "Less Than or Equal" relation to its superview.

How to set an image to a NSButton to be resized along with the button?

I have an image (3width x 15height, the first and last pixels are rounded on the corners), and I want to create a button (the height of the button is always the same, 15px, but the width changes) that uses this image no matter what size it is, kinda like UIEdgeInsets on iOS.
I've tried to use [[button cell] setImageScaling:NSImageScaleAxesIndependently]; but the image gets distorted and loses quality.
How can I do this on OSX? Thanks!
You could probably create a custom subclass of NSButtonCell and use the Application Kit's NSDrawThreePartImage() function in the cell's drawInteriorWithFrame:inView: method.
For more info on how to use NSDrawThreePartImage(), see Cocoa Drawing Guide: Drawing Resizable Textures Using Images.
If it's possible to set this in graphical interface > Attribute Inspection > Button section Background

TapToZoom queries

Has anyone analyzed the sample code for TapToZoom on iPhone.
The program performs 3 things
1. Double Tap, perform zoom
2. Two finger tap, perform zoom out.
and 3. Pinch zoom
I don't understand a small part of it.
The real image size is 1730 × 2430
So there is a scroll view in which an UIImageView of size 1730 × 2430 present. The image is set to Scale to Fit. i.e.
if the image is set to scale to fill the UIImageView which also has the 1730 × 2430, how does the image suddenly on launch fit the screen for the iPhone with reloution 320x460
The Image in the xib in its native size.
But when the app starts off, the image is not in its original size, but fits the scrollview content to fit.
How is this accomplished. I didn't find any piece of code that was doing the same in the app.
Check this piece of code is present in viewDidLoad -
// calculate minimum scale to perfectly fit image width, and begin at that scale
float minimumScale = [imageScrollView frame].size.width / [imageView frame].size.width;
[imageScrollView setMinimumZoomScale:minimumScale];
[imageScrollView setZoomScale:minimumScale];

Overlay non transparent Pixels in iOS Cocoa

Is there any way on the iOS SDK to overlay the non-transparent pixels in an image with colored pixels?
Thanks very much to both who answered.
The final solution I implemented used the code mentioned in the accepted answer within the drawRect method of a subclassed UIView, I used the following code to overlay the color:
CGContextSetFillColor(context, CGColorGetComponents([UIColor colorWithRed:0.5 green:0.5 blue:0 alpha:1].CGColor));
CGContextFillRect(context, area);
I think you are probably looking for the blend mode kCGBlendModeSourceAtop. First, draw the UIImage into your view. Then obtain the current graphics context with
CGContext currentCtx = UIGraphicsGetCurrentContext();
Then save the context state, and set the blend mode:
CGContextSaveGState(currentCtx);
CGContextSetBlendMode(currentCtx, kCGBlendModeSourceAtop);
Then you can draw whatever you want to be overlaid over the opaque pixels of the UIImage. Afterward, just reset the context state:
CGContextRestoreGState(currentCtx);
Hope this helps!
You can modify how something is drawn using the blend mode. See http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Reference/CGContext/Reference/reference.html%23//apple_ref/doc/c_ref/CGBlendMode for a full list of the blend modes supported by Core Graphics on iOS. From your description, I think you would be interested in either kCGBlendModeSourceIn, which draws the new content using the old content's alpha value as a mask, or kCGBlendModeSourceAtop, which draws the new content using the old content's alpha as a mask on top of the old content using the new content's alpha value as a mask. You can set the blend mode for all drawing using CGContextSetBlendMode, or you can draw a UIImage with a certain blend mode using -[UIImage drawAtPoint:blendMode:alpha:].