Files
360Clock/360Clock/ClockView.swift
bilal 0697440437 Добавить полноценный Widget Extension и исправить отображение циферблата в виджете.
Вынесена реализация виджета в отдельный target с корректным embedding и Info.plist, чтобы сборка и запуск работали стабильно на устройстве, а также улучшена адаптивная верстка меток и фонового контейнера для соответствия требованиям WidgetKit.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-17 18:59:07 +03:00

135 lines
6.3 KiB
Swift
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import SwiftUI
struct ClockView: View {
@State private var currentTime = Date()
@Environment(\.colorScheme) var colorScheme
let timer = Timer.publish(every: 0.001, on: .main, in: .common).autoconnect()
var body: some View {
VStack {
Text("360 Clock")
.font(.largeTitle)
.fontWeight(.bold)
.padding(.top)
Text(timeString)
.font(.title2)
.foregroundColor(.secondary)
.padding(.bottom, 10)
GeometryReader { geometry in
let size = min(geometry.size.width, geometry.size.height)
ZStack {
// Циферблат
Circle()
.stroke(colorScheme == .dark ? Color.white : Color.black, lineWidth: 2)
.frame(width: size * 0.8, height: size * 0.8)
// Метки на циферблате (каждые 15 градусов)
ForEach(0..<24, id: \.self) { index in
let angle = Double(index * 15) // 0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345
let radius = size * 0.35
// Начинаем с 0 вверху (угол -90 градусов от стандартного)
let adjustedAngle = angle - 90
let x = cos(adjustedAngle * .pi / 180) * radius
let y = sin(adjustedAngle * .pi / 180) * radius
// Показываем градусы
Text("\(index * 15)")
.font(.caption)
.fontWeight(.bold)
.foregroundColor(colorScheme == .dark ? Color.white : Color.black)
.offset(x: x, y: y)
}
// Центральная точка
Circle()
.fill(Color.red)
.frame(width: 8, height: 8)
// Секундная стрелка (60 оборотов за 1 оборот минутной стрелки)
ClockHand(angle: secondAngle, length: size * 0.35, width: 1, color: colorScheme == .dark ? Color.white : Color.black)
// Минутная стрелка (1 оборот за градус часовой стрелки)
ClockHand(angle: minuteAngle, length: size * 0.25, width: 2, color: .blue)
// Часовая стрелка (24-часовой формат)
ClockHand(angle: hourAngle, length: size * 0.2, width: 4, color: .red)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
.onReceive(timer) { input in
currentTime = input
}
}
private var timeString: String {
let formatter = DateFormatter()
formatter.timeStyle = .medium
return formatter.string(from: currentTime)
}
// Угол часовой стрелки для 24-часового формата
private var hourAngle: Double {
let calendar = Calendar.current
let hour = calendar.component(.hour, from: currentTime)
let minute = calendar.component(.minute, from: currentTime)
let second = calendar.component(.second, from: currentTime)
// 24-часовой формат: 15° = 1 час
// 0 часов = 0°, 1 час = 15°, 2 часа = 30°, ..., 24 часов = 360°
let baseHourAngle = Double(hour) * 15.0
// Добавляем плавное движение для минут и секунд
let minuteOffset = Double(minute) * 0.25 // 15° / 60 минут = 0.25° за минуту
let secondOffset = Double(second) * 0.0042 // 15° / 3600 секунд = 0.0042° за секунду
let totalAngle = baseHourAngle + minuteOffset + secondOffset
// В SwiftUI: 0° = вверх, 90° = вправо, 180° = вниз, 270° = влево
// Нам нужно: 0 часов = вверх (0°), 6 часов = вправо (90°), 12 часов = вниз (180°), 18 часов = влево (270°)
// Простая формула: totalAngle
return totalAngle
}
// Угол минутной стрелки (1 оборот за градус часовой стрелки)
private var minuteAngle: Double {
// Минутная стрелка делает 1 оборот (360°) за каждый градус поворота часовой стрелки
// hourAngle уже в градусах, поэтому просто умножаем на 360
let totalAngle = hourAngle * 360.0
// Приводим к диапазону 0-360°
return totalAngle.truncatingRemainder(dividingBy: 360.0)
}
// Угол секундной стрелки (60 оборотов за 1 оборот минутной стрелки)
private var secondAngle: Double {
// Секундная стрелка делает 60 оборотов за 1 оборот минутной стрелки
let totalAngle = minuteAngle * 60.0
// Приводим к диапазону 0-360°
return totalAngle.truncatingRemainder(dividingBy: 360.0)
}
}
struct ClockHand: View {
let angle: Double
let length: CGFloat
let width: CGFloat
let color: Color
var body: some View {
Rectangle()
.fill(color)
.frame(width: width, height: length)
.offset(y: -length/2) // Смещаем вверх, чтобы центр был в центре циферблата
.rotationEffect(.degrees(angle)) // Поворачиваем на нужный угол
}
}
struct ClockView_Previews: PreviewProvider {
static var previews: some View {
ClockView()
}
}