SwiftUI调用mutating func更新View实现模拟时钟秒针自动刷新
SwiftUI 采用声明式渲染范式,不需要直接调用视图实例方法修改属性,所有视图更新都由数据状态变化驱动,你的问题核心是状态链路没有打通,修改方案如下:
1. 修正 ClockHand 视图定义
@State 仅适合视图内部私有状态,这里秒针角度是外部传入的渲染参数,直接用普通 let 即可,不需要自定义更新方法:
struct ClockHand: View { let type: ClockHandType let angle: Double // 去掉@State,改为外部传入的常量 var x: CGFloat var y: CGFloat var length: CGFloat var size: CGFloat var body: some View { RoundedRectangle(cornerRadius: 5) .fill(type.getColor()) .frame(width: size*type.getWidth(), height: length ) .position(x: x, y: y) .offset(y: -length/2) .rotationEffect(Angle.degrees(angle)) } // 删掉多余的mutating update方法 }
2. 完善 ViewModel 逻辑
首先修正代码里的两处错误:原 initClock 方法名拼写和 activeWorldClock 属性缺失,再把定时器逻辑移到 ViewModel 内部,每秒触发状态更新通知:
class ClockViewModel: ObservableObject { @Published private var model: ClockModel // 内部维护定时器 private var timer: Timer? init(){ model = ClockViewModel.initClock() // 修正原拼写错误initClocks -> initClock // 启动定时器每秒触发更新 timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in // 手动发送更新通知,触发视图重新读取计算属性activeClock self?.objectWillChange.send() } } static func initClock()->ClockModel { let places = [ "Europe/Bern", ] return ClockModel(number: places.count){ index in places[index] } } var activeClock: TransformedClock { TransformedClock(clock: model.activeWorldClock!) } struct TransformedClock:Identifiable { var id:Int var secondAngle:Double var clock:ClockModel.WorldClock init(clock:ClockModel.WorldClock){ let second = clock.calendar.component(.second, from: Date()) self.clock = clock self.id = clock.id self.secondAngle = Double(second)/60.0*360 } } }
同时在 ClockModel 中补上缺失的当前激活时钟属性:
struct ClockModel{ var worldClocks: [WorldClock] // 补上缺失的activeWorldClock属性 var activeWorldClock: WorldClock? { worldClocks.first } // 其余原有代码保持不变 init(number: Int, timeFactory: (Int)->String){ worldClocks = [WorldClock]() for index in 0..<number{ let place = timeFactory(index) var calendar = Calendar.current worldClocks.append( WorldClock( id: index, calendar: calendar, place: place // 你原代码里的formatPlaceString如果有实现就保留 ) ) } } struct WorldClock:Identifiable { let id: Int // 补上id的属性声明 var calendar: Calendar var place: String } }
3. 修正 ClockView 状态绑定
给 viewModel 加上 @ObservedObject 包装器,才能响应 ViewModel 的状态更新通知,同时删掉 View 内部的定时器,直接用 ViewModel 提供的最新数据渲染:
struct ClockView: View { @ObservedObject var viewModel: ClockViewModel // 加上@ObservedObject var size: CGFloat // 删掉内部的timer定义,由ViewModel维护更新 var body: some View { let clock = viewModel.activeClock // 每次视图刷新时自动拉取最新的时钟数据 Circle() .fill(.white) .frame(width: size, height: size) .overlay{ GeometryReader{ geometry in let radius = geometry.size.width/2 let midX = geometry.safeAreaInsets.top + radius let midY = geometry.safeAreaInsets.leading + radius ClockFace(size: size, radius: radius, x: midX, y: midY) ClockHand( type: .second, angle: clock.secondAngle, // 传入最新的角度自动触发重绘 x: midX, y: midY, length: radius-size*0.04, size: size ) } } } }
修改完成后,每秒 ViewModel 会触发状态更新,ClockView 会自动拉取最新的秒针角度传入 ClockHand,系统会自动完成重绘,无需手动操作视图实例。
内容的提问来源于stack exchange,提问作者Remo
相关产品推荐
相关产品推荐

