Last active
February 29, 2024 07:47
-
-
Save ken-itakura/8939d79aad062999b062752ae6b38e09 to your computer and use it in GitHub Desktop.
Drawing an arrow for SwiftUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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