I have a circular UIView. I want to be able to apply a mask that sits on top of it, and rotates above it so that the circular view appears gradually.
Is this even possible in iOS? I know it is in flast. But not sure where would I even start with a task like this in iOS??
You can create a shape layer like in this answer that will look as if it is appearing gradually (like a clock making a full circle). Then you can put the circular content in another layer (or use your view's layer) and make the shape layer the mask:
CAShapeLayer *maskLayer = // see linked answer
yourCircularView.layer.mask = maskLayer;
CABasicAnimation *drawMaskAnimation = // see linked answer
[maskLayer addAnimation:drawMaskAnimation forKey:#"appear gradually"];];
All UIView is backed by CALayer which allows some of the properties to be animated as well as mask to be applied. You will find the tutorial in this page very helpful. www.raywenderlich.com
Related
Want to understand exact difference between masking any layer and adding a layer as sublayer. I have searched and found some answers but not able to understand it correctly.
Something I found like masking causes offscreen rendering but adding a sublayer doesn't so can't we just use addsublayer?
Here I found something but not getting this so if there is any example of this would help more
More specific : Lets have a layer say layer1 now I want to understand difference between following :
addSublayer(layer1)
mask = layer1
We know here that both are applied using main layer of our UIView like view.layer
Layer - is a "next view on super view", but sublayer is a "next layer on super layer".
Hierarhy:
View(Super) -> has view(super) -> has layers(super): [layer1, layer2, etc] ->
layer1(super) -> has sublayers(super) [sublayer1, sublayer2, etc].
sublayers - endpoint in hierarhy.
a view can have more layers. layer can have more sublayers. but sublayer cant have other sublayers.
// sorry for my english :)
If I understand this correctly, the layer is added on top of the view whereas the mask is a subtraction of the current layer.
If you have a gradient layer add it to the view it will follow the gradient direction specified lets say from the bottom to top. But if you add the same gradient as a mask, you would be subtracting the layer and therefore it would like the gradient comes from the top to the bottom. Also you can use masks to cut shapes in images. They are very similar though.
I have a UIImageView displaing an image. This view's layer is masked with CAShapeLayer in order to create circular "hole" in the image. To create the hole I use UIBezierPath with .usesEvenOddFillRule = true.
It works fine when static. But I need that hole to move with user finger. To do that I create new UIBezierPath with even-odd rule each time user moves their finger. On smaller phones with smaller images it looks OK but on iPhone 6 Plus it is choppy.
Any ideas on how to make it smooth are very wellcome. I cannot just move the frame of masking CAShapeLayer - it would move the hole bot also hide some edges of the image. So the only way is to change its .path each time user moves finger and that is slow.
EDIT: matt's answer would work in some scenarios but not in my case: I am not displaying the whole image only a part of it defined by UIBezierPath. This part is most often oval (but can be rectangular or rounded rectangle) and it has "hole" cut in it. While the hole is mowing with users finger the displayed part/shape of the image does not change - it is static.
The ineficient solution that was in place so far wa:
Create UIBezierPath with boundary of displayed part of the image
Set 'even off fill rule' on it
Add UIBezierPath of the hole to it
Set it as path of CAShapeLayer with some opaque fill color
Use that CAShapeLayer as mask of the UIImageView
This procedure was repeated each time a user moved their finger. I cannot simply move the whole mask layer as that would also change the part of the image being displayed. I what it to stay static and move only the hole in it.
it would move the hole bot also hide some edges of the image
Well, I don't agree. Moving the mask is exactly the way to do this. I don't see why you think there's a problem with that. Perhaps the issue is merely that you have not made the mask layer big enough. It does not have to be the same size as the layer it is masking. In this case, it needs to be about 9 times the size of the masked layer (3 horizontal and 3 vertical), so that it will continue to cover the masked the layer no matter how far in any direction the user slides it.
I am having some difficulty understanding on how layer masking works. Right now, I have a UIView with UILabels on it. I picture two layers - one with the UIView in the back and one for the labels on top. If I mask the UIView layer, the labels will be affected by the mask too.
The UILabels are children of the parent UIView, so I can understand a parent mask affecting the children as well.
However, when I look at it in terms of layers, it doesn't seem to make sense. Why does masking the deepest layer affect those on top?
Think of layers as sheets of paper. Think of the view's layer as a big sheet of white paper. As you figured out, the labels' layers are children of the view's layers. To relate, think of the labels' layers being strips of paper glued onto the big view's layer-sheet.
Let's say you wish to mask the layer with a circle. To translate that into our little analogy, you wish to cover the big view's layer-sheet with Harry Potter's invisibility cloak, with a circle shaped hole in it.
To do that, you'd cut the invisibility cloak to the same size as that of your view's layer-sheet.
cloakLayer.frame = bigViewLayer.frame;
Then, you'd carefully cut out a circle from it.
cloakLayer.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(0, 0) radius:15.0 startAngle:0.0 endAngle:2 * M_PI clockwise:YES];
cloakLayer.fillColor = [UIColor blackColor].CGColor; // the hole
Then, you'd paste this cloak-with-a-hole onto your big view's layer-sheet, carefully aligning the edges.
bigViewLayer.mask = cloakLayer;
What's gonna go invisible? Anything on the sheet (because the cloak was cut to the sheet's dimensions) that doesn't fall into the circle you removed from the cloak. That's the mask property.
Let's talk about the masksToBounds property.
Let's say while pasting the strips of label layer-sheets onto the big view's layer-sheet, you decided to place only half of the strip on the sheet, and made the rest hang off the edge(s).
Let's say you set masksToBounds to YES. What the gods of decoupage would do now is neatly cut off the parts of your labels' strips that are not within the edges of the big view's layer-sheet. That's the masksToBounds property.
Let's talk about borders. This is simple. Just pick a sharpie of borderColor whose nib is borderWidth points wide, and carefully draw on the edges of the view's layer-sheet. That's it.
I hope you get things now and can make your own analogies for other properties of the wonderful CALayer.
As you said, the UIView is the parent, and the UILabels are the children. When it comes time to update the screen, the UIView starts with a blank canvas. It draws itself into the canvas, and then has the children draw themselves into the canvas. When the children are drawing, they are subject to constraints imposed by the parent, e.g. clipping and masking.
iOS CALayer.mask
[CALayer]
[iOS CALayer.masksToBounds]
CALayer has a mask property which is CALayer, which applies mask's alpha channel to mask parent's layer.
layer + mask = masked layer
I'm not sure if this has been asked before, but I'm having a hard time finding it. Perhaps I'm not using the right search terms, so if an answer already exists, if someone could point me in the right direction, it'd be most appreciated!
I just noticed that the glimmer animation on the "slide to unlock" text of the lockscreen has changed with the iOS 7.1 update. The spotlight now has an ovular / diamond shape that cascades across the letters without appearing on the view behind it.
In the past, I've replicated this type of feature by changing the color of individual letters sequentially, but for this, the animation goes through the middle of the letters. Without affecting the background.
How can I replicate this?
You can animate label text and use custom slider for it, I hope it helps you:
CALayer *maskLayer = [CALayer layer];
// Mask image ends with 0.15 opacity on both sides. Set the background color of the layer
// to the same value so the layer can extend the mask image.
maskLayer.backgroundColor = [[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.15f] CGColor];
maskLayer.contents = (id)[[UIImage imageNamed:#"Mask.png"] CGImage];
// Center the mask image on twice the width of the text layer, so it starts to the left
// of the text layer and moves to its right when we translate it by width.
maskLayer.contentsGravity = kCAGravityCenter;
maskLayer.frame = CGRectMake(myLabel.frame.size.width * -1, 0.0f, myLabel.frame.size.width * 2, myLabel.frame.size.height);
// Animate the mask layer's horizontal position
CABasicAnimation *maskAnim = [CABasicAnimation animationWithKeyPath:#"position.x"];
maskAnim.byValue = [NSNumber numberWithFloat:myLabel.frame.size.width];
maskAnim.repeatCount = 1e100f;
maskAnim.duration = 1.5f;
[maskLayer addAnimation:maskAnim forKey:#"slideAnim"];
myLabel.layer.mask = maskLayer;
You should be able to use the mask property of CALayer to create a cutout of the contents of another layer.
Set the mask to contain your text (maybe a CATextLayer can work here). This is what Shimmer says it uses.
Make the foreground color of your label be a UIColor initiated with
+colorWithPatternImage or
-initWithPatternImage
using an animated image and setting the background color of the label to transparent. I've not tried this, but I don't see why it wouldn't work.
The best way to do this is with a multi layer object.
Top: UILabel with opaque background and clear text
Clear text is rendered in drawRect: func through complicated masking process
Middle: Worker View that is performing a repeating animation moving an image behind the top label
Bottom: a UIView that you add the middle and top subview to in that order. Can be whatever color you want the text to be
An example can be seen here
https://github.com/jhurray/AnimatedLabelExample
The most effective way I've found to recreate the glimmering text effect is to use the Shimmer Cocoapod created by Facebook. Below is the example image from the Shimmer GitHub repo, which is located at the following URL: https://github.com/facebook/Shimmer
Shimmer example
There are full instructions to install and use Shimmer on the repo, but the gist is that after installing the Cocoapod you'll add a special subview or layer into which will go the contents you wish to have glimmer/shimmer, then set the effect to start.
Try to have a semi-transparent foreground with transparent cutouts for the letters. The "glimmer" can be moved across behind the cutouts.
Make a layer on top that has cutout layers with an animated PNG or something as the background.
Under this layer, have another layer with exactly the reverse transparency (letters are opaque and space between letters is transparent.
This way, the user sees through the letters to the animation, and between the letters to whatever the letters are over.
Just make sure you have code to keep the layers in the right order.
I think that it's a semi transparent view, but it's a special view in which the drawrect is overridden to color each pixel of the letters with the same color (but stronger to make it visible) of the pixel in the view beneath it.
Imagine this like the magnifying view. it displays a magnified version of the the view beneath it.
I have an UIView in which I draw many shapes. I just want to keep redrawing this view in order to make kind of an animation. I searched for animation options, but all animations looks like they only work with properties, like transform, alpha... I just want a timed animation option, and that do not block the screen, I mean, that allows the application to realize the screen was tapped. Is it possible?
It is totally possible and you have a few different ways that you can go about doing it. If you want a simple png sequence style animation you can just fill a UIImageView like this:
imageView.animationImages = myImages;
imageView.animationDuration = 3;
[imageView startAnimating];
or you can override the drawrect function in a custom UIView, set up an NSTimer to tick however frequently you want to change the animation and call setNeedsDisplay on the view to draw the next frame.
Assuming you have a bezier path that represents each shape, try looking at CAShapeLayer - this can be used to draw a path on the screen, and you can animate the position, fill colour and many other properties of it.
Have one CAShapeLayer per shape, and add them as sub layers to your main view's layer.
You need to add the QuartzCore framework to use it, but it is very straightforward and there are plenty of tutorials out there.