Skip to content

Instantly share code, notes, and snippets.

@UnderscoreDavidSmith
Created November 6, 2023 11:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save UnderscoreDavidSmith/aba05adb41a95d0bce1bf74bc3e7ca7e to your computer and use it in GitHub Desktop.
Save UnderscoreDavidSmith/aba05adb41a95d0bce1bf74bc3e7ca7e to your computer and use it in GitHub Desktop.
//
// ContentView.swift
// ClockRotationBug
//
// Created by David Smith on 11/6/23.
//
import SwiftUI
struct ContentView: View {
@State var date:Date = Date.dateOn(year: 2023, month: 12, day: 31, hour: 23, minute: 58, second: 0)
var body: some View {
VStack {
rotationBugEntryView(date: date)
.frame(width:200, height:200)
HStack {
Button(action: {
withAnimation {
self.date = date.addingTimeInterval(-60)
}
}, label: {
Text("Last")
})
Button(action: {
withAnimation {
self.date = date.addingTimeInterval(60)
}
}, label: {
Text("Next")
})
}
}
}
}
#Preview {
ContentView()
}
struct rotationBugEntryView : View {
var date: Date
var hourAngle:Angle {
let anchor = Date.dateOn(year: 2023, month: 1, day: 1, hour: 0, minute: 0, second: 0)
let startOfDay = Calendar.current.startOfDay(for: date)
let daysBetween = Calendar.current.dateComponents([.day], from: anchor, to: startOfDay)
guard let numberOfDays = daysBetween.day else {
return .zero
}
let dayStartAngle = Double(numberOfDays) * (360.0 * 2.0)
let components = Calendar.current.dateComponents ([.hour, .minute], from: date)
if let hour = components.hour, let minute = components.minute {
let percentHourOfDay = ((Double(hour) + Double(minute) / 60.0)) / 24.0
let hourAngleInDay = (percentHourOfDay * (360.0 * 2.0))
return Angle.degrees(dayStartAngle + hourAngleInDay )
}
return .zero
}
var minuteAngle:Angle {
let anchor = Date.dateOn(year: 2023, month: 1, day: 1, hour: 0, minute: 0, second: 0)
let startOfDay = Calendar.current.startOfDay(for: date)
let daysBetween = Calendar.current.dateComponents([.day], from: anchor, to: startOfDay)
guard let numberOfDays = daysBetween.day else {
return .zero
}
let dayStartAngle = Double(numberOfDays) * (360.0 * 24.0)
let components = Calendar.current.dateComponents ([.hour, .minute], from: date)
if let hour = components.hour, let minute = components.minute {
let percentMinuteInDay = Double(hour * 60 + minute) / (24.0 * 60.0)
let minuteAngleInDay = percentMinuteInDay * (360.0 * 24.0)
return Angle.degrees(dayStartAngle + minuteAngleInDay)
}
return Angle.zero
}
var body: some View {
VStack {
GeometryReader { proxy in
ZStack {
Circle()
.fill(Color.gray)
ForEach(0..<12) { hour in
VStack(spacing:2) {
Rectangle()
.fill(.white)
.frame(width: 2, height: 5)
Text("\(hour == 0 ? 12 : hour)")
.font(.system(size: 8))
.foregroundStyle(.white)
.rotationEffect(Angle(degrees:-360.0 * ( Double(hour) / 12.0)))
.frame(width: 10, height: 5)
Spacer()
}
.rotationEffect(Angle(degrees:360.0 * ( Double(hour) / 12.0)))
}
ClockHand(lengthPercent: 1.0)
.stroke(.black, style: StrokeStyle(lineWidth: 7, lineCap: .round))
.shadow(radius: 1)
.rotationEffect(minuteAngle)
ClockHand(lengthPercent: 0.7)
.stroke(.white, style: StrokeStyle(lineWidth: 7, lineCap: .round))
.shadow(radius: 1)
.rotationEffect(hourAngle)
}
}
Text(date, style: .time)
Text(formatDate())
}
}
func formatDate() -> String {
let formatter = DateFormatter()
formatter.timeStyle = .none
formatter.dateStyle = .medium
return formatter.string(from: date)
}
}
struct ClockHand:Shape {
let lengthPercent:Double
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.midX, y: rect.midY))
path.addLine(to: CGPoint(x: rect.midX, y: 3.5 + rect.height * (0.5 * (1.0 - lengthPercent))))
return path
}
}
extension Date {
static func dateOn(year:Int, month:Int, day:Int, hour:Int, minute:Int, second:Int) -> Date {
var components = DateComponents()
components.year = year
components.month = month
components.day = day
components.minute = minute
components.hour = hour
components.second = second
return Calendar.current.date(from: components)!
}
}
extension Date {
static func dateSince(days:Int, hour:Int, minute:Int, second:Int) -> Date {
let anchor = Date.dateOn(year: 2023, month: 1, day: 1, hour: 0, minute: 0, second: 0)
var sum:Double = 0.0
sum += Double(days) * (24.0 * 60 * 60)
sum += Double(hour) * (60 * 60)
sum += Double(minute) * (60)
sum += Double(second)
return anchor.addingTimeInterval(sum)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment