Skip to content

Instantly share code, notes, and snippets.

@ken-itakura
Last active February 29, 2024 07:47
Show Gist options
  • Save ken-itakura/8939d79aad062999b062752ae6b38e09 to your computer and use it in GitHub Desktop.
Save ken-itakura/8939d79aad062999b062752ae6b38e09 to your computer and use it in GitHub Desktop.
Drawing an arrow for SwiftUI
//
// Arrow.swift
//
// Created by Ken Itakura on 2024/02/29.
//
import SwiftUI
struct Arrow: View {
var start: CGPoint
var end: CGPoint
var color: Color? = Color.black
var tipColor: Color? // arrow tip not filled, if nil
var lineWidth: CGFloat = 2.0
var tipHeight: CGFloat = 20.0
var tipWidth: CGFloat = 20.0
var body: some View{
let s = (x: Double(start.x), y: Double(start.y))
let e = (x: Double(end.x), y: Double(end.y))
let th = Double(tipHeight)
let tw = Double(tipWidth)
let vlen = sqrt(pow(e.x - s.x, 2) + pow(e.y - s.y, 2))
let eShorten = (x: e.x - lineWidth * (e.x - s.x) / vlen,
y: e.y - lineWidth * (e.y - s.y) / vlen)
let tipBase = (x: e.x - th * (e.x - s.x) / vlen,
y: e.y - th * (e.y - s.y) / vlen)
let vTip = (x: (s.y - e.y)/vlen, y: (e.x - s.x)/vlen)
let tip1vert = (x: tipBase.x + tw * vTip.x, y: tipBase.y + tw * vTip.y)
let tip2vert = (x: tipBase.x - tw * vTip.x, y: tipBase.y - tw * vTip.y)
ZStack{
Path{ path in
path.move(to: CGPoint(x: CGFloat(s.x), y: CGFloat(s.y)))
if(tipColor == nil)
{
path.addLine(to: CGPoint(x: CGFloat(e.x), y: CGFloat(e.y)))
}
else{
// Prevents the end of the line from protruding from the tip
path.addLine(to: CGPoint(x: CGFloat(eShorten.x), y: CGFloat(eShorten.y)))
}
}
.stroke(color!, lineWidth: lineWidth)
if let tipColor = tipColor{
Path { path in
path.move(to: CGPoint(x: CGFloat(e.x), y: CGFloat(e.y)))
path.addLine(to: CGPoint(x: CGFloat(tip1vert.x), y: CGFloat(tip1vert.y)))
path.addLine(to: CGPoint(x: CGFloat(tip2vert.x), y: CGFloat(tip2vert.y)))
path.closeSubpath()
}.fill(tipColor)
}
else{
Path { path in
path.move(to: CGPoint(x: CGFloat(e.x), y: CGFloat(e.y)))
path.addLine(to: CGPoint(x: CGFloat(tip1vert.x), y: CGFloat(tip1vert.y)))
path.move(to: CGPoint(x: CGFloat(e.x), y: CGFloat(e.y)))
path.addLine(to: CGPoint(x: CGFloat(tip2vert.x), y: CGFloat(tip2vert.y)))
}
.stroke(color!, lineWidth: lineWidth)
}
}
}
}
struct Arrow_Previews: PreviewProvider {
static var previews: some View {
let colors = [Color.red, Color.blue, Color.green, Color.yellow]
ForEach(0..<5) { index in
Arrow(
start: CGPoint(x: Double.random(in: 50...200), y: Double.random(in: 50...200)),
end: CGPoint(x: Double.random(in: 50...200), y: Double.random(in: 50...200)),
color: colors.randomElement(),
tipColor: colors.randomElement(),
lineWidth: Double.random(in: 1...5),
tipHeight: Double.random(in: 10...30),
tipWidth: Double.random(in: 10...30)
)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment