SwiftUI 绘制时钟
2024-06-28 本文已影响0人
_浅墨_
最终效果:
源码:
import SwiftUI
struct ContentView: View {
var numbers = [12,1,2,3,4,5,6,7,8,9,10,11]
var icons = ["calendar", "message", "figure.walk", "music.note"]
@State var hour: Double = 0
@State var minute: Double = 0
var body: some View {
ZStack {
Rectangle()
.fill(.gray.gradient)
.ignoresSafeArea()
clockCase
RadialLayout {
ForEach(numbers, id: \.self) { item in
Text("\(item)")
.font(.system(.title, design: .rounded))
.bold()
.foregroundColor(.black)
}
}
.frame(width: 240)
RadialLayout {
ForEach(icons, id: \.self) { item in
Circle()
.foregroundColor(.black)
.frame(width: 44)
.overlay(Image(systemName: item)
.foregroundColor(.white))
}
}
.frame(width: 120)
Circle()
.strokeBorder(.black, style: StrokeStyle(lineWidth: 10, dash: [1, 10]))
.frame(width: 220)
RadialLayout {
ForEach(numbers, id: \.self) { item in
Text("\(item * 5)")
.font(.caption)
.foregroundColor(.black)
}
}
.frame(width: 360)
clockHands
}
.onAppear {
hour = 360
minute = 360
}
}
var clockHands: some View {
ZStack {
RoundedRectangle(cornerRadius: 4)
.foregroundStyle(.black)
.frame(width: 8, height: 70)
.overlay(RoundedRectangle(cornerRadius: 4).stroke(lineWidth: 1).fill(.white))
.offset(y: -32)
.rotationEffect(.degrees(hour), anchor: .center)
.shadow(radius: 5, y: 5)
.animation(.linear(duration: 120), value: hour)
RoundedRectangle(cornerRadius: 4)
.foregroundStyle(.black)
.frame(width: 8, height: 100)
.overlay(RoundedRectangle(cornerRadius: 4).stroke(lineWidth: 1).fill(.white))
.offset(y: -46)
.rotationEffect(.degrees(minute), anchor: .center)
.shadow(radius: 5, y: 5)
.animation(.linear(duration: 10).repeatCount(12, autoreverses: false), value: minute)
Circle()
.fill(.white)
.frame(width: 3)
}
}
var clockCase: some View {
ZStack {
Circle()
.foregroundStyle(
.gray
.shadow(.inner(color: .gray, radius: 30, x: 30, y: 30))
.shadow(.inner(color: .white.opacity(0.2), radius: 0, x: -1, y: -1))
.shadow(.inner(color: .black.opacity(0.2), radius: 0, x: 1, y: 1))
)
.frame(width: 360)
Circle()
.foregroundStyle(
.white
.shadow(.inner(color: .gray, radius: 30, x: -30, y: -30))
.shadow(.drop(color: .black.opacity(0.3), radius: 30, x: 30, y: 30))
)
.frame(width: 320)
Circle()
.foregroundStyle(.white.shadow(.inner(color: .gray, radius: 30, x: 30, y: 30)))
.frame(width: 280)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct RadialLayout: Layout {
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
return proposal.replacingUnspecifiedDimensions()
}
func placeSubviews(
in bounds: CGRect,
proposal: ProposedViewSize,
subviews: Subviews,
cache: inout Void
) {
let radius = min(bounds.size.width, bounds.size.height) / 3.0
let angle = Angle.degrees(360.0 / Double(subviews.count)).radians
for (index, subview) in subviews.enumerated() {
var point = CGPoint(x: 0, y: -radius)
.applying(CGAffineTransform(
rotationAngle: angle * Double(index)))
point.x += bounds.midX
point.y += bounds.midY
subview.place(at: point, anchor: .center, proposal: .unspecified)
}
}
}
代码注释:
这段代码是一个简单的时钟应用的 SwiftUI 实现。让我为您详细解释一下:
- 首先定义了一个
ContentView
结构体,其中包含了时钟的数字、图标以及时分针的状态信息。 -
body
属性描述了视图的外观和布局。整体布局采用ZStack
,在屏幕上叠放多个视图。 -
clockCase
方法定义了时钟的外壳,采用多层圆形的设计,带有阴影效果。 -
clockHands
方法定义了时钟的时针和分针,通过旋转不同角度来表示时间的流逝。 -
RadialLayout
结构体实现了自定义的布局方式,将子视图按照半径和角度进行排列。 - 在
body
方法中,使用RadialLayout
分别排列了时钟的数字和图标,以及时钟的刻度。 - 在
onAppear
中初始化了时钟的时分针位置。 - 最后,定义了
ContentView_Previews
结构体用于预览视图。
这段代码展示了 SwiftUI 中如何使用 ZStack、Circle、RoundedRectangle 等视图来构建一个简单的时钟界面,并通过自定义布局方式 RadialLayout
实现了时钟数字和图标的排列。