(iphone) i'm getting black rectangle behind image, why? - iphone

I have UIImageView subclass instance which has multiple image sublayers.
I want to add/remove shadow dynamically to this view.
The following code works fine(imageView subclass's implementation) for a imageView with one sublayer, but shows black rectangle instead of shadow for a imageView with multiple sublayers.
(actually, for one layer image, the code seems to keep adding shadow on top of previous shadow if run multiple times, but this is a minor problem)
- (void) drawLayer: (CALayer*) layer inContext: (CGContextRef)context
{
SYSLOG(LOG_DEBUG, "in drawLayer, isShadowed: %d", isShadowed);
if(isShadowed == true)
{
CGContextSaveGState(context);
CGContextClearRect(context, self.bounds);
CGContextSetShadow(context, CGSizeMake(10, 10), 3);
CGContextBeginTransparencyLayer(context, NULL);
[layer renderInContext:context];
CGContextEndTransparencyLayer(context);
CGContextRestoreGState(context);
}
else
{
[layer renderInContext:context];
}
}
Thank you

If your layer's opaque is not NO, CGContextClearRect paints black rather than punching a transparent hole in the layer. I'm guessing that's the problem.

Related

Can I use UIBezierPath to draw a shadow image around a UITableView section?

I have a shadow image that I would like to draw around the outer edge of a grouped UITableView section. This is the image:
I can get the UIBezierPath that represents the rect I want to draw around, but I can't figure out how to repeat the image along the route of the rect. So far it just fills the rect with the image:
UIImage *patternImg = [UIImage imageNamed:#"cellShadow"];
UIColor *fill = [UIColor colorWithPatternImage:patternImg];
[fill setFill];
CGRect aSectRect = [self rectForSection:0];
UIBezierPath *aSectPath = [self createRoundedPath:aSectRect];
[aSectPath fill];
Is this possible? What do I need to do?
Unfortunately, there's no way to have UIBezierPath use an image as a "brush" which is essentially what you'd want. But you can have CoreGraphics draw a shadow for you:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShadow(context, CGSizeZero, myShadowRadius);
// Draw your shape here
Now if you draw just one shape it will get a shadow. But if you draw more shapes, each will get its own shadow which might not be what you want. The solution is called a transparency layer, which is not related to CALayers or something but is just some kind of "grouping" in CoreGraphics:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShadow(context, CGSizeZero, myShadowRadius);
CGContextBeginTransparencyLayer(context, NULL);
// Draw your shapes here.
CGContextEndTransparencyLayer(context);
Between the CGContextBeginTransparencyLayer and CGContextEndTransparencyLayer calls the shadow is disabled. After the CGContextEndTransparencyLayer call, the shadow is applied to everything that has been drawn between begin and end as if it were just one shape.

Core Graphics Transparent Overlay

I'm replacing the background view of a UITableViewCell with my own custom subclass of UIView, in which I override the drawRect method with my own, which creates a multi-colored and changing background.
The problem is that when the TableViewCell is selected, the graphics under are completely hidden and it looks odd. I need to create a custom selectedBackgroundView in order to fix this. The problem is that that view needs to create a blue gradient tint over the graphics already there, and I don't know how to draw a CGRect or something similar that is partially transparent.
// Write this In your - (void)drawRect:(CGRect)rect
// To draw semi transparent Square
// Create Current Context To Draw
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath *square = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 100, 100)];
// following method will fill red color with alpha 0.4 last one in parameter list
// first 3 are Red, Green and Blue Respectively.
CGContextSetRGBFillColor(context, 1, 0, 0, 0.4);
[square fill];
[square stroke];

CGContextFillEllipseInRect acting weird

Here is my drawRect:
- (void)drawRect:(CGRect)rect
{
CGContextFillEllipseInRect(UIGraphicsGetCurrentContext(), self.bounds);
}
and the instances of this class are added a subviews the next way:
(#define DOTS_SIZE 30)
[self addSubview:[[VertexView alloc] initWithFrame:CGRectMake(anchor.x-DOTS_SIZE/2, anchor.y-DOTS_SIZE/2, DOTS_SIZE, DOTS_SIZE)]];
As far as I understand, I should get and ellipse (circle my case) in the views bounds. But I get it fully filled with rectangle (square).
By the way, I have logged bounds and their size is 30x30, so I should get nice little circles, but I get squares (T_T)
I'll be thankful for any advise!
The problem is that you have to make some setup before drawing the ellipse. For example:
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor); // Or any other color.
CGContextFillEllipseInRect(ctx, self.bounds);
}
And to make background transparent you can check out: Setting A CGContext Transparent Background

Adding a vertical rule (border) between two UIScrollViews

I have two UIScrollView instances and I want to draw a vertical line between them (not 100% height, more like 80%). How can I achieve this?
Use Core Graphics. Nest the two UIScrollViews inside another view and override the drawRect method on the parent. See Apple's UIView documentation for drawRect for more detail and the Core Graphics Context Reference for more information about drawing.
- (void) drawRect: (CGRect) rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw divider
UIGraphicsPushContext(context);
// Fill the background, this must happen if the view is not opaque.
if (self.opaque)
{
[[UIColor whiteColor ] set];
CGContextFillRect(context, rect);
}
[[UIColor grayColor] set];
CGContextSetLineWidth(context, 1);
CGContextBeginPath(context);
CGContextMoveToPoint(context, rect.size.width * 0.5, 0.1 * rect.size.height);
CGContextAddLineToPoint(context, rect.size.width * 0.5, 0.9 * rect.size.height);
CGContextStrokePath(context);
UIGraphicsPopContext();
}
edit
To address my oversight, there is no simple way that I know of to draw on top of subviews.
This behavior can be simulated using the same basic view hierarchy as above. Subclass UIView to create a completely transparent view that does not accept any touch events. Place the drawing code (I added a conditional for opacity in the code above) in the custom view and then insert an instance of the custom view at the front of the view hierarchy.
A vertical line between your views could also just be a thin, tall UIView (on top of the scroll views) with, say, a solid background color. This would mean you don't need to worry about custom drawing code. This also lets you lay it out in Interface Builder, use autosizing, etc.

How to use a CGBlendMode on a UIView that scrolls above a fixed background?

Our main UIView is a UIScrollView with a fixed background image (very common, obviously). In that scrollView, we have several UIViews that hold content and scroll up and down as the user scrolls (also common). Those UIViews each have their own background, a simple gradient from white to black.
The goal is to have the background gradient of those (inner) UIViews be partially opaque AND use a CGBlendMode other than "kCGBlendModeNormal" (specifically, "kCGBlendModeOverlay"). You should be able to see through to the "parent" scrollView’s fixed background image as the UIViews scroll up and down above it.
- (void)drawRect:(CGRect)rect {
gradientStart = [UIColor colorWithRed:1 green:1 blue:1 alpha:1.0];
gradientEnd = [UIColor colorWithRed:0 green:0 blue:0 alpha:1.0];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[2] = { 0.0f, 1.0f };
NSArray *colors = [NSArray arrayWithObjects:(id)gradientStart.CGColor, (id)gradientEnd.CGColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)colors, locations);
CGColorSpaceRelease(colorSpace);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetAlpha(context, 0.50); //this works!
CGContextSetBlendMode(context, kCGBlendModeOverlay); //doesn’t seem to do anything!
CGContextClearRect(context, rect);
CGPoint startPoint, endPoint;
startPoint.x = 0.0;
startPoint.y = 0.0;
endPoint.x = 0.0;
endPoint.y = rect.size.height;
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient);
[super drawRect:rect];
}
Everything works as expected except the CGContextSetBlendMode, which is ignored. We can't seem to find a way to change the blendMode of a UIView relative to what is behind it, the same way you can with alpha. Please note that this is different than building up multiple layers in a SINGLE UIView; in that case, this technique does change the blendMode of the layers "on top". We want to see through to the parent scrollView's fixed background image (as we scroll the child view up and down above it), with both an alpha and an overlay blend applied.
Here's an image showing the issue: http://img2.sbck.us/blendmode.png
Thanks in advance for your help!
I believe what you want is not possible with your current setup. On iOS, it is simply not possible for the blend mode of a view to have an effect on the stuff that is drawn under the view. You would have to draw the scroll view's background and the gradients in the same view.
This is possible, at least with two image views. It might even be possible with more general views. The approach is to implement drawRect in the parent view, and do as follows:
Determine the rect for the foreground view.
Convert the rect in the foreground view to a rect in the background view.
Begin a new graphics context.
Draw the background with the proper blend mode.
Draw the foreground with the proper blend mode.
Extract the image from the graphics context.
End the graphics context.
Use the extracted image accordingly.
This allows a foreground image to blend with a background image.
Seems like you could do this by setting the 'compositingFilter' property of your view's CALayer. The comment in CALayer.h says "A filter object used to composite the layer with its (possibly filtered) background. Default value is nil, which implies source-over compositing."
Alas, CoreImage which provides the filters is not (officially) available on iOS.
I guess your other alternative would be to use OpenGL. You could still use UIView with OpenGL after a fashion by rendering your UIView's into images which could then be used a textures.