I have a UIImageView called zigZag. All I want to do is set a layer mask on it so that it is not visible at all.
I then want to animate the layer mask later so that the UIImageView becomes visible again.
I have been working on this for 4 hours now and can't seem to figure out what the heck I'm doing wrong.
// Setup the mask layer
// Make it the same size as our zigZag image
// so that the entire image is covered and not visible
let maskLayer = CALayer()
maskLayer.frame = self.zigZag!.frame
self.zigZag!.layer.mask = maskLayer
This works, and the zigZag UIImageView is not visible on screen, but here's the thing. I can literally pass in any value I want to maskLayer.frame and it will still be hidden.
This leads me to believe that I am doing something fundamentally wrong when thinking about creating my mask layers. There is a lot more to this problem I am trying to achieve, but I figured the first step was figuring out how to properly set a mask layer to hide an entire UIImageView to make it appear as if it is not even on screen.
Thanks for the help I greatly appreciate it.
is there any other views behind the imageView? if not, try this
let maskLayer = CALayer()
maskLayer.frame = CGRectMake(0, 0, 10, 10)
maskLayer.backgroundColor = UIColor.whiteColor().CGColor
self.zigzagImageView.image = img;
self.zigzagImageView.layer.addSublayer(maskLayer)
Related
In my App I am trying to get a paralax effect in my table. (e.g it's background) which contains a blurred map for each entry.
How I've set it up is i have a XIB with a UITableViewCell and have added the labels to the contentView. Then in code I add the image to the backgroundView. (The backgroundView doesn't exist, and I create an "empty view" to where I add the image).
Then I add the UIInterpolatingMotionEffect to the image view.
This works. But, since the image has the same dimensions as the table view cell, when a person tilts the phone and the paralax effect kicks in white edges appear. As you can see in the image below.
That is not something I want. So I thought, well I could just put a bigger image in the backgroundView. This works while I am not in editing mode. But in editing mode the background image overlaps partially with the delete button. As seen here:
So I thought, I should clip the backgroundView containing the image view. Which I've done with the following code:
self.backgroundView.clipsToBounds = YES;
or this
// Create a mask layer and the frame to determine what will be visible in the view.
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = self.bounds;
// Create a path with the rectangle in it.
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
// Set the path to the mask layer.
maskLayer.path = path;
// Release the path since it's not covered by ARC.
CGPathRelease(path);
// Set the mask of the view.
self.backgroundView.layer.mask = maskLayer;
But neither solutions work. It looks like it is not being clipped. Is there an other way to go about this?
I am wondering if it is possible to clip a view to a Bezier Path. What I mean is that I want to be able to see the view only in the region within the closed Bezier Path. The reason for this is that I have the outline of an irregular shape, and I want to fill in the shape gradually with a solid color from top to bottom. If I could make it so that a certain view is only visible within the path then I could simply create a UIView of the color I want and then change the y coordinate of its frame as I please, effectively filling in the shape. If anyone has any better ideas for how to implement this that would be greatly appreciated. For the record the filling of the shape will match the y value of the users finger, so it can't be a continuous animation. Thanks.
Update (a very long time later):
I tried your answer, Rob, and it works great except for one thing. My intention was to move the view being masked while the mask remains in the same place on screen. This is so that I can give the impression of the mask being "filled up" by the view. The problem is that with the code I have written based on your answer, when I move the view the mask moves with it. I understand that that is to be expected because all I did was add it as the mask of the view so it stands to reason that it will move if the thing it's tied to moves. I tried adding the mask as a sublayer of the superview so that it stays put, but that had very weird results. Here is my code:
self.test = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
self.test.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.test];
UIBezierPath *myClippingPath = [UIBezierPath bezierPath];
[myClippingPath moveToPoint:CGPointMake(100, 100)];
[myClippingPath addCurveToPoint:CGPointMake(200, 200) controlPoint1:CGPointMake(self.screenWidth, 0) controlPoint2:CGPointMake(self.screenWidth, 50)];
[myClippingPath closePath];
CAShapeLayer *mask = [CAShapeLayer layer];
mask.path = myClippingPath.CGPath;
self.test.layer.mask = mask;
CGRect firstFrame = self.test.frame;
firstFrame.origin.x += 100;
[UIView animateWithDuration:3 animations:^{
self.test.frame = firstFrame;
}];
Thanks for the help already.
You can do this easily by setting your view's layer mask to a CAShapeLayer.
UIBezierPath *myClippingPath = ...
CAShapeLayer *mask = [CAShapeLayer layer];
mask.path = myClippingPath.CGPath;
myView.layer.mask = mask;
You will need to add the QuartzCore framework to your target if you haven't already.
In Swift ...
let yourCarefullyDrawnPath = UIBezierPath( .. blah blah
let maskForYourPath = CAShapeLayer()
maskForYourPath.path = carefullyRoundedBox.CGPath
layer.mask = maskForYourPath
Just an example of Rob's solution, there's a UIWebView sitting as a subview of a UIView called smoothView. smoothView uses bezierPathWithRoundedRect to make a rounded gray background (notice on right). That works fine.
But if smoothView has only normal clip-subviews, you get this:
If you do what Rob says, you get the rounded corners in smoothView and all subviews ...
Great stuff.
I've noticed that when I set a color for UISegmentedControl.backgroundColor, the color bleeds beyond the edges of the control (though not beyond the view's bounds). Here's an example with the segmented control's background color set to white and the container view's background color set to gray:
I've set the AutoLayout constraints of the segmented control such that the intrinsicContentSize should be used, but I haven't seen anyone else posting about this problem
Note that the image above is the best I've been able to get it to look... before that it was bleeding over by about 3-4px.
I've tried configuring the view to clipSubviews and the layer backing the UIView to masksToBounds, but I didn't expect that to fix the problem since I assume the bleeding is contained inside the view's/layer's bounds.
Any suggestions or advice appreciated. If not I'll just have to create images to back the UISegmentedControl that fix the bleeding, but that's annoying to have to maintain, to say the least.
Set the segment control's layer's corner radius to 4.0. It should help. You may need to import QuartzCore to be able to access the layer's properties.
segment.layer.cornerRadius = 4.0;
segment.clipsToBounds = YES;
Set segment control layer corner radius to 5. and ClipsToBounds YES .
segmentController.layer.cornerRadius = 5;
segmentController.clipsToBounds = YES;
Hope its work for you
the best result I could achieve in Swift:
segmentedControl.layer.cornerRadius = 4
let mask = CAShapeLayer()
mask.frame = CGRectMake(0, 0, segmentedControl.bounds.size.width-1, segmentedControl.bounds.size.height);
let maskPath = UIBezierPath(roundedRect: mask.frame,
byRoundingCorners: [.BottomLeft, .BottomRight, .TopLeft, .TopRight],
cornerRadii: CGSize(width: 4.0, height: 4.0))
mask.path = maskPath.CGPath
segmentedControl.layer.mask = mask
I'm trying to darken the surrounding area of my UIImageView, and leave a portion alone (which I define with my mask).
Right now I'm defining my mask and setting my imageView.layer.mask, however instead of darkening the rest of the image, it's completely removing it instead.
Example of the type of effect I want:
Example of what I'm getting:
The reference docs mention that the mask uses it's layer's alpha, so I've tried manipulating the mask's opacity. However, that only seems to affect the opacity of the portion I want to leave alone, while the rest of the image is still being cut out completely.
Can anyone point out what I'm doing wrong? Thanks.
Here's my code:
CAShapeLayer *mask = [CAShapeLayer layer];
GMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, 1052, 448);
CGPathAddLineToPoint(path, nil, 2, 484);
CGPathAddLineToPoint(path, nil, 54, 1263);
CGPathAddLineToPoint(path, nil, 56, 1305);
CGPathAddLineToPoint(path, nil, 380, 1304);
CGPathAddLineToPoint(path, nil, 1050, 1311);
CGPathCloseSubpath(path);
mask.path = path;
CGPathRelease(path);
//mask.opacity = 0.5; //doesn't affect the surrounding portion, only the cut out area.
self.imageView.layer.mask = mask;
What you're doing wrong is using a layer mask in the first place. You are trying to shade or darken an area of your image. That's not what a layer mask does at all! Basically a layer mask punches through in an existing layer, causing whatever is behind it to show through. That's exactly what you discovered:
it's completely removing it instead
Yes, because that's what layer masks do! If you don't want that, why are you using a layer mask?
What you want is just to lay a second image view (or just a sublayer) over the first one. It contains an image that you draw. It is transparent except where it has a semi-transparent dark color fill. That will darken what's behind it. You use a clipping path to define the area that doesn't get the dark color fill.
Alternatively, alter the image in your image view by drawing on top of it with compositing or possibly using a CIFilter.
In case anyone else has the same issue: if the goal is to darken the layer, the mask is not suitable. But if you need to make a part of the image transparent or semi-transparent, it is definitely a great way to go.
For example, this code could live in an NSView subclass:
let imageLayer = CALayer()
let maskLayer = CAShapeLayer()
let ovalPath = NSBezierPath(ovalIn: bounds)
maskLayer.path = ovalPath
maskLayer.autoresizingMask = [.layerWidthSizable, .layerHeightSizable]
maskLayer.frame = bounds
maskLayer.backgroundColor = NSColor.black.withAlphaComponent(0.2).cgColor
imageLayer.contents = NSImage(named: "sampleImage")
imageLayer.autoresizingMask = [.layerWidthSizable, .layerHeightSizable]
imageLayer.frame = bounds
imageLayer.mask = maskLayer
This gives 0.2 opacity to an oval 'hole' in the imageLayer.
I'm trying to add some shadows to one of my views and what I would like to achieve is drawing shadows only on one side and let them have sharp edges. No I've tried quite a few methods without any luck (using the shadow related properties of the view's CALayer + UIBezierPaths). However, iOS is always rendering a shadow with soft edges like this:
But what I really want to acchieve is something like this (without round corners and sharp edges on the sides except one):
Is there any elegant way to do this or will I have to draw the shadow myself using CoreGraphics?
PS: I forgot to mention, my view should actually be a custom UIButton, so overriding drawRect: would be a pain here
I've experienced a mask removing the shadow from view...so maybe you can try that.
CALayer *mask = [[[CALayer alloc] init] autorelease];
mask.backgroundColor = [UIColor blackColor].CGColor;
mask.frame = CGRectMake(0.0, 0.0, yellowView.bounds.size.width + shadowWidth, yellowView.bounds.size.height);
yellowView.layer.mask = mask;
I think what you want to be changing is the shadowRadius value - set that to zero and you should get the sharp edge you're looking for:
myView.layer.shadowRadius = 0;