How to get a border on UIBezierPath - iphone

I have this
CGRect container = CGRectMake(conX, conY, 220, 50);
UIBezierPath* path = [UIBezierPath bezierPathWithRoundedRect:container cornerRadius:5.0];
[[UIColor blueColor] setFill];
[path fillWithBlendMode:kCGBlendModeNormal alpha:0.7];
I want to get a gray borer around it, have looked at a lot of stuff online but can't see a way to do it easily, is it possible?

If you're not opposed to using layers, something like this could work for you:
//create triangle path
UIBezierPath* path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, 30)];
[path addLineToPoint:CGPointMake(100, 30)];
[path addLineToPoint:CGPointMake(100, 0)];
[path addLineToPoint:CGPointMake(0, 30)];
//apply path to shapelayer
CAShapeLayer* greenPath = [CAShapeLayer layer];
greenPath.path = path.CGPath;
[greenPath setFillColor:[UIColor greenColor].CGColor];
[greenPath setStrokeColor:[UIColor blueColor].CGColor];
greenPath.frame=CGRectMake(0, 0,100,30);
//add shape layer to view's layer
[[self layer] addSublayer:greenPath];

You can get bezierPath using this extension.
let path = UIBezierPath(yourView.bounds, cornerRadius: 12)
let layer = CAShapeLayer()
layer.masksToBounds = false
layer.path = path.cgPath
layer.strokeColor = UIColor.red.cgColor
layer.fillColor = UIColor.clear.cgColor
layer.lineWidth = 5
layer.addSublayer(topLayer)
extension UIBezierPath {
static func borderPathInRect(_ drawRect: CGRect, cornerRadius: CGFloat) -> UIBezierPath {
let path = UIBezierPath()
path.move(to: CGPoint(x: drawRect.origin.x, y: cornerRadius))
path.addArc(withCenter: CGPoint(x: cornerRadius, y: cornerRadius),
radius: cornerRadius,
startAngle: CGFloat.pi,
endAngle: 3/2*CGFloat.pi ,
clockwise: true)
path.addLine(to: CGPoint(x: drawRect.size.width - cornerRadius, y: 0))
path.addArc(withCenter: CGPoint(x: drawRect.size.width - cornerRadius, y: cornerRadius),
radius: cornerRadius,
startAngle: 3/2*CGFloat.pi,
endAngle: 2*CGFloat.pi,
clockwise: true)
path.addLine(to: CGPoint(x: drawRect.size.width, y: drawRect.size.height - cornerRadius))
path.addArc(withCenter: CGPoint(x: drawRect.size.width - cornerRadius, y: drawRect.size.height - cornerRadius),
radius: cornerRadius,
startAngle: 0,
endAngle: CGFloat.pi/2,
clockwise: true)
path.addLine(to: CGPoint(x: cornerRadius, y: drawRect.size.height))
path.addArc(withCenter: CGPoint(x: cornerRadius, y: drawRect.size.height - cornerRadius),
radius: cornerRadius,
startAngle: CGFloat.pi/2,
endAngle: CGFloat.pi,
clockwise: true)
path.addLine(to: CGPoint(x: 0, y: cornerRadius))
path.lineJoinStyle = .round
return path
}
}

Related

Need to create custom shape for UIView Swift

I need to create this custom UIView for my project.I need to achieve this
I was doing trial and error by creating, My own custom UIView and then drawing on it.
I'm working on this
My Question is, if I'm able to draw my custom shape, How will I clip-off the remaining white space of the UIView
func drawCustomShape() {
let viewSize = self.frame.size
let buttonSlotRadius = CGFloat(30)
let effectiveViewHeight = viewSize.height - buttonSlotRadius
let path = UIBezierPath()
path.move(to: CGPoint(x: 30, y: 0))
path.addLine(to: CGPoint(x: viewSize.width - 30, y: 0))
path.addArc(withCenter: CGPoint(x: viewSize.width - 30,
y: 30),
radius: 30,
startAngle: .pi * 3 / 2,
endAngle: 0,
clockwise: true)
path.addLine(to: CGPoint(x: viewSize.width, y: effectiveViewHeight))
path.addArc(withCenter: CGPoint(x: viewSize.width - 30,
y: effectiveViewHeight - 30),
radius: 30,
startAngle: 0,
endAngle: .pi / 2,
clockwise: true)
path.addLine(to: CGPoint(x: viewSize.width / 4, y: effectiveViewHeight))
// close path join to origin
path.close()
UIColor.secondarySystemFill.setFill()
path.fill()
path.lineWidth = 6.0
path.stroke()
}
You can use UIView with clear background color, and use CAShapeLayer with desired path like this:
extension UIView {
func drawCustomShape() {
let cornerRadius: CGFloat = 18.0
let shapeOffset = self.frame.size.height * 0.2
//create shape layer
let shapeLayer = CAShapeLayer()
shapeLayer.frame = self.bounds
shapeLayer.lineWidth = 1.0
shapeLayer.fillColor = UIColor.white.cgColor
self.layer.addSublayer(shapeLayer)
//create path
let path = UIBezierPath()
//top left point
path.move(to: CGPoint(x: 0, y: cornerRadius))
//top left corner
path.addQuadCurve(to: CGPoint(x: cornerRadius, y: 0),
controlPoint: CGPoint(x: 0, y: 0))
//top right point
path.addLine(to: CGPoint(x: self.frame.size.width - cornerRadius, y: 0))
//top right corner
path.addQuadCurve(to: CGPoint(x: self.frame.size.width, y: cornerRadius),
controlPoint: CGPoint(x: self.frame.size.width, y: 0))
//bottom right point
path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height - shapeOffset - cornerRadius))
//bottom right corner
path.addQuadCurve(to: CGPoint(x: self.frame.size.width - cornerRadius, y: self.frame.size.height - shapeOffset),
controlPoint: CGPoint(x: self.frame.size.width, y: self.frame.size.height - shapeOffset))
//bottom left
path.addLine(to: CGPoint(x: cornerRadius, y: self.frame.size.height))
//bottom left corner
path.addQuadCurve(to: CGPoint(x: 0, y: self.frame.size.height - cornerRadius),
controlPoint: CGPoint(x: 0, y: self.frame.size.height))
path.close()
shapeLayer.path = path.cgPath
}
Usage:
myView.drawCustomShape()
Result:

How to curve edges of UIImageview in swift ios?

I used
let view = UIImageView(CGRect(x: 0, y: 10, width: 100, height: 100))
view.cornerRadius = 10
self.mainView.addSubview(view)
This rounds the corner I want round the edges
The standard technique is to
Define a UIBezierPath for the shape.
Make a CAShapeLayer using that path.
Define the image view’s layer’s mask to use that shape layer.
E.g.
#IBDesignable
class RoundedCornerView: UIImageView {
#IBInspectable
var cornerRadius: CGFloat = 17 { didSet { setNeedsLayout() } }
#IBInspectable
var lineWidth: CGFloat = 75 { didSet { setNeedsLayout() } }
override func layoutSubviews() {
super.layoutSubviews()
let rect = bounds.insetBy(dx: lineWidth / 2, dy: lineWidth / 2)
let path = UIBezierPath()
var point = CGPoint(x: rect.minX + cornerRadius, y: rect.minY + cornerRadius)
path.move(to: point)
var controlPoint = CGPoint(x: point.x + cornerRadius, y: rect.minY)
point = CGPoint(x: rect.midX, y: rect.minY)
path.addQuadCurve(to: point, controlPoint: controlPoint)
point = CGPoint(x: rect.maxX - cornerRadius, y: rect.minY + cornerRadius)
controlPoint = CGPoint(x: point.x - cornerRadius, y: rect.minY)
path.addQuadCurve(to: point, controlPoint: controlPoint)
controlPoint = CGPoint(x: rect.maxX, y: point.y + cornerRadius)
point = CGPoint(x: rect.maxX, y: rect.midY)
path.addQuadCurve(to: point, controlPoint: controlPoint)
point = CGPoint(x: rect.maxX - cornerRadius, y: rect.maxY - cornerRadius)
controlPoint = CGPoint(x: rect.maxX, y: point.y - cornerRadius)
path.addQuadCurve(to: point, controlPoint: controlPoint)
controlPoint = CGPoint(x: point.x - cornerRadius, y: rect.maxY)
point = CGPoint(x: rect.midX, y: rect.maxY)
path.addQuadCurve(to: point, controlPoint: controlPoint)
point = CGPoint(x: rect.minX + cornerRadius, y: rect.maxY - cornerRadius)
controlPoint = CGPoint(x: point.x + cornerRadius, y: rect.maxY)
path.addQuadCurve(to: point, controlPoint: controlPoint)
controlPoint = CGPoint(x: rect.minX, y: point.y - cornerRadius)
point = CGPoint(x: rect.minX, y: rect.midY)
path.addQuadCurve(to: point, controlPoint: controlPoint)
point = CGPoint(x: rect.minX + cornerRadius, y: rect.minY + cornerRadius)
controlPoint = CGPoint(x: rect.minX, y: point.y + cornerRadius)
path.addQuadCurve(to: point, controlPoint: controlPoint)
path.close()
let shapeLayer = CAShapeLayer()
shapeLayer.lineWidth = lineWidth
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.strokeColor = UIColor.white.cgColor
layer.mask = shapeLayer
clipsToBounds = true
}
}
Yields:
You’ll have to play around with values for cornerRadius and lineWidth for your image view’s dimensions, but hopefully this illustrates the idea. The default values I used in the code above was for a 300×300 image view.
Note, I implemented the path in layoutSubviews so that it would respond to resizing of your image view. I also make it #IBDesignable so that you could see it rendered in IB if you happened to be using storyboards.
Try to wrap image inside UIView
let image = UIImageView(CGRect(x:0,y:0,width:100,height:100))
let view = UIView(CGRect(x:0,y:10,width:100,height:100))
view.cornerRadius = 10
view.addSubiew(image)
self.mainView.addSubview(view)
Swift 4
let image = UIImageView(frame: CGRect(x:0,y:0,width:100,height:100))
let view = UIView(frame: CGRect(x:0,y:10,width:100,height:100))
view.layer.cornerRadius = 30
view.addSubview(image)
self.mainView.addSubview(view)

Circular crown with UIBezierPath

I'am trying draw a circular crown with UIBezierPath. I can draw two circle with two diferent radius:
let bezierCrown = UIBezierPath()
bezierCrown.addArc(withCenter: center,
radius: raidus1,
startAngle: 0,
endAngle: CGFloat.pi*2,
clockwise: true)
bezierCrown.move(to: CGPoint(x: center.x+distancia2, y: center.y))
bezierCrown.addArc(withCenter: center,
radius: raidus2,
startAngle: 0,
endAngle: CGFloat.pi*2,
clockwise: true)
But when I fill:
UIColor.green.setFill()
bezierCrown.fill()
Everything is filled: image.
I want only the crown to be filled.
You need to set the fillRule to evenOdd
let center = CGPoint(x: 200, y: 400)
let raidus1:CGFloat = 100
let raidus2:CGFloat = 80
let bezierCrown = UIBezierPath()
bezierCrown.addArc(withCenter: center,
radius: raidus1,
startAngle: 0,
endAngle: CGFloat.pi*2,
clockwise: true)
bezierCrown.move(to: CGPoint(x: center.x+raidus2, y: center.y))
bezierCrown.addArc(withCenter: center,
radius: raidus2,
startAngle: 0,
endAngle: CGFloat.pi*2,
clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = bezierCrown.cgPath
shapeLayer.fillColor = UIColor.blue.cgColor
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillRule = .evenOdd
view.layer.addSublayer(shapeLayer)

How to make the UIView as Like Ticket in UITablevViewCell

TicketViewTableViewCell.swift
#IBOutlet weak var ticketView: TestView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
ticketView.layer.masksToBounds = true
ticketView.layer.cornerRadius = 8.0
ticketView.circleRadius = 10
ticketView.circleY = ticketView.frame.height * 0.6
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
enter image description here
I want to get the UIView like the above image in TableViewCell.
You can use this custom view as a background view:
#IBDesignable class TicketBackground: UIView {
#IBInspectable var cornerRadius : CGFloat = 4
#IBInspectable var indentRadius : CGFloat = 10
#IBInspectable var color : UIColor = UIColor.lightGray
override func draw(_ rect: CGRect) {
drawBackground()
}
private func drawBackground()
{
self.backgroundColor = UIColor.clear
let width = self.frame.width
let height = self.frame.height
let path = UIBezierPath()
path.move(to: CGPoint(x: cornerRadius, y: 0))
path.addLine(to: CGPoint(x: width-cornerRadius, y: 0))
path.addArc(withCenter: CGPoint(x: width-cornerRadius, y: cornerRadius), radius: cornerRadius, startAngle: 3*CGFloat.pi/2, endAngle: 0, clockwise: true)
path.addLine(to: CGPoint(x: width, y: height/2-indentRadius))
path.addArc(withCenter: CGPoint(x: width, y: height/2), radius: indentRadius, startAngle: 3*CGFloat.pi/2, endAngle: CGFloat.pi/2, clockwise: false)
path.addLine(to: CGPoint(x: width, y: height-cornerRadius))
path.addArc(withCenter: CGPoint(x: width-cornerRadius, y: height-cornerRadius), radius: cornerRadius, startAngle: 0, endAngle: CGFloat.pi/2, clockwise: true)
path.addLine(to: CGPoint(x: cornerRadius, y: height))
path.addArc(withCenter: CGPoint(x: cornerRadius, y: height-cornerRadius), radius: cornerRadius, startAngle: CGFloat.pi/2, endAngle: CGFloat.pi, clockwise: true)
path.addLine(to: CGPoint(x: 0, y: height/2+indentRadius))
path.addArc(withCenter: CGPoint(x: 0, y: height/2), radius: indentRadius, startAngle: CGFloat.pi/2, endAngle: 3*CGFloat.pi/2, clockwise: false)
path.addLine(to: CGPoint(x: 0, y: cornerRadius))
path.addArc(withCenter: CGPoint(x: cornerRadius, y: cornerRadius), radius: cornerRadius, startAngle: CGFloat.pi, endAngle: 3*CGFloat.pi/2, clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = self.color.cgColor
self.layer.addSublayer(shapeLayer)
}
}

Wrong shadowPath for some cornerRadius

Found that in some edge cases the shadowPath seems to render the shadow too rounded.
I'm applying the same shadow settings to all the subviews, I expected that the UIBezierPath with the same corner radius as the view, would behave the same. But seems that when the height starts to decrease they behave different.
Any idea why or how to fix it?
Try something like that
var cornerRadius: CGFloat = 32.0
let layerHalfHeight = subView.bounds.height / 2.0
if layerHalfHeight < cornerRadius {
cornerRadius = layerHalfHeight
}
subView.layer.cornerRadius = cornerRadius
let path = UIBezierPath()
path.move(to: CGPoint(x: cornerRadius, y: 0.0))
path.addLine(to: CGPoint(x: subView.bounds.width - cornerRadius, y: 0.0))
path.addArc(withCenter: CGPoint(x: subView.bounds.width - cornerRadius, y: cornerRadius), radius: cornerRadius, startAngle: 3.0 * .pi / 2.0, endAngle: 2 * .pi, clockwise: true)
path.addLine(to: CGPoint(x: subView.bounds.width, y: subView.bounds.height - cornerRadius))
path.addArc(withCenter: CGPoint(x: subView.bounds.width - cornerRadius, y: subView.bounds.height - cornerRadius), radius: cornerRadius, startAngle: 0.0, endAngle: .pi / 2.0, clockwise: true)
path.addLine(to: CGPoint(x: cornerRadius, y: subView.bounds.height))
path.addArc(withCenter: CGPoint(x: cornerRadius, y: subView.bounds.height - cornerRadius), radius: cornerRadius, startAngle: .pi / 2.0, endAngle: .pi, clockwise: true)
path.addLine(to: CGPoint(x: 0.0, y: cornerRadius))
path.addArc(withCenter: CGPoint(x: cornerRadius, y: cornerRadius), radius: cornerRadius, startAngle: .pi, endAngle: 3.0 * .pi / 2.0, clockwise: true)
path.close()
subView.layer.shadowPath = path.cgPath
So main idea is when the height of a layer become less then default corner radius * 2 then you need to change corner radius to half of the height.
For now, I added a corrector for the UIBezierPath cornerRadius, it kind of works but feels like is not the proper solution.
var cornerRadiusFix: CGFloat = cornerRadius
if cornerRadiusFix > subView.bounds.height / 3 {
cornerRadiusFix = cornerRadiusFix * 0.8
}
subView.layer.shadowPath = UIBezierPath(roundedRect: subView.bounds, cornerRadius: cornerRadiusFix).cgPath
For shadow path to work, you would have to change the bounds in UIView.layoutSubviews of the view with the shadow.
In your case, there is no need to use shadow path. Just remove the line setting the shadow path and everything will work correctly.