Darken view as if disabled - ios

How do you darken a view as if it were disabled/highlighted, preferably without using any additional views?
By view I mean a UIView, with all its children. I want to achieve the same effect of a disabled/highlighted UIButton.
Do not assume that the view is fully opaque.

What I'm currently playing with:
Create a black layer with opacity (_highlightLayer). This is similar to the "black view with alpha" approach.
Mask _highlightLayer with an non-opaque image of the original view.
Add the _highlightLayer to the view's layer.
Only the non-transparent pixels of the view will be darkened.
The code:
- (void)highlight
{
// Black layer with opacity
_highlightLayer = [CALayer layer];
_highlightLayer.frame = CGRectMake(0, 0, self.layer.bounds.size.width, self.layer.bounds.size.height);
_highlightLayer.backgroundColor = [UIColor blackColor].CGColor;
_highlightLayer.opacity = 0.5;
// Create an image from the view
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *maskImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Create a mask layer for the black layer
CALayer *maskLayer = [CALayer layer];
maskLayer.contents = (__bridge id) maskImage.CGImage;
maskLayer.frame = _highlightLayer.frame;
_highlightLayer.mask = maskLayer;
[self.layer addSublayer:_highlightLayer];
}
And then:
- (void)unhighlight
{
[_highlightLayer removeFromSuperlayer];
_highlightLayer = nil;
}
Of course, this should only be used for small views.

Example:
You have a UIButton *button1 and UIView *view1
You can disable a button and a view in this way:
[view1 setHidden:YES];
[button1 setEnabled:NO];
Enabling a button and a view can be done this way:
[view1 setHidden:NO];
[button1 setEnabled:YES];
Hope this helps..

While this approach fails to meet your preference of not using additional views, simply adding a black view with alpha of 0.6 or so seems to achieve the effect, with the added benefit that you can use this new view to intercept UIEvents so that all the subviews are effectively disabled en masse.
You can even get graphically fancy and instead of just using a black background for the overlaid view, you can fill its background with a radial gradient to achieve the sort of spotlighted effect that happens in iOS when a popup view disables the view behind it...

Related

Animating UIView and Mask

I have a UIImageView which is basically a mole. I want this mole to pop out of a hole. So actually what I have in mind is that I will create a mole some points below the hole and have a mask over it so and then animate up the image view so it looks like its popping out of hole. So following the thought , I had made written down this code :
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = _moleIcon.frame;
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
maskLayer.path = path;
CGPathRelease(path);
_moleIcon.layer.mask = maskLayer;
[UIView animateWithDuration:2.0 animations:^(void){
_moleIcon.transform=CGAffineTransformMakeTranslation(0, 50);
}];
but the problem, is that it seems that the mask itself is moving with the actual UIImageView. Any help would be highly appreciated.
try to use 2 UIViews, just maskView should be bring to front:
[superview bringSubviewToFront:maskView];
and set background color and alpha property for mask view,
maskView.backgroundColor = [UIColor blackColor];
maskView.alpha = 0.8;
and now you can move 2 view directly.
but better is to add 2 view to other view - container, and rotate this one view.

CALayer - How do I create an image with a solid background and transparent text?

I'm trying to create movie generation application by using AVComposition and have a trouble in making title frame.
Each frame is actually a calayer and title layer is on top of other frames.
Title(Text) needs to be transparent with black background so that they can see some part of the first content frame under title text letters.
I searched most articles about calayer mask, but nothing helped me.
I thought this article (How to make only the part covered by text/title transparent in a UIView in IOS) is helpful and coded like Dave's way, but got only white screen.
Here is what I have done:
// create UILabel from the title text
CGRect rectFrame = CGRectMake(0, 0, videoSize.width, videoSize.height);
UILabel *lbTitle = [[UILabel alloc] initWithFrame:rectFrame];
lbTitle.text = self.titleText;
lbTitle.font = [UIFont fontWithName:#"Helvetica" size:60];
lbTitle.textColor = [UIColor blackColor];
lbTitle.backgroundColor = [UIColor whiteColor];
// get title image and create mask layer
UIGraphicsBeginImageContextWithOptions(lbTitle.bounds.size, TRUE, [[UIScreen mainScreen] scale]);
[lbTitle.layer renderInContext:UIGraphicsGetCurrentContext()];
CGImageRef viewImage = [UIGraphicsGetImageFromCurrentImageContext() CGImage];
UIGraphicsEndImageContext();
CALayer *maskLayer = [CALayer layer];
maskLayer.contents = (__bridge id)viewImage;
maskLayer.frame = rectFrame;
// create title background layer and set mastLayer as mast layer of this layer
// this layer corresponds to "UIView's layer" in Dave's method
CALayer *animatedTitleLayer = [CALayer layer];
animatedTitleLayer.backgroundColor = [UIColor whiteColor].CGColor;
animatedTitleLayer.mask = maskLayer;
animatedTitleLayer.frame = rectFrame;
...
[view.layer addSubLayer:animatedTitleLayer];
Here I used animatedTitleLayer as title background(black background), but what I see is white screen.
Anyone can help me? Thanks in advance.
The mask uses the alpha channel to determine what parts to mask out and what parts to keep. However, your label that you render into an image is rendered as black text on a white background so there is no transparency in the image.
You have also specified that the graphics context you are using to render the image is opaque so even if the background color of the label as was clear you would get an opaque image.
So you need to set a clear background color on the label and pass NO as the second argument when you create the graphics context.

iOS: UIView hierarchy: make Map View active under UIView

I have simple UIView hierarchy:
container View:
→ Map View
→ Custom View
My custom view partially overlaps the Map View. I can see a map, but I can't interact with the map e.g. zoom, scroll etc.
How can I archive partially map overlap and interaction in the same time?
Feel free to ask me if you didn't understand something.
EDIT
I want to disable black areas to interaction, but allow interaction in the circle i.e. in the center of my UIView with black overlay areas.
I'm assuming you have something like this:
You want to be able to touch the red area, and touch the map where the yellow area is, but it is being blocked by the yellow subview?
If so, subclass the yellow subview and override the -pointInside: method, which allows you to specify whether a touched point will collide with the view, or fall back to a view behind it.
- (BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
return [[UIBezierPath bezierPathWithOvalInRect:self.bounds] containsPoint:point];
}
if the view only partly covers the view below it, apply a mask to tell IOS that
e.g. from my github fork of XBPageCurl
- (void)applyCornerMaskAsNeeded {
//
//create mask
//
UIImage *cornerImage = [UIImage imageNamed:#"corner_view_mask.png"]; //this is black//white/alpha
CGRect b = self.layer.bounds;
CGRect rtCornerRect=CGRectZero;
UIGraphicsBeginImageContextWithOptions(b.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
//white bg
CGContextSetFillColorWithColor(context, [[UIColor whiteColor] CGColor]);
CGContextFillRect(context, b);
//draw corner image mask
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, cornerImage.size.height);
CGContextConcatCTM(context, flipVertical);
CGContextSetBlendMode(context, kCGBlendModeCopy);
rtCornerRect = [self cornerRectFor:XBPageDragViewCornerTopRight withSize:cornerImage.size.width];
CGContextDrawImage(context, rtCornerRect, cornerImage.CGImage);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//
//apply mask
//
CALayer *l = [CALayer layer];
l.frame = b;
l.contents = (id)image.CGImage;
l.name = #"XBPageDragViewCornersMask";
[self.layer setMask:l];
self.layer.masksToBounds = YES;
}
another example is in the apple docs but this should already be pretty clear:
you draw mask. black, alpha
you apple the mask to your view's layer
done

Drawing sprites programatically for sprite kit with alpha channel?

I'm trying to prototype an app for sprite kit, and it would be advantageous to me to be able to draw the sprites I need programatically for now, and then make actual sprites later. I've made a rough circle, and when I convert to a UIImage (or CGImage) using renderInContext, it shows up on the sprite view with a black background, ignoring the alpha channel, apparently. If I add the UIImage to a regular view as a UIImageView, the alpha channel is rendered properly. I was wondering what I'm doing wrong, or if theres' a workaround for this.
float rockSize = 100;
UIView* drawingView = [[UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
drawingView.backgroundColor = [UIColor clearColor];
CALayer* circleLayer = [CALayer layer];
circleLayer.frame = CGRectMake(0,0,rockSize,rockSize);
circleLayer.backgroundColor = [Utils getTiledColorFromColor:[UIColor purpleColor]].CGColor;
circleLayer.cornerRadius = rockSize/2.0;
circleLayer.masksToBounds = YES;
[drawingView.layer addSublayer:circleLayer];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(rockSize, rockSize), NO, [UIScreen mainScreen].scale);
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [UIColor clearColor].CGColor);
CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(0,0,rockSize,rockSize));
[drawingView.layer renderInContext: UIGraphicsGetCurrentContext()];
UIImage *layerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
SKSpriteNode *rockNode = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImage:layerImage]];
CGPoint position = CGPointMake(200,100);
rockNode.position = position;
[self addChild:rockNode];
Such an affect when you see black view background frequently happens when the view has opaque property incorrectly set to YES without actually being opaque (usually this happens when the view is created programmatically, opaque = YES is default).
You can fix this for example by setting opaque to NO.

add shadow to a layer

I can add shadow to imageView layer using the following code.
self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"test.png"]];
self.imageView.center = self.view.center;
CALayer *containerLayer= [CALayer layer];
containerLayer.shadowColor = [UIColor blackColor].CGColor;
containerLayer.shadowRadius = 10.0f;
containerLayer.shadowOffset = CGSizeMake(10.0f, 5.0f);
containerLayer.shadowOpacity = .8f;
[containerLayer addSublayer:self.imageView.layer];
[self.view.layer addSublayer:containerLayer];
1 . The problem is that I don't know why I have to add imageView.layer to containerLayer to get the imageView shadow effect. However, if I add containerLayer to imageView.layer, there's no shadow in imageView, why?
The error code is:
self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"test.png"]];
self.imageView.center = self.view.center;
CALayer *containerLayer= [CALayer layer];
/*same as before*/
[self.imageView.layer addSublayer:containerLayer];
[self.view.layer addSublayer:self.imageView.layer];
Question2: the containerLayer's(used to provide shadow to imageView) frame = {{0, 0}, {0, 0}}, but the final position is in the center of screen. why?
A layer needs something opaque inside it to create a shadow around (unless you've specified a shadowPath explicitly). So your first version of the code works because the containerLayer has the imageView's layer as a sublayer. But, as you noticed from Question #2, the containerLayer's frame indicates that it is actually located in the upper left corner with a size of (0,0). The reason you can still see the image is that containerLayer is not masking to its bounds. Add this line to your first version, and the image disappears:
[containerLayer setMasksToBounds: YES]; // kitten (and shadow) is gone
Your version #2 of the code does not display a shadow because, in part, the containerLayer does not "contain" anything. If you use version #2 but give the containerLayer a new frame and an opaque background color, a shadow appears. (But this obviously isn't a solution, because the image is covered up...) Also note that there is no shadow when the layer's background is [UIColor clearColor].
[self.imageView.layer addSublayer:containerLayer];
containerLayer.frame = self.imageView.layer.bounds;
containerLayer.backgroundColor = [UIColor yellowColor].CGColor; // yellow box w/shadow
// containerLayer.backgroundColor = [UIColor clearColor].CGColor; // no shadow here
If you wanted to have a container with a shadow that houses the UIImageView, you could do something like this:
UIView * shadowView = [[UIView alloc] initWithFrame: self.imageView.frame];
shadowView.layer.shadowColor = [UIColor blackColor].CGColor;
shadowView.layer.shadowRadius = 10.0f;
shadowView.layer.shadowOffset = CGSizeMake(10.0f, 5.0f);
shadowView.layer.shadowOpacity = .8f;
[self.view addSubview: shadowView];
self.imageView.frame = (CGRect) { CGPointZero, self.imageView.frame.size };
[shadowView addSubview: self.imageView];
A CALayer could be used instead of a UIView in a similar way. Or you could apply shadow attributes to the imageView's layer directly, making sure that neither the view nor the layer is clipping/masking to bounds.
If you want to add a shadow over the imageview just change the alpha value (0.0 min - 1.0 max) of that imageview. This will give you the shadow effect and whenever you want to remove the shadow just restore the alpha value to 1.0.
For ex:
self.imageview.alpha = 0.5 // shadow state
self.imageview.alpha = 1.0 // original state

Resources