I need to highlight an irregular shape when the user taps on it. My idea on how to do it was to draw a polygon that matches the shape, fill it with a color and change the opacity to make it translucent. Unfortunately I can't find anything on how to do this. Essentially, I want to draw the filled polygon, overlay it on to my map and then be able to dismiss (or hide) it. How can I accomplish this?
You probably want to use a CAShapeLayer. Here's a demo:
import XCPlayground
import UIKit
import CoreText
let view = UIView(frame: CGRectMake(0, 0, 300, 300))
XCPShowView("view", view)
let imageView = UIImageView(image: UIImage(named: "profile.jpg"))
imageView.frame = view.bounds
imageView.contentMode = UIViewContentMode.ScaleAspectFill
view.addSubview(imageView)
let shape = CAShapeLayer()
view.layer.addSublayer(shape)
shape.opacity = 0.5
shape.lineWidth = 2
shape.lineJoin = kCALineJoinMiter
shape.strokeColor = UIColor(hue: 0.786, saturation: 0.79, brightness: 0.53, alpha: 1.0).CGColor
shape.fillColor = UIColor(hue: 0.786, saturation: 0.15, brightness: 0.89, alpha: 1.0).CGColor
let path = UIBezierPath()
path.moveToPoint(CGPointMake(120, 20))
path.addLineToPoint(CGPointMake(230, 90))
path.addLineToPoint(CGPointMake(240, 250))
path.addLineToPoint(CGPointMake(40, 280))
path.addLineToPoint(CGPointMake(100, 150))
path.closePath()
shape.path = path.CGPath
Result:
Swift 3:
let shape = CAShapeLayer()
cell.layer.addSublayer(shape)
shape.opacity = 0.5
shape.lineWidth = 2
shape.lineJoin = kCALineJoinMiter
shape.strokeColor = UIColor(hue: 0.786, saturation: 0.79, brightness: 0.53, alpha: 1.0).cgColor
shape.fillColor = UIColor(hue: 0.786, saturation: 0.15, brightness: 0.89, alpha: 1.0).cgColor
let path = UIBezierPath()
path.move(to: CGPoint(x: 120, y: 20))
path.addLine(to: CGPoint(x: 230, y: 90))
path.addLine(to: CGPoint(x: 240, y: 250))
path.addLine(to: CGPoint(x: 100, y: 150))
path.close()
shape.path = path.cgPath
Related
Background
I have some simple code that draws a triangle with three straight sides using UIBezierPath and CAShapeLayer. The result is in image A, below.
However, I want the longest side of the triangle to have an inwards curve. The desired result is in image B, below.
Question
What change to the code below can achieve the longest side of the triangle to have an inwards curve? How do I instruct the path to curve?
Code
func drawShape() {
let path1 = UIBezierPath()
path1.move(to: CGPoint(x: 50, y: 600))
path1.addLine(to: CGPoint(x: 300, y: 600))
path1.addLine(to: CGPoint(x: 300, y: 50))
path1.close()
let shape1 = CAShapeLayer()
shape1.path = path1.cgPath
shape1.fillColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1).cgColor
view.layer.insertSublayer(shape1, at: 1)
}
Image
I have added addQuadCurve before closing the path. It gives the desired result.
Code
func drawShape() {
let path1 = UIBezierPath()
path1.move(to: CGPoint(x: 50, y: 600))
path1.addLine(to: CGPoint(x: 300, y: 600))
path1.addLine(to: CGPoint(x: 300, y: 50))
path1.addQuadCurve(to: CGPoint(x: 50, y: 600), controlPoint: CGPoint(x: (300-50), y: (600*0.5)))
path1.close()
let shape1 = CAShapeLayer()
shape1.path = path1.cgPath
shape1.fillColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1).cgColor
view.layer.insertSublayer(shape1, at: 1)
}
Image
I am trying to create a simple gradient for the background of a UIViewController. My code is:
let gradientLayer = CAGradientLayer()
gradientLayer.frame = view.bounds
let bottomColor = UIColor(hue: 208 / 360, saturation: 82 / 100, brightness: 0.9, alpha: 1)
let topColor = UIColor(hue: 208 / 360, saturation: 41 / 100, brightness: 0.9, alpha: 1)
gradientLayer.colors = [bottomColor, topColor]
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.1)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
view.layer.addSublayer(gradientLayer)
The gradient does not show. When I look in the view debugger, I do not see it. I check the frame of the view and it looks ok
x: 0, y:0, width: 375, height: 667
I've tried the view.layer.insert at method as well, but that did not work. If I do
view.layer.backgroundColor.orange
then I do see an orange background. Am I missing something?
Change
gradientLayer.colors = [bottomColor, topColor]
to
gradientLayer.colors = [bottomColor.cgColor, topColor.cgColor]
I have filled the circle boundary with CAShapeLayer.Now i am giving my CAShape color as red but i want to give it as a gradient color.Please tell me how can i do this?
circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(1.5 * M_PI), endAngle: CGFloat(-0.5 * M_PI), clockwise: false)
//add gradient to the below
progressCircle = CAShapeLayer ()
progressCircle.path = circlePath?.cgPath
progressCircle.strokeColor = UIColor.red.cgColor
progressCircle.fillColor = UIColor.clear.cgColor
progressCircle.lineWidth = 4.0
progressCircle.strokeStart = 0
progressCircle.strokeEnd = 0.7
let gradient: CAGradientLayer = CAGradientLayer()
let startingColorOfGradient = UIColor(colorLiteralRed: 50/255, green: 189/255, blue: 170/255, alpha: 1.0).cgColor
let endingColorOFGradient = UIColor(colorLiteralRed: 133/255, green: 210/255, blue: 230/255, alpha: 1.0).cgColor
gradient.startPoint = CGPoint(x: 1.0, y: 0.5)
gradient.endPoint = CGPoint(x: 0.0, y: 0.5)
gradient.colors = [startingColorOfGradient , endingColorOFGradient]
circle.layer.addSublayer(progressCircle)
circle.layer.addSublayer(gradient)
I think you're missing the gradient frame, the gradient locations and you should add it to the layer's mask. The following example works for me:
let gradient = CAGradientLayer()
gradient.startPoint = CGPoint(x: 1, y: 0.5)
gradient.endPoint = CGPoint(x: 0, y: 0.5)
gradient.frame = CGRect(x: 0, y: 0, width: yourWidth, height: yourHeight)
gradient.colors = [
UIColor(white: 1, alpha: 0.1).cgColor,
UIColor(white: 1, alpha: 0.5).cgColor,
UIColor(white: 1, alpha: 0.9).cgColor
]
gradient.locations = [
0.0,
0.5,
1.0
]
circle.layer.mask = gradient
Hope this helps!
let gradient: CAGradientLayer = CAGradientLayer()
let colorTop = UIColor(red: 112.0/255.0, green: 219.0/255.0, blue: 155.0/255.0, alpha: 1.0).CGColor
let colorBottom = UIColor(red: 86.0/255.0, green: 197.0/255.0, blue: 238.0/255.0, alpha: 1.0).CGColor
gradient.colors = [colorTop, colorBottom]
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.frame = loginButton.bounds
gradient.cornerRadius = 5
loginButton.layer.addSublayer(gradient)
The resulting gradient runs off goes beyond the button's frame. Why does this happen?
Probably you are setting gradient layer in viewDidLoad() or viewWillAppear(). At that time controller did not calculated views sizes. At time you add this sublayer button size was not calculated yet, so sublayer size is wrong. So you should fix next things:
First of all you should add gradient at viewDidAppear(), at this point all view's sizes are calculated.
Second, you should use layer.insertSublayer(layer, atIndex:index) instead of addSublayer(layer). Because in your case sublayer will hide buttons native layer (title, background...)
You should recalculate your sublayer size in viewDidLayoutSubviews(). Because when your button will change it's size (while rotating for example), sublayer will not change it's frame, so you should change it by yourself.
Add clipsToBounds and cornerRadius to loginbutton. That should fix the problem.
loginButton.clipsToBounds = true
loginButton.layer.cornerRadius = 5;
Is your Login Button's frame correct?It seems correct when I reproduce
let loginButton = UIButton(frame: CGRect(x: 10, y: 50, width: 300, height: 30))
self.view.addSubview(loginButton)
let gradient:CAGradientLayer = CAGradientLayer()
let colorTop = UIColor(red: 112.0/255.0, green: 219.0/255.0, blue: 155.0/255.0, alpha: 1.0).CGColor
let colorBottom = UIColor(red: 86.0/255.0, green: 197.0/255.0, blue: 238.0/255.0, alpha: 1.0).CGColor
gradient.colors = [colorTop, colorBottom]
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.frame = loginButton.bounds
gradient.cornerRadius = 5
loginButton.layer.addSublayer(gradient)
And it appear like below
I have tried this and this but still no success. Basically I want to create something like this (inner circle). Circle will be created according to some data lets say if data is 50 we will get a half circle and if its 100 we will get full circle.
And this is what I have so far, so how can I create the above design
I have created a View using interface builder, and drawn these circle using this code
override func viewDidLoad() {
let circlePath = UIBezierPath(arcCenter: CGPoint(x: myView.layer.frame.height/2,y: myView.layer.frame.height/2), radius: CGFloat(100), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.CGPath
shapeLayer.fillColor = UIColor.clearColor().CGColor
shapeLayer.strokeColor = UIColor.grayColor().CGColor
shapeLayer.lineWidth = 3.0
let colorCirclePath = UIBezierPath(arcCenter: CGPoint(x: myView.layer.frame.height/2,y: myView.layer.frame.height/2), radius: CGFloat(100), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
let coloredShapeLayer = CAShapeLayer()
coloredShapeLayer.path = colorCirclePath.CGPath
coloredShapeLayer.fillColor = UIColor.clearColor().CGColor
coloredShapeLayer.strokeColor = UIColor.whiteColor().CGColor
coloredShapeLayer.lineWidth = 10.0
self.myView.layer.addSublayer(coloredShapeLayer)
self.myView.layer.addSublayer(shapeLayer)
}
This is how I am creating the gradient.
let gradient: CAGradientLayer = CAGradientLayer()
let startingColorOfGradient = UIColor(colorLiteralRed: 50/255, green: 189/255, blue: 170/255, alpha: 1.0).CGColor
let endingColorOFGradient = UIColor(colorLiteralRed: 133/255, green: 210/255, blue: 230/255, alpha: 1.0).CGColor
gradient.startPoint = CGPoint(x: 1.0, y: 0.5)
gradient.endPoint = CGPoint(x: 0.0, y: 0.5)
gradient.colors = [startingColorOfGradient , endingColorOFGradient]
self.myView.layer.insertSublayer(gradient, atIndex:0)
If you want a real simulation of gradient color you can check this SO answer .It's based on a cross that divided your rectangular view in 4 portions, then shift the colors on each portion to obtain a regular gradient applied to the layer mask.