When UIButton content mode is "center," background image is pixelated - iphone

Here's how I'm setting the background image and content mode:
[btn setBackgroundImage:[UIImage imageNamed:#"dots_game_horiz_blue.png"]
forState:UIControlStateNormal];
[btn setContentMode:UIViewContentModeCenter];
Is there a reason that the background image would be pixelated? The frame is slightly larger than the image size, and I just want the image to be centered.

I guess a background image is not considered to be "content" by the UIButton, so the content mode did not apply to the background image. Instead, I set the image of the button and it worked just fine:
[btn setImage:[[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"dots_game_horiz_blue" ofType:#"png"]] forState:UIControlStateNormal];
[btn setContentMode:UIViewContentModeCenter];

In your xib / storyboard, in the Attributes inspector, you need to:
Set your UIButton type to Custom
Set your image in the property Image, not Background
Validating step 2, you can see the button frame changed according to the image set. Set the frame as you want it to be, the image will resize itself to fit in
Select Image for the property Edge in order to apply inset to the button image
Enter inset values you need. If you set 5 to Top, Bottom, Left, Right, the image will be centered inside the button, with a border of 5
You can see the changes in the xib / storyboard view, no need to launch a debug.
Tip: Inset is the same for every images of the button (State Config)

You could also implement - (CGRect)backgroundRectForBounds:(CGRect)bounds to control how the background is drawn.

The correct solution is to change the type from System to Custom. Then the background image will honor the content type instead of just centering. In my case I set it to Aspect Fill and apply a 45 degree corner radius to round it. Simple and works perfectly.

I needed to create rounded buttons for avatar photos and found the answers to this question to help but they did not get me all of the way there. I implemented the backgroundRectForBounds method and scaled the image to fit and it works well.
I have the code on GitHub.
https://github.com/brennanMKE/CircleButton
The method is also listed below. It is important to set the background image and not the image for the button which does not work with this method.
- (CGRect)backgroundRectForBounds:(CGRect)bounds {
UIImage *backgroundImage = [self backgroundImageForState:self.state];
if (backgroundImage) {
CGFloat maxWidth = CGRectGetWidth(self.frame);
CGFloat xDelta = maxWidth / backgroundImage.size.width;
CGFloat yDelta = maxWidth / backgroundImage.size.height;
CGFloat delta = xDelta > yDelta ? xDelta : yDelta;
CGFloat x = floorf((self.bounds.size.width - (backgroundImage.size.width * delta)) / 2);
CGFloat y = floorf((self.bounds.size.height - (backgroundImage.size.height * delta)) / 2);
return CGRectMake(x, y, backgroundImage.size.width * delta, backgroundImage.size.height * delta);
}
else {
return [super backgroundRectForBounds:bounds];
}
}

Related

Progress view not rounded on iOS 7

I have set some images to custom a UIProgressView and give a rounded style to it, but the problem is when the progress value is changed the control is not anymore rounded on iOS 7.
Here is the code that i use:
self.progressBar.progressImage = [UIImage imageNamed:#"prog_progress"];
self.progressBar.trackImage = [UIImage imageNamed:#"prog_track"];
CGAffineTransform transform = CGAffineTransformMakeScale(1.0f, 5.0f);
self.progressBar.transform = transform;
Progress View Changes:
Please add category to UIProgressView as below
- (CGSize)sizeThatFits:(CGSize)size
{
return CGSizeMake(self.frame.size.width, self.frame.size.height);
}
#end
The progressbar has roundet corners. Have a closer look. Due your skaling you are compressing the image along with its corners. Even if setting insets this aproach will never work. Smal numbers will at one point drop below the inset.
There are many aproaches, but given your image aproach:
A simple blue view under an imageview. the image view has your white image with the grey bar. Give the grey area trabsparency, so the blue is visible. Now u can set the blue view to any size without loosing the round corners.

How to resize UIImage created with resizableImageWithCapInsets when used for an UIImageView / UIView

I'm writing code to use a custom image as the background for a UIView.
The app runs in portrait only. Prior to the 4" retina screen, I used fixed size, hand-drawn png as the background image (card.png below). I had code like this:
UIView* frontView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
frontView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
UIImage* cardImage = [UIImage imageNamed:#"card.png"];
UIImageView* im = [[UIImageView alloc] initWithImage:cardImage];
im.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
[frontView addSubview:im];
// frontView has a bunch of stuff added...
As you'd expect, when I run this on an iPhone 5, everything can be dynamically sized and positioned, except for the hand drawn png, card.png. So that background image isn't using the full height that is available...
So what (I think) I want to do, is something like this...
UIImage* cardImage = [[UIImage imageNamed:#"card_resizable.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(72, 0, 60, 0)];
I've redrawn card.png as card_resizable.png - it's 140 pixels in height, there's an 8 pixel chunk in the middle, which I want to tile, so the top inset is 72 pixels tall and the bottom inset is 60 pixels tall.
What's the next step?
Should I create the UIImageView with the dynamic size of frontView?
How do I make the resizable image, resize?
The few things that I've tried... none of them have resulted in tiling the resizable image.
Cheers,
Gavin
Update:
I was incorrectly setting the pixel values for UIEdgeInsetsMake(72, 0, 60, 0)
These values are based on the #2x image. When changed to UIEdgeInsetsMake(36, 0, 30, 0) - I get the correct, vertical resizing.
As Mike pointed out, I needed to set the size of the UIImageView to the parent, and correctly set it's autoresizingMask:
im.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
I know have the UIImageView sized dynamically sized, correctly, to fill its parent view - frontView (I've given it a green background to check visually on both 3.5" screen and 4" screen.
The resizable image, cardImage, is also resizing, correctly, in the vertical plane.
Once you've passed the stretchable image to a UIImageView you just resize that image view to whatever height you need.
In your case, you probably want to make the image view fill its parent by doing this:
im.frame = frontView.bounds;
im.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[frontView addSubview:im];
We make im fill the frontView and tell it to automatically increase its width/height when its parent (frontView) changes size. This should do what you want.
Can you try with stretchableImage
UIImage *image = [UIImage imageNamed:#"card_resizable.png"];
UIImage* cardImage = [image stretchableImageWithLeftCapWidth:0.0f
topCapHeight:70.0f];
Take a look at UIView contentMode property. it should give you some sense about how the UIImageView render the image inside, whenever it change it's size.

Objective-C: AutoresizingMask + 2 Subviews = struggling

Solution: For those who view this someday in the future, the solution I used was indeed viewDidLayoutSubviews. The solution was actually rather complex - I had to calculate several scaling values and dynamically re-size the Art view every time the page needed a re-layout. There were several odd problems to handle, but after each was taken down the implementation feels pretty solid.
If anybody runs in to a similar problem later on, let me know and I can post the relevant code.
I've got a 'blank' UIView, a subview that is an UIImageView containing a single image, and a second subview that is basically a collection of CGContext arcs and lines and all that.
Where I'm at: I've placed the UIImageView subview on top of the UIView so that its image is the 'background'. Then I placed the CGContext arcs and lines subview on top of that (I'll call this the Art subview for clarity). All good. Everything displays perfectly and aligned.
Problem: When I rotate, things get screwy. The arcs and lines on the Art subview end up at the wrong coordinates, and depending on my autoresizingmask settings the image gets stretched, etc. I can fix one of these problems at a time, but I can't find the right combination to fix them both!
Things I've Tried: I've tried just about every autoresizingMask option & combination of options, but per above I can't quite lick the problem with those. Given that I also tried using some custom code in viewDidLayoutSubviews, but this felt really flimsy and much less extensible vs. using autoresizingMasks. So I abandoned the path after making some nominal progress.
What I've Learned: As long as I bind my UIImageView frame and the Art subview frame to the same dimensions, then the arcs and lines stay at the proper coordinates. That is probably the easiest way to state my goal: to have a UIImageView that stays in the correct aspect ratio (not just the image within, but the view itself), and then match the Art subview exactly to its frame - even as the screen rotates, etc.
Here is a diagram of what I'd like to achieve:
+ = UIView
~ = UIImageView subview
. = Art subview
Portrait
Wherein the image within the UIImageView takes up basically the whole screen (though not quite), and the Art subview is layered on top of it with the dots below representing a crude line/arc.
++++++++++
+~~~~~~~~+
+~ . ~+
+~ . ~+
+~ . ~+
+~ . ~+
+~~~~~~~~+
++++++++++
Landscape
Wherein the UIImageView sublayer maintains its aspect ratio, and the Art sublayer stays 'locked' to the image within the UIImageView.
++++++++++++++++++++++++++
+ ~~~~~~~~ +
+ ~ . ~ +
+ ~ . ~ +
+ ~ . ~ +
+ ~ . ~ +
+ ~~~~~~~~ +
++++++++++++++++++++++++++
My View Controller (where I think the problem is - also removed all autoresizingMask settings to clean up the code)
- (void)viewWillAppear:(BOOL)animated {
// get main screen bounds & adjust to include apple status bar
CGRect frame = [[UIScreen mainScreen] applicationFrame];
// get image - this code would be meaningless to show, but suffice to say it works
UIImage *image = [.. custom method to get image from my image store ..];
// custom method to resize image to screen; see method below
image = [self resizeImageForScreen:image];
// create imageView and give it basic setup
imageView = [[UIImageView alloc] initWithImage:image];
[imageView setUserInteractionEnabled:YES];
[imageView setFrame:CGRectMake(0,
0,
image.size.width,
image.size.height)];
[imageView setCenter:CGPointMake(frame.size.width / 2,
frame.size.height / 2)];
[[self view] addSubview:imageView];
// now put the Art subview on top of it
// (customArtView is a subclass of UIView where I handle the drawing code)
artView = [[customArtView alloc] initWithFrame:imageView.frame];
[[self view] addSubview:artView];
}
the resizeImageForScreen: method (this seems to be working fine, but I figured I'd include it anyway)
- (UIImage *)resizeImageForScreen:(UIImage *)img {
// get main screen bounds & adjust to include apple status bar
CGRect frame = [[UIScreen mainScreen] applicationFrame];
// get image
UIImage *image = img;
// resize image if needed
if (image.size.width > frame.size.width || image.size.height > frame.size.height) {
// Figure out a scaling ratio to make sure we maintain the same aspect ratio
float ratio = MIN(frame.size.width / image.size.width, frame.size.height / image.size.height);
CGRect newImageRect;
newImageRect.size.width = ratio * image.size.width;
newImageRect.size.height = ratio * image.size.height;
newImageRect.origin.x = 0;
newImageRect.origin.y = 0;
// Create a transparent context # the newImageRect size & no scaling
UIGraphicsBeginImageContextWithOptions(newImageRect.size, NO, 0.0);
// Draw the image within it (drawInRect will scale the image for us)
[image drawInRect:newImageRect];
// for now just re-assigning i since i don't need the big one
image = UIGraphicsGetImageFromCurrentImageContext();
// Cleanup image context resources; we're done!
UIGraphicsEndImageContext();
}
return image;
}
There is no combination of autoresizing flags and content modes that will do what you want. Using viewDidLayoutSubviews is one reasonable way to handle the layout. That's why it exists: the autoresizing flags are pretty limited.
A different approach is to change your -[ArtView drawRect:] method so that the autoresizing flags can do what you want. You can make your drawRect: method implement the same algorithm that UIImageView implements for UIViewContentModeScaleAspectFit.

Position image in UIButton for all states [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
UIButton: how to center an image and a text using imageEdgeInsets and titleEdgeInsets?
I'm customizing the placement of the image and label of my UIButton but it's only set on the initial state. What the hack, how do I ensure the positioning for all 4 button states?
This is the code I'm using. The poisoning works for the first state but gets reset on the other states.
CGRect imgRect = reviewsButton.imageView.frame;
imgRect.origin.x = 10;
imgRect.origin.y += 4;
reviewsButton.imageView.frame = imgRect;
CGRect lblrect = reviewsButton.titleLabel.frame;
lblrect.origin.x = 85;
reviewsButton.titleLabel.frame = lblrect;
[reviewsButton setBackgroundColor:[UIColor colorWithRed:0.192 green:0.198 blue:0.206 alpha:0.15]];
There are UIButton properties which have been made specially for this
#property(nonatomic) UIEdgeInsets contentEdgeInsets
#property(nonatomic) UIEdgeInsets imageEdgeInsets
#property(nonatomic) UIEdgeInsets titleEdgeInsets
Use this property to resize and reposition the effective drawing rectangle for the button image. You can specify a different value for each of the four insets (top, left, bottom, right). A positive value shrinks, or insets, that edge—moving it closer to the center of the button. A negative value expands, or outsets, that edge. Use the UIEdgeInsetsMake function to construct a value for this property. The default value is UIEdgeInsetsZero.
#jspooner Settings the button frame after you are finished altering the imageview frame and label frame might do the trick.
You should create your image and then apply it to the button states:
UIImage* reviewButtonImage = [UIImage initWithContentsOfFile:/*path*/];
[reviewButton setImage:reviewButtonImage forState:UIControlStateNormal|UIControlStateHighlighted|UIControlStateDisabled|UIControlStateSelected];
I was never able to get the desired layout to work with UIButton so I created a custom UIView and handled touches there.

bigger UISlider ok but tracking zone problem

My goal is to create a bigger UISlider with 35 pixels height for the thumb image.
I subclassed UISlider and added the method :
- (CGRect)trackRectForBounds:(CGRect)bounds
{
return CGRectMake(bounds.origin.x, bounds.origin.y, self.bounds.size.width, 50);
}
Then I set the thum image from my controller using setThumbImage:
My bigger thumb is well displayed.
The problem I have is that the tracking zone is still the same around 19 px height, how to extend it to 50 ?
Thanks
T.
A long-standing problem with the iPhone OS' UISlider control is that you can't make it taller. More specifically, you can make it look taller by specifying taller images for its thumb and track, but the touch area stays as a tiny 23px tall.
Michael Patricios has published a way to make the default sliders a lot easier to touch, and a variation of that technique can handle larger sliders. What you need to do is subclass UISlider, and override pointInside in your class:
// How many extra touchable pixels you want above and below the 23px slider
#define SIZE_EXTENSION_Y 10
- (BOOL) pointInside:(CGPoint)point withEvent:(UIEvent*)event {
CGRect bounds = self.bounds;
bounds = CGRectInset(bounds, 0, SIZE_EXTENSION_Y);
return CGRectContainsPoint(bounds, point);
}
In Interface Builder, set the slider to use your new UISlider subclass, and you now have a slider with a 43px touchable height.
I'm using this solution :)
CGRect sliderFrame = self.mySlider.frame;
sliderFrame.size.height = 142.0;
[self.mySlider setFrame:sliderFrame];
Increase UISlider thumb hit area by increasing the height of the UISlider frame. This approach avoids any offset and subsequent need to correct the slider frame.
- (void)viewDidLoad{
[super viewDidLoad];
//expand slider hit area
self.slider.frame = CGRectInset(self.slider.frame, 0, -10);
}
I believe that you may want to look at thumbRectForBounds:trackRect:value: