Gradientmask for UIVisualEffectView - ios

I know that UIVisualEffectView ist very uncustomizable, so I can't setup the radius of blurness of the view or even the color.
Now I realized I could not even mask one.
I want to realize a Tabbar with blured Background, but to the top corner it gets sharper till 100%.
Because I know I couldn't adjust the blur radius I had the idea to work with a gradient mask to archieve something like this:
But as sad at the beginning I could not even mask a simple Rectangle:
let gradientMask = CAGradientLayer()
gradientMask.frame = effectView.frame
gradientMask.colors = [UIColor.black.cgColor, UIColor.clear.cgColor]
effectView.layer.mask = gradientMask
The result is, the UIVisualEffectView doesn't show at all anymore.
Have you guy a workaround or something else?
EDIT: The view in the screenshot is for example, in the final app the background is a dynamic list with tiles where I can scroll through. So the workaround with snapshots will not work in my case.

Related

How can I implement the iMessage gradient in Swift iOS?

What I'm trying to Achieve
I am trying to implement the gradient bubble effect in Swift iOS, where the chat bubbles towards the top are a lighter color and the chat bubbles towards the bottom are a darker color, and when you scroll a bubble you see the gradient change.
The link below is an example of the iMessage gradient effect.
An example image of the iMessage gradients
What I've Tried
I created a view and added a gradient layer:
let gradient = CAGradientLayer()
gradient.startPoint = CGPoint(x: 1, y: 0)
gradient.endPoint = CGPoint(x: 0, y: 1)
gradient.colors = [UIColor.systemBlue.cgColor, UIColor.systemPink.cgColor]
gradient.frame = UIScreen.main.bounds
view.layer.addSublayer(gradient)
view.backgroundColor = .yellow
I created a view and used it as a mask
mask.backgroundColor = .yellow
mask.alpha = 1
mask.frame.size = CGSize(width: 100, height: 100)
mask.center = view.center
view.mask = mask
The result is like this Gif below:
Example of my progress using a Mask View
I was originally hoping to add a gradient to a UICollectionView and have the UICollectionViewCells mask the gradient to achieve the above iMessage gradient effect, but then I realized I can only apply one mask to a UIView (not multiple), so I'm stuck on using this approach.
Other Ideas
My other ideas were to apply a gradient to each UICollectionViewCell and determine the gradient offset of each UICollectionViewCell manually based on the location of the cell, however, my main concern is this is not going to have good performance.
Please Help
I was hoping to see if anyone could outline a general method or link to how to achieve the iMessage gradient background effect?
I understand this is a more general question and often times general questions are "bad" questions for stack overflow, but I'm really stuck on this problem and would incredibly appreciate any help or advice for achieving this effect!
Thank you for your time!
Solution Figured Out
Wow, this is sketchy/hacky to implement. Below is a GitHub Gist of how its done.
https://gist.github.com/josharnoldjosh/e04d41f10de6ab378da931ab11370d11
The way it works is you set a background to the gradient, then, you mask each individual cell, "cutting out" a hole in the cell to be transparent. The rest of the cell must be white to simulate the background being white.
There are multiple parts to this. First, you need to set up a gradient that is top-to-bottom. Your current gradient goes from the top right to the bottom left.
Change these 2 lines:
gradient.startPoint = CGPoint(x: 1, y: 0)
gradient.endPoint = CGPoint(x: 0, y: 1)
To
gradient.startPoint = CGPoint(x: 0.5, y: 0)
gradient.endPoint = CGPoint(x: 0.5, y: 1)
Next is the issue of how to make the view take on the gradient color tied to the screen, as you scroll the table view/collection view. That is not easy, and I'm not sure how you'd do that. I would probably have to attach code to the table view/collection view's UIScrollViewDelegate and implement the scrollViewDidScroll(_:) method, figure out the change in scroll view offset, and shift the gradient layer to match the scroll offset.

Gradient view not displaying on simulator swift

so i added a gradient view to my UIView but when i run the app it doesn't display the UIVIEW properly please have a look at the screen shots i tried it running without the gradient and view worked perfectlyhere you can see the code i wrote for gradient view
and this is main story board
First of all, both of your gradient colors are white.
The cause why your gradient is not dispalying is you're specifying the locations wrong. There is no need to setup startPoint and endPoint so you can delete those lines.
gradient.startPoint = CGPoint.zero
gradient.endPoint = CGPoint(x: 0, y: 1)
And edit the locations to
gradient.locations = [0,1]
If you want to show the colors other way around, just switch them in the colors property.
Next time it would be useful to post the code regular way not as an screenshot.

Can't get layer mask to work properly

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)

Fading out items in UICollectionView

I have a UICollectionView and I'm implementing sticky headers as per this link: http://blog.radi.ws/post/32905838158/sticky-headers-for-uicollectionview-using#notes
It works fantastically however my window has a background image applied, and my header views have a transparent background. Consequentially, when my items scroll above the header view, you can still see them.
Ideally I would fade out the cells with a gradient, to the point it is invisible by the time it appears behind the header view.
Thanks.
You haven't posted any code, so here's a go at it without looking at code. Just setup a mask layer over your UICollectionView's superview and you're good to go:
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.collectionView.superview.bounds;
gradient.colors = #[(id)[UIColor clearColor].CGColor, (id)[UIColor blackColor].CGColor];
// Here, percentage would be the percentage of the collection view
// you wish to blur from the top. This depends on the relative sizes
// of your collection view and the header.
gradient.locations = #[#0.0, #(percentage)];
self.collectionView.superview.layer.mask = gradient;
For this solution to work properly, you'd have to embed your collection view in a super view of its own.
For more information on layer masks, check out the documentation.
I created a fade mask over a collectionview that has this kind of effect. Maybe you're looking for something similar.
// This is in the UICollectionView subclass
private func addGradientMask() {
let coverView = GradientView(frame: self.bounds)
let coverLayer = coverView.layer as! CAGradientLayer
coverLayer.colors = [UIColor.whiteColor().colorWithAlphaComponent(0).CGColor, UIColor.whiteColor().CGColor, UIColor.whiteColor().colorWithAlphaComponent(0).CGColor]
coverLayer.locations = [0.0, 0.5, 1.0]
coverLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
coverLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
self.maskView = coverView
}
// Declare this anywhere outside the sublcass
class GradientView: UIView {
override class func layerClass() -> AnyClass {
return CAGradientLayer.self
}
}
Additionally, you can make it sticky (i.e. it will always fade out the cells on the edge, instead of scrolling with the collection) by adding this to the collectionview subclass.
func scrollViewDidScroll(scrollView: UIScrollView) {
self.maskView?.frame = self.bounds
}
would seem to me the code you are following/using has done heavy work for you. As far I can see (not in position to test right now) just pass the alpha attribute:
layoutAttributes.zIndex = 1024;
layoutAttributes.frame = (CGRect){
.origin = origin,
.size = layoutAttributes.frame.size
like such
layoutAttributes.zIndex = 1024;
layoutAttributes.alpha = 0.1; //add this
layoutAttributes.frame = (CGRect){
.origin = origin,
.size = layoutAttributes.frame.size
instead of having a transparent background on your header, I would create a gradient transparent png and use that instead. It'd be a lot more efficient and easier handling the gradient with an image than doing it with code.
You should use a UIScrollViewDelegate for the CollectionView and use the scrollviewdidscroll method to create the fade, or subclass UICollectionViewFlowLayout.
Here is how I achieved that effect. I created in photoshop a gradient image, fading to the color of the background, which is in my case black. Here's what it looks like:
I placed the ImageView on my ViewController. I stretched it to the correct size and location of where I wanted and used AutoLayout constraints to lock it in place. (I had to use the arrow keys on my keyboard to move it around at times because clicking and dragging the location of the image tended to drop it inside of the CollectionView)
Click the ImageView, go to Editor -> Arrange -> Send to Front to make sure it sits on top of the CollectionView.
Image mode is Scale to Fill, and I have deselected User Interaction Enabled.
This will take some tweaking to get everything perfect but it works very well and looks nice.
I'm not entirely sure how you mean by with your background image and whatnot, but maybe make the gradient image part of the actual background image you have, so it blends in.

iOS: drop shadow with sharp edges

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;

Resources