How to crop the UIView as semi circle? - swift

I want to crop UIView in semi circle shape
Thanks in advance.

A convenient way is just subclass a UIView, add a layer on it and make the view color transparent if it's not by default.
import UIKit
class SemiCirleView: UIView {
var semiCirleLayer: CAShapeLayer!
override func layoutSubviews() {
super.layoutSubviews()
if semiCirleLayer == nil {
let arcCenter = CGPoint(x: bounds.size.width / 2, y: bounds.size.height / 2)
let circleRadius = bounds.size.width / 2
let circlePath = UIBezierPath(arcCenter: arcCenter, radius: circleRadius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * 2, clockwise: true)
semiCirleLayer = CAShapeLayer()
semiCirleLayer.path = circlePath.cgPath
semiCirleLayer.fillColor = UIColor.red.cgColor
layer.addSublayer(semiCirleLayer)
// Make the view color transparent
backgroundColor = UIColor.clear
}
}
}

This question was already answered here: Draw a semi-circle button iOS
This is a extract of that question but using a UIView:
Swift 3
let myView = [this should be your view]
let circlePath = UIBezierPath(arcCenter: CGPoint(x: myView.bounds.size.width / 2, y: 0), radius: myView.bounds.size.height, startAngle: 0.0, endAngle: CGFloat(M_PI), clockwise: true)
let circleShape = CAShapeLayer()
circleShape.path = circlePath.cgPath
myView.layer.mask = circleShape
Swift 4
let myView = [this should be your view]
let circlePath = UIBezierPath(arcCenter: CGPoint(x: myView.bounds.size.width / 2, y: 0), radius: myView.bounds.size.height, startAngle: 0.0, endAngle: .pi, clockwise: true)
let circleShape = CAShapeLayer()
circleShape.path = circlePath.cgPath
myView.layer.mask = circleShape
I hope this helps you

it works in swift 4 and swift 5 for this issue.
let yourView = (is the view you want to crop semi circle from top)
let circlePath = UIBezierPath(arcCenter: CGPoint(x: yourView.bounds.size.width / 2, y: yourView.bounds.size.height), radius: yourView.bounds.size.height, startAngle: 0.0, endAngle: .pi, clockwise: false)
let circleShape = CAShapeLayer()
circleShape.path = circlePath.cgPath
yourView.layer.mask = circleShape

Related

draw an arc from endpoints of line and fill in

I did the following:
let xOrigin = (pointsOfPerformance[POP.gluteusMaximus.index].x_origin / 375) * self.view.bounds.width
let yOrigin = (pointsOfPerformance[POP.gluteusMaximus.index].y_origin / 666.6666) * (self.view.bounds.width * 16 / 9)
let p0 = CGPoint(x: xOrigin, y: yOrigin)
let xDest = (pointsOfPerformance[POP.upperBack.index].x_origin / 375) * self.view.bounds.width
let yDest = (pointsOfPerformance[POP.upperBack.index].y_origin / 666.6666) * (self.view.bounds.width * 16 / 9)
let p1 = CGPoint(x: xDest, y: yDest)
let midPoint = CGPoint(x: (xOrigin + xDest) / 2, y:
(yOrigin + yDest) / 2)
let path = UIBezierPath()
path.move(to: p0)
path.addLine(to: p1)
path.addArc(withCenter: p2, radius: 94, startAngle: 200, endAngle: 130, clockwise: true)
let centerOfCircle = makePoint(xOffset: p2.x, yOffset: p2.y)
view.addSubview(centerOfCircle)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.animationRed.cgColor
shapeLayer.strokeColor = UIColor.animationRed.cgColor
shapeLayer.lineWidth = 0.05
view.layer.addSublayer(shapeLayer)
How do I get the circle to only show up as a slice of its curve that comes off the line on the back. It's unclear how I manipulate the function to get only the part coming off the line on the back visible.
This works on MacOS:
class CustomView: NSView {
override func draw(_ rect: CGRect) {
let bkgrnd = NSBezierPath(rect: rect)
let fillColor = NSColor.white
fillColor.set()
bkgrnd.fill()
let center = NSMakePoint(rect.size.width/2,rect.size.height/2)
let radius:CGFloat = rect.size.width/4
let path1 = NSBezierPath()
path1.appendArc(withCenter: center, radius: radius, startAngle: 90, endAngle: 270)
NSColor.gray.set()
path1.fill()
let path2 = NSBezierPath()
path2.appendArc(withCenter: center, radius: radius, startAngle: 270, endAngle: 90)
NSColor.blue.set()
path2.fill()
}
}
Demonstrates getting a small slice of the periphery of a circle for MacOS:
class CustomView: NSView {
override func draw(_ rect: CGRect ) {
let bkgrnd = NSBezierPath(rect: rect)
let fillColor = NSColor.white
fillColor.set()
bkgrnd.fill()
let center = NSMakePoint(rect.size.width/2,rect.size.height/2)
let radius:CGFloat = rect.size.width/4
let circle = NSBezierPath()
circle.appendArc(withCenter: center, radius: radius, startAngle: 0, endAngle: 360)
NSColor.gray.set()
circle.fill()
let arc = NSBezierPath()
arc.appendArc(withCenter: center, radius: radius, startAngle: 120, endAngle: 200)
NSColor.blue.set()
arc.fill()
}
}

Set position of outerBelowCircle CAShapeLayer in the center of View

I make a circle by using CAShapeLayer, and I want to set the center of it, in the center of the view. But the circle will be created exactly below the center line (see the screenshot), could anyone help me on this?
let circle = CAShapeLayer()
view.layer.addSublayer(circle)
circle.position = view.center
let circularPath = UIBezierPath(arcCenter: .zero, radius: 100, startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true)
circle.path = circularPath.cgPath
I also change the way that for setting it in the center, but It doesn't work
circle.position = CGPoint(x: view.layer.bounds.midX, y: view.layer.bounds.midY)
Consider the following method according to your screenshot this method will resolve your issue.
func showCircle() {
let view = UIView()
let circle = CAShapeLayer()
view.layer.addSublayer(circle)
let positionX = view.center.x
let positionY = view.center.y - 100 // your circle radius
circle.position = CGPoint(x: positionX, y: positionY)
let circularPath = UIBezierPath(arcCenter: .zero, radius: 100, startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true)
circle.path = circularPath.cgPath
}
I haven't tested the code. Please verify it by yourself.

How to call UIBezier outline in Swift Playground?

EDIT: Sorry, I wasn't clear originally. I want to get the "outline" path of a line or shape. I'm specifically trying to understand how to use:
context.replacePathWithStrokedPath()
and / or:
CGPathRef CGPathCreateCopyByStrokingPath(CGPathRef path, const CGAffineTransform *transform, CGFloat lineWidth, CGLineCap lineCap, CGLineJoin lineJoin, CGFloat miterLimit);
https://developer.apple.com/documentation/coregraphics/1411128-cgpathcreatecopybystrokingpath?language=objc
I'm not looking for workarounds, thanks.
=====
I'm really trying to wrap my head around drawing a line with an outline around it. i'm using UIBezier, but running into brick walls. So far, I've got this:
import UIKit
import PlaygroundSupport
let screenWidth = 375.0 // points
let screenHeight = 467.0 // points
let centerX = screenWidth / 2.0
let centerY = screenHeight / 2.0
let screenCenterCoordinate = CGPoint(x: centerX, y: centerY)
class LineDrawingView: UIView {
override func draw(_ rect: CGRect) {
let path = UIBezierPath()
path.lineWidth = 5
path.lineCapStyle = .round
//Move to Drawing Point
path.move(to: CGPoint(x:20, y:120))
path.addLine(to: CGPoint(x:200, y:120))
path.stroke()
let dot = UIBezierPath()
dot.lineWidth = 1
dot.lineCapStyle = .round
dot.move(to: CGPoint(x:200, y:120))
dot.addArc(withCenter: CGPoint(x:200, y:120), radius: 5, startAngle: CGFloat(0.0), endAngle: CGFloat(8.0), clockwise: true)
UIColor.orange.setStroke()
UIColor.orange.setFill()
path.stroke()
dot.fill()
let myStrokedPath = UIBezierPath.copy(path)
myStrokedPath().stroke()
}
}
let tView = LineDrawingView(frame: CGRect(x: 0,y: 0, width: screenWidth, height: screenHeight))
tView.backgroundColor = UIColor.white
PlaygroundPage.current.liveView = tView
So, where am I going wrong in this? I cannot seem to figure out where to use CGPathCreateCopyByStrokingPath...or how...
EDIT 2:
Ok, now I've got this. Closer, but how do I fill the path again?
let c = UIGraphicsGetCurrentContext()!
c.setLineWidth(15.0)
let clipPath = UIBezierPath(arcCenter: CGPoint(x:centerX,y:centerY), radius: 90.0, startAngle: -0.5 * .pi, endAngle: 1.0 * .pi, clockwise: true).cgPath
c.addPath(clipPath)
c.saveGState()
c.replacePathWithStrokedPath()
c.setLineWidth(0.2)
c.setStrokeColor(UIColor.black.cgColor)
c.strokePath()
The class was modified slightly to produce this graphic:
The path was not copied in the modified code. Instead the existing path was used to draw, then modified and reused. The dot did not have a stroke so that was added. Since only closed paths can be filled, I drew a thinner path on top of a thicker path by changing the line width.
This is the modified code:
class LineDrawingView: UIView {
override func draw(_ rect: CGRect) {
let path = UIBezierPath()
path.lineWidth = 7
path.lineCapStyle = .round
//Move to Drawing Point
path.move(to: CGPoint(x:20, y:120))
path.addLine(to: CGPoint(x:200, y:120))
path.stroke()
let dot = UIBezierPath()
dot.lineWidth = 1
dot.lineCapStyle = .round
dot.move(to: CGPoint(x:200, y:120))
dot.addArc(withCenter: CGPoint(x:200, y:120), radius: 5, startAngle: CGFloat(0.0), endAngle: CGFloat(8.0), clockwise: true)
dot.stroke()
UIColor.orange.setStroke()
UIColor.orange.setFill()
path.lineWidth = 5
path.stroke()
dot.fill()
}
}
So, I've found the (an) answer. I used CAShapeLayer:
let c = UIGraphicsGetCurrentContext()!
c.setLineCap(.round)
c.setLineWidth(15.0)
c.addArc(center: CGPoint(x:centerX,y:centerY), radius: 90.0, startAngle: -0.5 * .pi, endAngle: (-0.5 * .pi) + (3 / 2 * .pi ), clockwise: false)
c.replacePathWithStrokedPath()
let shape = CAShapeLayer()
shape.path = c.path
shape.fillColor = UIColor.yellow.cgColor
shape.strokeColor = UIColor.darkGray.cgColor
shape.lineWidth = 1
myView.layer.addSublayer(shape)
It works well enough, but not on overlapping layers. I need to learn how to connect contours or something.

How to remove arc border in shadowed UIView with rounded / arc path?

I have ticket View like this
.
My method is I have 2 view, 1 is the ticket itself, and other is for shadow. I have to do this because if I mask the view, it got clipped and the shadow will not appear in the ticket view.
here is the code for create the ticket view:
let shapeLayer = CAShapeLayer()
shapeLayer.frame = someView.bounds
shapeLayer.path = UIBezierPath(roundedRect: someView.bounds,
byRoundingCorners: [UIRectCorner.bottomLeft,UIRectCorner.bottomRight] ,
cornerRadii: CGSize(width: 5.0, height: 5.0)).cgPath
let rect = CGRect(x:0, y:0, width:200, height:100)
let cornerRadius:CGFloat = 5
let subPathSideSize:CGFloat = 25
let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
let leftSubPath = UIBezierPath(arcCenter: CGPoint(x: rect.width / 2, y: 0),
radius: subPathSideSize / 2, startAngle: .pi, endAngle: .pi * 0, clockwise: false)
leftSubPath.close()
let rightSubPath = UIBezierPath(arcCenter: CGPoint(x: rect.width / 2, y: rect.height),
radius: subPathSideSize / 2, startAngle: .pi, endAngle: .pi * 0, clockwise: true)
rightSubPath.close()
path.append(leftSubPath)
path.append(rightSubPath.reversing())
let mask = CAShapeLayer()
mask.frame = shapeLayer.bounds
mask.path = path.cgPath
someView.layer.mask = mask
Notes: SomeView is the TicketView.
And here is the code for adding shadow:
let shadowMask = CAShapeLayer()
shadowMask.frame = shadowView.bounds
shadowMask.path = path.cgPath
shadowMask.shadowOpacity = 0.2
shadowMask.shadowRadius = 4
shadowMask.masksToBounds = false
shadowMask.shadowOffset = CGSize(width: 0, height: 2)
shadowView.backgroundColor = UIColor.clear
shadowView.layer.addSublayer(shadowMask)
The shadow makes arc/rounded corner have border like this one (marked with circle red).
Here is my Playground gist
Do you know how to remove the border in the rounded corner and arc path?
Thank you.
u need to add clipToBounds in this block that you have written.
let mask = CAShapeLayer()
mask.frame = shapeLayer.bounds
mask.path = path.cgPath
someView.clipsToBounds = true
someView.layer.mask = mask
So I think there is different calculation for corner in mask and path. So I used fillColor of the shadowLayer to match the color of the CouponView. And now, the borders are gone.
shadowLayer.fillColor = someView.backgroundColor.cgColor

How to change the endAngle programmatically

I would like to create an arc with Swift and SpriteKit by doing something like this:
func createArc(endAngle: CGFloat) -> SKShapeNode {
let bezierPath = UIBezierPath(arcCenter: CGPointMake(0, 0), radius: 300, startAngle: CGFloat(M_PI_2), endAngle: endAngle, clockwise: false)
let pathNode = SKShapeNode(path: bezierPath.CGPath)
pathNode.strokeColor = SKColor.blackColor()
pathNode.lineWidth = 50
pathNode.position = CGPoint(x: size.width/2, y: size.height/2)
pathNode.antialiased = true
return pathNode
}
But how should I change the endAngle value of the UIBezierPath programmatically say in the update() function?