Dactyloidae/mobile/ios/Client/Frontend/Widgets/ChevronView.swift
2026-06-26 21:04:09 -07:00

123 lines
4.3 KiB
Swift

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
enum ChevronDirection {
case left
case up
case right
case down
}
enum ChevronStyle {
case angular
case rounded
}
class ChevronView: UIView {
fileprivate let Padding: CGFloat = 2.5
fileprivate var direction = ChevronDirection.right
fileprivate var lineCapStyle = CGLineCap.round
fileprivate var lineJoinStyle = CGLineJoin.round
var lineWidth: CGFloat = 3.0
var style: ChevronStyle = .rounded {
didSet {
switch style {
case .rounded:
lineCapStyle = CGLineCap.round
lineJoinStyle = CGLineJoin.round
case .angular:
lineCapStyle = CGLineCap.butt
lineJoinStyle = CGLineJoin.miter
}
}
}
init(direction: ChevronDirection) {
super.init(frame: CGRect.zero)
self.direction = direction
if UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft {
if direction == .left {
self.direction = .right
} else if direction == .right {
self.direction = .left
}
}
self.backgroundColor = UIColor.clear
self.contentMode = UIViewContentMode.redraw
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
super.draw(rect)
let strokeLength = (rect.size.height / 2) - Padding
let path: UIBezierPath
switch direction {
case .left:
path = drawLeftChevronAt(CGPoint(x: rect.size.width - (strokeLength + Padding), y: strokeLength + Padding), strokeLength: strokeLength)
case .up:
path = drawUpChevronAt(CGPoint(x: (rect.size.width - Padding) - strokeLength, y: (strokeLength / 2) + Padding), strokeLength: strokeLength)
case .right:
path = drawRightChevronAt(CGPoint(x: rect.size.width - Padding, y: strokeLength + Padding), strokeLength: strokeLength)
case .down:
path = drawDownChevronAt(CGPoint(x: (rect.size.width - Padding) - strokeLength, y: (strokeLength * 1.5) + Padding), strokeLength: strokeLength)
}
tintColor.set()
// The line thickness needs to be proportional to the distance from the arrow head to the tips. Making it half seems about right.
path.lineCapStyle = lineCapStyle
path.lineJoinStyle = lineJoinStyle
path.lineWidth = lineWidth
path.stroke()
}
fileprivate func drawUpChevronAt(_ origin: CGPoint, strokeLength: CGFloat) -> UIBezierPath {
return drawChevron(CGPoint(x: origin.x-strokeLength, y: origin.y+strokeLength),
head: CGPoint(x: origin.x, y: origin.y),
rightTip: CGPoint(x: origin.x+strokeLength, y: origin.y+strokeLength))
}
fileprivate func drawDownChevronAt(_ origin: CGPoint, strokeLength: CGFloat) -> UIBezierPath {
return drawChevron(CGPoint(x: origin.x-strokeLength, y: origin.y-strokeLength),
head: CGPoint(x: origin.x, y: origin.y),
rightTip: CGPoint(x: origin.x+strokeLength, y: origin.y-strokeLength))
}
fileprivate func drawLeftChevronAt(_ origin: CGPoint, strokeLength: CGFloat) -> UIBezierPath {
return drawChevron(CGPoint(x: origin.x+strokeLength, y: origin.y-strokeLength),
head: CGPoint(x: origin.x, y: origin.y),
rightTip: CGPoint(x: origin.x+strokeLength, y: origin.y+strokeLength))
}
fileprivate func drawRightChevronAt(_ origin: CGPoint, strokeLength: CGFloat) -> UIBezierPath {
return drawChevron(CGPoint(x: origin.x-strokeLength, y: origin.y+strokeLength),
head: CGPoint(x: origin.x, y: origin.y),
rightTip: CGPoint(x: origin.x-strokeLength, y: origin.y-strokeLength))
}
fileprivate func drawChevron(_ leftTip: CGPoint, head: CGPoint, rightTip: CGPoint) -> UIBezierPath {
let path = UIBezierPath()
// Left tip
path.move(to: leftTip)
// Arrow head
path.addLine(to: head)
// Right tip
path.addLine(to: rightTip)
return path
}
}