stretchableImageWithLeftCapWidth:topCapHeight doesn't work in initWithCoder: of UIImageView subclass - iphone

I have a UIImageView subclass called ShadowView, which displays a shadow that can be used under anything. ShadowViews are to be loaded from a nib.
In initWithCoder:, I have the following code:
- (id)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (self != nil) {
UIImage *shadowImage = [[UIImage imageNamed:#"drop_shadow_4_pix.png"] stretchableImageWithLeftCapWidth:4 topCapHeight:4];
[self setContentMode:UIViewContentModeScaleToFill];
[self setImage:shadowImage];
}
return self;
}
When I run the app, though, this image does not appear.
But if I change it to
...
UIImage *shadowImage = [UIImage imageNamed:#"drop_shadow_4_pix.png"];
...
it works fine, but it is stretched wrong.
Any ideas as to why this is happening?
Edit: it is the same when I load the shadowview programmatically, with initWithFrame: implemented similarly to initWithCoder:.
Another Edit: I think I solved the problem. I needed to set the autoresizing masks.

Is shadowImage nil?
UIImage *shadowImage = [[UIImage imageNamed:#"drop_shadow_4_pix.png"] stretchableImageWithLeftCapWidth:4 topCapHeight:4];
That method could return nil if the base image is less than 5 pixels wide or 5 pixels tall since it needs the 4 pixels for the caps + 1 pixel to stretch.

Related

Implementing custom textView

I want to improve a super cool text view with placeholder and add to it super cool frame like textField has. To do it you simply need to add this code to your awakeFromNib method:
- (void)awakeFromNib
{
[super awakeFromNib];
if (self.editable) {
CALayer *selfLayer = self.layer;
UIImage *stretchableImage = [UIImage imageNamed:#"TextView"];
selfLayer.contents = (id)stretchableImage.CGImage;
selfLayer.contentsScale = [UIScreen mainScreen].scale; // Needed for the retina display, otherwise our image will not be scaled properly.
selfLayer.contentsCenter = CGRectMake(0.5, 0.5, 1.0/stretchableImage.size.width,1.0/stretchableImage.size.height);
self.backgroundColor = [UIColor clearColor];
}
}
The problem is if you add this if() to the above mentioned placeholderTextView's awakeFromNib the drawRect method of the placeholderTextView does not getting called! WHY ? Is it because of accessing layer property of this view? Please guide me through this graphics stuff..!

annotationView override drawRect, image with alpha

Hello i want to override drawrect in my custom annotationView, so when i write
[[_mapView viewForAnnotation:annotation] setNeedsDisplay];
my annotation view will be redrawn and i wouldn't have to remove the annotation and add it again.
here is my drawRect
- (void)drawRect:(CGRect)rect {
UIImage* theImage = nil;
if( _pinType == T_UNKNOWN ) theImage = [UIImage imageNamed:#"imgU.png"];
else theImage = [UIImage imageNamed:#"imgK.png"];
[theImage drawInRect:rect];
}
The problem is that my images are with alpha and the alpha part is black.
So maybe anyone knows the solution or some suggestions to this?
I've read a lot of post about this, also using core graphics, but didn't find the solution..
Thanks in advance!
Do you want this view to be partially transparent and display things under it? If so, use [self setOpaque:NO]. An opaque view's drawRect is responsible for drawing every pixel in the rectangle with a fully opaque color.
This function will be work correct for iOS 5.0. When you will use iOS version < 5.0, you'll got alpha part as black.
To try use another method for draw your images. I don't know for what you use this code. To try use:
UIImageView *image = [[UIImageView alloc] initWithImage: theImage];
image.opaque = NO;
[self.view addSubview: image];

UITableViewCell: how to give the built-in imageView a margin on retina display

I'd like to apply a margin to the UIImageView on the left of a plain old UITableViewCell (grouped style).
The only way I've found to do this (via here) is to resize the UIImage itself before attaching it to the UIImageView. If the image is smaller then the cell, it will be centred; leaving the desired margin as a side-effect.
Well, that works, but now my image is blurry because the 100 unit row height is not 100 pixels on an iPhone4, its 200. So I end up with a UIImage scaled to 90x90 pixels that produces a 90x90 unit (180x180 pixel) UIImageView image. Enter ugly blurriness.
So my question is: how do I achieve a margin around the imageView without over-downsampling my image?
(ideally without downsampling at all - I need to keep the original for later anyway).
I feel like I'm missing something obvious; I really don't want to implement a custom cell class just for this.
Thanks guys, I've come up with a 'solution' that doesn't require subclassing.
What I do is still downsample the image, but to the 2x size (180x180 from the example in the question).
Then, when I come to create the final UIImage from the processed CGImage I use:
UIImage: +(UIImage *)imageWithCGImage:(CGImageRef)imageRef scale:(CGFloat)scale orientation:(UIImageOrientation)orientation
and set scale: to 2. Now everything works. But I'm still creating duplicate images just to keep UIKit happy.
Implement this method in your UITableViewCell subclass
-(void) layoutSubviews
{
[super layoutSubviews];
self.imageView.frame = CGRectInset(self.imageView.frame, 5, 5);
}
Note: I haven't tested any of this, so it's possible that UITableViewCell will override some of these settings to lay its subviews out according to its own internal logic.
Have you tried just adjusting the image view's frame? Try this:
UITableViewCell * tableViewCell = [[[UITableViewCell alloc] init] autorelease];
tableViewCell.imageView.image = [UIImage imageNamed:#"table-view-image"];
CGRect imageViewFrame = tableViewCell.imageView.frame;
imageViewFrame.origin.x += 10.0f;
tableViewCell.imageView.frame = imageViewFrame;
That will just pad the image view 10 points more than its normal x coordinate. If you want to pad both sides, you can also set the contentMode property on the image view to UIViewContentModeCenter and adjust its width:
UITableViewCell * tableViewCell = [[[UITableViewCell alloc] init] autorelease];
tableViewCell.imageView.image = [UIImage imageNamed:#"table-view-image"];
tableViewCell.imageView.contentMode = UIViewContentModeCenter;
CGRect imageViewFrame = tableViewCell.imageView.frame;
imageViewFrame.size.width += 20.0f;
tableViewCell.imageView.frame = imageViewFrame;
That will make the image view 20 points wider, but because the content mode is set to center, the image will be drawn without stretching. If the dimensions of your image are right, this will effectively pad the image by 10 points on the left and right. However, you need to be aware that if you do this, the UIImage you provide must already be the exact dimensions to fit in the image view. contentMode's default setting is UIViewContentModeScaleToFill, so it will automatically scale images to fill the image view's frame. Setting it to UIViewContentModeCenter will no longer do this, but it will center the actual image.
In my case I used my own subclass inherited from UITableViewCell.
It's very clean and easier to use.
Also, I could use different sizes of image to be well resized for this cell.
The point is to use additional property that will replace regular imageView
1: In the subclass, I added a property mainImageView which can be used instead of imageView.
#property (nonatomic, retain) UIImageView *mainImageView;
2: Then in - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier.
I allocated and initialized mainImageView.
You can set any frame(rect) for mainImageView.
Add it as a subview to the contentView.
I used insetImageView to align it to the vertical middle of `contentView'.
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code.
mainImageView = [[UIImageView alloc] initWithFrame:frameCellMainImageView];
[self.contentView addSubview:mainImageView];
insetImageView = (sizeRowHeightTwoLinedDetail - frameCellMainImageView.size.height) / 2.0;
}
return self;
}
3: Override - (void)layoutSubviews to make sure other properties like textLabel, detailTextLabel set their frames to be well-situated with added property mainImageView
- (void)layoutSubviews {
[super layoutSubviews];
CGRect frameImageView = frameCellMainImageView;
frameImageView.origin.x = insetImageView;
frameImageView.origin.y = insetImageView;
[self.mainImageView setFrame:frameImageView];
// Use changed frame
frameImageView = self.mainImageView.frame;
CGFloat newLeftInset = frameImageView.size.width + insetImageView;
CGRect frameTextLabel = self.textLabel.frame;
CGRect frameDetailLabel = self.detailTextLabel.frame;
frameTextLabel.origin.x += newLeftInset;
frameDetailLabel.origin.x += newLeftInset;
CGFloat newTextWidth = 320.0;
newTextWidth -= newLeftInset;
newTextWidth -= insetImageView;
newTextWidth -= insetImageView;
frameTextLabel.size.width = newTextWidth;
frameDetailLabel.size.width = newTextWidth;
[self.textLabel setFrame:frameTextLabel];
[self.detailTextLabel setFrame:frameDetailLabel];
}
4: When using this cell in UITableDataSourceDelegate methods, use cell.mainImageView to receive messages, instead of regular cell.imageView

subclassed UITableViewCell - backgroundView covers up anything I do in drawRect

I'm trying to make a subclassed UITableViewCell where I draw an image in the upper right corner. I have it working perfectly - except when I set self.backgroundView, my background image covers up the image drawn in drawRect.
There must be a way to be able to set a background image (and the selectedBackgroundView) without covering up what's being done in drawRect.
Am I going about this the wrong way?
EDIT: I've posted an example project with the problem.
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
// TODO: figure out why this covers up self.starImage that's drawn in drawRect
self.backgroundView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"cellBackground.png"]] autorelease];
}
return self;
}
- (void)drawRect:(CGRect)rect {
[self.starImage drawAtPoint:CGPointMake(self.bounds.size.width - self.starImage.size.width, 0.0)];
}
EDIT 2: at AWrightIV's request, here's how I got it working... which didn't require subclassing UITableViewCell at all. I'm just adding a subview to cell.backgroundView:
// create a UIImageView that contains the background image for the cell
UIImageView *bgImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"cellBackground.png"]];
// create another UIImageView that contains the corner image
UIImage *starRedImage = [UIImage imageNamed:#"starcorner_red.png"];
UIImageView *starImageView = [[UIImageView alloc] initWithFrame:CGRectMake(297,
0,
starRedImage.size.width,
starRedImage.size.height)];
starImageView.image = starRedImage;
// add the corner UIImageView as a subview to the background UIImageView
[bgImageView addSubview:starImageView];
// set cell.background to use the background UIImageView
cell.backgroundView = bgImageView;
You are not really supposed to mix the drawing with your cell like that, you are operating at a lower-level than the UITableViewCell machinery is operating, and this is why you get this problem.
This is just one of the various problems you will end up running into. You will run into other problems as you go down that path, including problems with how the selection works.
The proper approach is to create a custom UIView that contains the code to draw, and then you can addSubView that into your cell's root view. That will take care of the rendering in the proper order, and wont interfere with the selection system, and will work correctly in this case.
You shouldn't override the -drawRect: of a tablecell. Instead, create a new custom view and add it to the cell's contentView, and draw in there.
Have you tried adding a [super drawRect:rect]; there?
Here's a solution that's a bit of a kludge, but it fits my requirements exactly... with one fatal flaw: when cells get reused, the star corner shows up when I don't want it to.
http://dl.dropbox.com/u/2349787/UIImage_Position_subclassed_cell2.zip
I'm still using drawRect here, but only because self.starImage is null if you access it within the initWithStyle method. Also, instead of adding the subview to self.contentView, I'm adding it to self.backgroundView to prevent the cell's delete button from interfering with it. The star corner is positioned correctly in both portrait and landscape mode, and works fine within edit mode as well.
With the cell reuse issue though, It's still a no go... so, maybe I'm back to trying to do it without subclassing UITableViewCell.
I'm open to any further suggestions. Thank you!
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
// Initialization code
self.backgroundView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"cellBackground.png"]] autorelease];
}
return self;
}
- (void) drawRect:(CGRect)rect {
UIImageView *imageView = [[[UIImageView alloc] initWithFrame:CGRectMake(self.bounds.size.width - self.starImage.size.width, 0, self.starImage.size.width, self.starImage.size.height)] autorelease];
imageView.image = self.starImage;
imageView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
[self.backgroundView addSubview:imageView];
}

UIImageView subclass does not display

I am using a custom subclass of UIImageView, but I can't figure out why it's not being displayed.
The relevant code from my UIImageView subclass:
-(id) initWithImage:(UIImage*)image{
if(self = [super initWithImage:image]){
}
return self;
}
And from the view controller for the view that will be displaying my subclass:
UIImage *zoomRatateSliderHorizontal =
[[UIImage imageNamed:#"ZoomRotate Slider Horizontal.png"]
stretchableImageWithLeftCapWidth:(75.0)/2-1.0 topCapHeight:0];
scaleHorizontalControl = [[ViewTransformationController alloc]
initWithImage:zoomRatateSliderHorizontal];
scaleHorizontalControl.onScreenFrame = CGRectMake(0.0, 5.0,
self.view.frame.size.width,
scaleHorizontalControl.image.size.height);
scaleHorizontalControl.frame = scaleHorizontalControl.onScreenFrame;
scaleHorizontalControl.offScreenFrame = CGRectZero;
[self.view addSubview:scaleHorizontalControl];
Before I was subclassing, I had no trouble with the following in the view controller:
UIImage *zoomRatateSliderVertical =
[[UIImage imageNamed:#"ZoomRotate Slider Vertical.png"]
stretchableImageWithLeftCapWidth:0 topCapHeight:(75.0)/2-1.0];
scaleOrRatateViewVertical = [[UIImageView alloc]
initWithImage:zoomRatateSliderVertical];
scaleOrRatateViewVertical.frame = CGRectMake(-zoomRatateSliderVertical.size.width,
zoomRatateSliderHorizontal.size.height+5.0+5.0,
zoomRatateSliderVertical.size.width,
465.0 - zoomRatateSliderHorizontal.size.height-10.0-5.0);
[self.view addSubview:scaleOrRatateViewVertical];
Using break points I checked the frame and image being passe to my class, and they both appear to be valid and desired.
Any advice on what I'm doing wrong I'd greatly appreciate.
The Apple Docs read:
The UIImageView class is optimized to draw its images to the display. UIImageView will not call drawRect: a subclass. If your subclass needs custom drawing code, it is recommended you use UIView as the base class.
So I'd subclass UIView and work from there.