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.
Related
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.
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.
I have a UISlider that I need to draw on top of on at arbitrary points along the slider.
I can draw the tick marks in viewDidLoad, but since I can't yet get the correct bounds of the UISlider at this point they are drawn in the wrong places. If I draw them in viewDidLayoutSubviews I do get the correct bounds, but the tick marks don't get displayed.
I'm trying to draw these marks as follows:
override func viewDidLayoutSubviews(){
super.viewDidLayoutSubviews()
writeTickMarks()
}
func writeTickMarks(){
// create a vertical line
let path = UIBezierPath()
path.move(to: CGPoint(x: calculatedXValue, y: calculatedYValue))
path.addLine(to: CGPoint(x: calculatedXValue, y: calculatedYValue + 5))
// draw the vertical line in blue with a thickness of 2
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.opacity = 0.5
shapeLayer.lineWidth = 2
// add the shape to the slider's view
self.slider.superview?.layer.addSublayer(shapeLayer)
}
I've tried calling setNeedsDisplay and layoutIfNeeded on self.view as well as self.slider.superview immediately following the call to writeTickMarks, but these calls don't seem to have any affect. :-/
How can I programmatically draw these new layers to my UISlider?
Disclaimer - I can't answer why the ticks aren't displayed at all when you call your method from viewDidLayoutSubviews, that's pretty odd. But...
I have had numerous headaches with the same concept - The view controller lifecycle and when it actually understands the view's true geometry. The trouble with viewDidLayoutSubviews is that it can often get called multiple times when a UI loads, and that can cause CALayers to be added multiple times.
I also have a CAShapeLayer which I use with a UIBezierPath. I have a hacky solution that works for me.
I do call the CAShapeLayer init method in viewDidLayoutSubviews (equivalent to your writeTickMarks() method).
I made my CAShapeLayer a class-level property, and check whether it's non-nil on every method call:
if self.shapeLayer != nil {
self.shapeLayer!.removeFromSuperView()
self.shapeLayer = nil
}
I remove and de-allocate it if it is non-nil to avoid it being added on every invocation of viewDidLayoutSubviews.
It's not very efficient, but gets around the multiple calls of viewDidLayoutSubviews and the unknown geometry in viewDidLoad.
The code should work as it is (and without the setNeedsDisplay/layoutIfNeeded calls). Check to make sure you aren't just drawing things off the screen. I've done something similar myself where my calculated y values were putting my points 900px below the bottom edge of the screen.
Im having a really stupid problem with a UIView. I create the UIView programatically and then make it a circle by making the corner radius half the height of the view looks like this:
But when I try to animate the circle to a different location it becomes all deformed like this:
Here is the code I am using to animate the view just a simple UIView.animateWithDuration:
UIView.animateWithDuration(2, delay: 0, options: UIViewAnimationOptions.AllowUserInteraction, animations: {
self.secondDot.frame.origin = CGPoint(x: self.view.frame.size.width - self.secondDot.frame.size.width, y: 0)
self.secondDot.frame.size = self.secondDot.frame.size
}, completion: nil)
I should also add that the circle doesn't always become deformed but more often than not it does. Please help any suggestions would be much appreciated.
EDIT: I should mention that the circle is in motion already before the
UIView.animationWithDuration occurs I don't know if that could be a
problem
What is the point of this line?
self.secondDot.frame.size = self.secondDot.frame.size
You either
want to resize the circle uniformly while moving
or not.
If case 1) the the newSize.width != newSize.height...but thats a problem because your circle is not a "square" anymore behind the scenes.
If case 2) just remove the lines.
I had exactely the same situation.
After some research, I've found comment in header file for setFrame method
// animatable. do not use frame if view is transformed since it will
not correctly reflect the actual location of the view. use bounds +
center instead.
I have decided to use setCenter instead of setFrame which solved the distortion issue.
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.