How to create a view like UIPopover using UIBezierPaths in iPhone? - ios

I have an app in which there are so many popover and every popover so many objects like UIButtons, UILable,UITableView.
Now I want to create view like popover using UIBezierpathswith upside arrow.
How can I do that in a UIView?
don't want to any third party tool.
Any help will be appreciated.

Check my answer from link here and just change the point values according to your need or path.
Hope it will help you.

I am late but could be helpful to someone
func drawPopoverRadius(text: String) -> UIView {
let layer = CAShapeLayer()
let path = UIBezierPath()
path.move(to: .zero)
path.addLine(to: CGPoint(x: 51, y: 0))
path.addLine(to: CGPoint(x: 51, y: 48))
path.addLine(to: CGPoint(x: 51/2 - 4, y: 48))
path.addLine(to: CGPoint(x: 51/2, y: 52))
path.addLine(to: CGPoint(x: 51/2 + 4, y: 48))
path.addLine(to: CGPoint(x: 0, y: 48))
path.close()
layer.path = path.cgPath
layer.fillColor = UIColor.gray.cgColor
layer.anchorPoint = .zero
layer.name = "popover"
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 51, height: 52))
label.clipsToBounds = false
label.numberOfLines = 0
label.font = NunitoSans.regular.font(size: 14)
label.textColor = .white
label.text = text
label.layer.addSublayer(layer)
return label
}

Related

How to create dotted step indicator in swift 5(iOS)

I have a requirement like to draw a dotted step indicator in swift 5. I have searched for lot of tutorials on the same but I couldn't get the proper result. I need the step indicator which should be in vertical position(Top to Bottom). I have used the following code but it is coming wrongly when I run the app in iPad.
private func drawLinePath() {
//let linePath = UIBezierPath()
let path = UIBezierPath()
let centerX = self.frame.width / 2.0
let lineHeight = self.frame.height / 10
path.move(to: CGPoint(x: centerX, y: 0))
path.addLine(to: CGPoint(x: centerX, y: lineHeight))
path.move(to: CGPoint(x: centerX, y:lineHeight + 3))
path.addLine(to: CGPoint(x: centerX, y: lineHeight + 5))
path.move(to: CGPoint(x: centerX, y:lineHeight + 8))
path.addLine(to: CGPoint(x: centerX, y: lineHeight + 10))
path.move(to: CGPoint(x: centerX, y:lineHeight + 12))
path.addLine(to: CGPoint(x: centerX, y: lineHeight + 15))
path.move(to: CGPoint(x: centerX, y:lineHeight + 18))
path.addLine(to: CGPoint(x: centerX, y: lineHeight + 21 ))
path.move(to: CGPoint(x: centerX, y:lineHeight + 23))
path.addLine(to: CGPoint(x: centerX, y: lineHeight + 26))
path.move(to: CGPoint(x: centerX, y:lineHeight + 28))
path.addLine(to: CGPoint(x: centerX, y: lineHeight + 31))
path.move(to: CGPoint(x: centerX, y:lineHeight + 33))
path.addLine(to: CGPoint(x: centerX, y: lineHeight + 36))
path.move(to: CGPoint(x: centerX, y:lineHeight + 38))
path.addLine(to: CGPoint(x: centerX, y: lineHeight + 41))
self.path = path.cgPath
}
here is code working perfectly.
func drawLineFromPoint(start : CGPoint, toPoint end:CGPoint, ofColor lineColor: UIColor, inView view:UIView) {
//design the path
let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)
//design path in layer
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = lineColor.cgColor
shapeLayer.lineWidth = 4.0
shapeLayer.lineCap = .round
shapeLayer.lineDashPattern = [0.001,16]
shapeLayer.lineDashPhase = 4
view.layer.addSublayer(shapeLayer)
}
func drawLine() {
let frm1: CGRect = img1.frame
let frm2 : CGRect = img2.frame
drawLineFromPoint(start: CGPoint(x: frm1.origin.x + 12, y: frm1.origin.y + 25), toPoint: CGPoint(x: frm1.origin.x + 12, y: frm2.origin.y), ofColor: UIColor.black, inView: self.view)
}
override func viewDidLoad() {
super.viewDidLoad()
drawLine()
}
here is image
For that dotted path you can take UILabel and set text as '......' with your required font size and color. No need to draw any Bezier path. Just use UILabel.
take UIView for dashed line and use below code for line.
extension UIView {
func createDashedLine(from point1: CGPoint, to point2: CGPoint, color: UIColor, strokeLength: NSNumber, gapLength: NSNumber, width: CGFloat) {
let shapeLayer = CAShapeLayer()
shapeLayer.strokeColor = color.cgColor
shapeLayer.lineWidth = width
shapeLayer.lineDashPattern = [strokeLength, gapLength]
let path = CGMutablePath()
path.addLines(between: [point1, point2])
shapeLayer.path = path
layer.addSublayer(shapeLayer)
}}
Now use it like below..
let topPoint = CGPoint(x: vw.frame.midX, y: vw.bounds.minY)
let bottomPoint = CGPoint(x: vw.frame.midX, y: vw.bounds.maxY)
vw.createDashedLine(from: topPoint, to: bottomPoint, color: .lightGray, strokeLength: 4, gapLength: 6, width: 2)
You can use StepIndicator pod for that, you can find example from this link
https://github.com/chenyun122/StepIndicator
let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize.init(width: 20, height: 40))
let layer = CAShapeLayer()
let path = UIBezierPath(roundedRect: rect, cornerRadius: 4)
layer.path = path.cgPath
layer.strokeColor = UIColor.gray.cgColor
layer.lineDashPattern = [3,3];
layer.backgroundColor = _CLEAR_COLOR.cgColor;
layer.fillColor = _CLEAR_COLOR.cgColor;
viewDottedLine.layer.addSublayer(layer);
Change rect values as per your view and lineDashPattern.
Hope that will work for you.

Custom change color of cell bottom border

I need to change color of bottom cell in UICollectionView, in this question just I do this
I need set color to bottom cell like this
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor(red: 184/255, green: 215/255, blue: 215/255, alpha: 1).cgColor
border.frame = CGRect(x: 0, y: cell.frame.size.height - width, width: cell.frame.size.width, height: cell.frame.size.height)
border.borderWidth = width
cell.layer.addSublayer(border)
cell.layer.masksToBounds = true
Instead of trying to add it as a border, I would add two layers instead as it is much easier. Something like :
override func layoutSubviews() {
super.layoutSubviews()
backgroundShape = CAShapeLayer.init()
backPath = UIBezierPath.init(rect: self.bounds)// Use your path
backgroundShape.path = backPath.cgPath
backgroundShape.fillColor = UIColor.blue.cgColor//border color in your case
self.layer.addSublayer(backgroundShape)
foregroundShape = CAShapeLayer()
forgroundPath = UIBezierPath.init(rect: CGRect.init(x: 0, y: -20, width: self.bounds.width, height: self.bounds.height))// Use your path with a little negative y
foregroundShape.path = forgroundPath.cgPath
foregroundShape.fillColor = UIColor.yellow.cgColor//white in your case
self.layer.addSublayer(foregroundShape)
}
A detailed answer:
class BorderedCell: UICollectionViewCell{
var backgroundShape: CAShapeLayer!
var backPath: UIBezierPath!
var foregroundShape: CAShapeLayer!
var forgroundPath: UIBezierPath!
override func layoutSubviews() {
super.layoutSubviews()
backgroundShape = CAShapeLayer.init()
backPath = drawCurvedShape(with: 0)
backgroundShape.path = backPath.cgPath
backgroundShape.fillColor = UIColor(red:0.76, green:0.86, blue:0.86, alpha:1.0).cgColor
self.layer.addSublayer(backgroundShape)
foregroundShape = CAShapeLayer()
forgroundPath = drawCurvedShape(with: -8)
foregroundShape.path = forgroundPath.cgPath
foregroundShape.fillColor = UIColor.white.cgColor
self.layer.addSublayer(foregroundShape)
}
func drawCurvedShape(with startingY: CGFloat) -> UIBezierPath{
let path = UIBezierPath.init()
path.move(to: CGPoint.init(x: 0, y: startingY))
path.addLine(to: CGPoint.init(x: self.bounds.width, y: startingY))
path.addLine(to: CGPoint.init(x: self.bounds.width, y: self.bounds.height + startingY - 30))
path.addQuadCurve(to: CGPoint.init(x: self.bounds.width - 30, y: self.bounds.height + startingY), controlPoint: CGPoint.init(x: self.bounds.width, y: self.bounds.height + startingY))
path.addLine(to: CGPoint.init(x: 0, y: self.bounds.height + startingY))
path.addLine(to: CGPoint.init(x: 0, y: startingY))
path.close()
return path
}
}
Output:
I did not make the drawShape exactly to the shape you require, just to something that would serve the purpose.

Dots following the path

I am looking for some simple method that will animate dots moving by closed path.
I have some path created with UIBezierPath. Which is represented by CAShapeLayer.
I want to place on this shape dots that will move around this closed path. The problem is that I couldn't find the best method. I do not want to add every dot programmatically. Maybe there is some method where could be use particles.
Path will have different shapes. The most important thing is to please dots with the same distance between each other.
I will be glad for help
Here is an example you can run in playground.
It's based on an article by Matt Long (from a while back) http://www.cimgf.com/2009/10/20/marching-ants-with-core-animation/
import UIKit
import PlaygroundSupport
let container = UIView(frame: CGRect(x: 0, y: 0, width: 600, height: 700))
container.backgroundColor = UIColor.green
let v = UIView(frame: CGRect(x: 100, y: 100, width: 300, height: 300))
v.backgroundColor = UIColor.yellow
let shp = CAShapeLayer()
shp.bounds = v.bounds
let pth = UIBezierPath()
pth.move(to: CGPoint(x: 20, y: 220))
pth.addLine(to: CGPoint(x: 20, y: 40))
pth.addLine(to: CGPoint(x: 40, y: 80))
pth.addLine(to: CGPoint(x: 120, y: 40))
pth.addLine(to: CGPoint(x: 140, y: 40))
pth.close()
shp.strokeColor = UIColor.orange.cgColor
shp.fillColor = UIColor.cyan.cgColor
shp.lineWidth = 3.0
shp.lineDashPattern = [5, 5]
shp.path = pth.cgPath
shp.position = CGPoint(x: 150, y: 150)
v.layer.addSublayer(shp)
container.addSubview(v)
let dashAnim = CABasicAnimation(keyPath: "lineDashPhase")
dashAnim.fromValue = 0
dashAnim.toValue = 15
dashAnim.duration = 0.75
dashAnim.repeatCount = 10000
shp.add(dashAnim, forKey: "lineDashPhase")
PlaygroundPage.current.liveView = container
PlaygroundPage.current.needsIndefiniteExecution = true

Remove border from UIBezierPath

I have drawn a very simple path using a UIBezierPath and a custom UIView subclass but for some reason, a border appears around the bounds of the view and I cannot seem to get rid of it or see why it was created.
I used this code:
private override func draw(_ rect: CGRect) {
let path = UIBezierPath(rect: CGRect(x: 15, y: 15, width: 300, height: 300))
path.move(to: CGPoint(x: 100, y: 100))
path.addLine(to: CGPoint(x: 150, y: 150))
path.addLine(to: CGPoint(x: 100, y: 200))
path.addLine(to: CGPoint(x: 150, y: 250))
path.lineWidth = 25.0
UIColor.darkGray.setStroke()
path.stroke()
}
and it gave this result... (the border should not be there, only the zig-zag line)
Any help please?
You're creating that rectangle by
let path = UIBezierPath(rect: CGRect(x: 15, y: 15, width: 300, height: 300))
you just need to do
let path = UIBezierPath()
You are initialising the path with a rect, which gets drawn with the stroke.
Just replace your first line with this:
let path = UIBezierPath()

How can I create arc shape ios swift

I have an application with some buttons. I want to create a arc shape button like the following image
How can I do it? I use the following code to achieve this.
button.layer.cornerRadius = button.bounds.size.height
and
button.layer.cornerRadius = 0.5*button.bounds.size.height
I also tried this with the using of width
button.layer.cornerRadius = button.bounds.size.width
and
button.layer.cornerRadius = 0.5*button.bounds.size.width
How can I do it? Please someone help me to solve this.
To create arc shape you need to use UIBezierPath
please read apple's UIBezierPath_class Document.
They have mention method for create arc shape.
Creates and returns a new UIBezierPath object initialized with an arc of a circle.
For constructing the path
Appends an arc to the receiver’s path.
You can also Refer Core Graphics Tutorial Part 1 With Example and search with arc keyword on browser you will redirect to arc shape on the page.
Another image posted by #Jack in above comments and asked help, here is the image:
I couldn't answer him as comment, so pasting my playground here instead.
let shapeView = UIView(frame: CGRect(x: 0, y: 0, width: 360, height: 60))
let shapeSize = shapeView.frame.size
shapeView.backgroundColor = UIColor.white
let path = CGMutablePath()
path.move(to: CGPoint.zero)
let curveWidthOneFourth = shapeSize.width / 4
let curveHeight = shapeSize.height * 0.2
path.addCurve(to: CGPoint(x: curveWidthOneFourth * 2, y: curveHeight), control1: .zero, control2: CGPoint(x: curveWidthOneFourth, y: curveHeight))
path.addCurve(to: CGPoint(x: shapeSize.width, y: 0), control1: CGPoint(x: curveWidthOneFourth * 3, y: curveHeight), control2: CGPoint(x: shapeSize.width, y: 0))
path.addLine(to: CGPoint(x: shapeSize.width, y: shapeSize.height))
path.addLine(to: CGPoint(x: 0, y: shapeSize.height))
path.addLine(to: CGPoint.zero)
And add our path to a view's layer:
let layer = CAShapeLayer()
layer.path = path
layer.fillColor = UIColor.red.cgColor
shapeView.layer.addSublayer(layer)
Encountered a similar task today, I used CGMutableShape instead of UIBezierPath because I was required to animation the shape.
Anyways, to understand the shape I divided the shape into 6 parts (lines & curves):
Base UIView to draw our shape in
let shapeView = UIView(frame: CGRect(x: 16, y: 16, width: 100, height: 44))
shapeView.backgroundColor = UIColor.lightGray
self.view.addSubview(shapeView)
Actual shape and its path
let path = CGMutablePath()
path.move(to: CGPoint(x: 10, y: 0)) // Move path to beginning of part-1
// Draw above divided parts
path.addLine(to: CGPoint(x: 90, y: 0)) // Part-1
path.addCurve(to: CGPoint(x: 95, y: 22), control1: CGPoint(x: 93, y: 6.6), control2: CGPoint(x: 95, y: 14)) // Part-2
path.addCurve(to: CGPoint(x: 90, y: 44), control1: CGPoint(x: 95, y: 30), control2: CGPoint(x: 93, y: 37)) // Part-3
path.addLine(to: CGPoint(x: 10, y: 44)) // Part-4
path.addCurve(to: CGPoint(x: 5, y: 22), control1: CGPoint(x: 7, y: 37), control2: CGPoint(x: 5, y: 30)) // Part-5
path.addCurve(to: CGPoint(x: 10, y: 0), control1: CGPoint(x: 5, y: 14), control2: CGPoint(x: 6, y: 6.6)) // Part-6
let arcLayer = CAShapeLayer()
arcLayer.path = path
arcLayer.fillColor = UIColor.gray.cgColor
shapeView.layer.insertSublayer(arcLayer, at: 0)

Resources