iOS开发

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 实现。让我为您详细解释一下:

  1. 首先定义了一个 ContentView 结构体,其中包含了时钟的数字、图标以及时分针的状态信息。
  2. body 属性描述了视图的外观和布局。整体布局采用 ZStack,在屏幕上叠放多个视图。
  3. clockCase 方法定义了时钟的外壳,采用多层圆形的设计,带有阴影效果。
  4. clockHands 方法定义了时钟的时针和分针,通过旋转不同角度来表示时间的流逝。
  5. RadialLayout 结构体实现了自定义的布局方式,将子视图按照半径和角度进行排列。
  6. body 方法中,使用 RadialLayout 分别排列了时钟的数字和图标,以及时钟的刻度。
  7. onAppear 中初始化了时钟的时分针位置。
  8. 最后,定义了 ContentView_Previews 结构体用于预览视图。

这段代码展示了 SwiftUI 中如何使用 ZStack、Circle、RoundedRectangle 等视图来构建一个简单的时钟界面,并通过自定义布局方式 RadialLayout 实现了时钟数字和图标的排列。

上一篇 下一篇

猜你喜欢

热点阅读