You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过系统通知重新渲染SwiftUI视图(无需冗余状态)

问题:SwiftUI视图如何通过系统通知触发重渲染,避免冗余占位状态

我有一个SwiftUI视图,布局完全依赖计算属性,部分属性会随时间自然变化。当这些属性变化到特定程度时,我能收到系统通知,希望此时触发视图重渲染,但不想为了强制刷新而维护无意义的占位状态。

举个例子,如下视图仅显示当前日期:

struct TodayView: View {
    var body: some View {
        Text(Date.now.formatted(date: .complete, time: .omitted))
    }
}

如果用户停留在该界面过午夜,视图不会自动刷新显示新日期,但系统会发送.NSCalendarDayChanged通知(我清楚无法保证精准触发,接收该通知已满足需求)。

直接使用.onReceive无法触发重渲染:

struct TodayView: View {
    var body: some View {
        Text(Date.now.formatted(date: .complete, time: .omitted))
            .onReceive(NotificationCenter.default.publisher(for: .NSCalendarDayChanged)) { _ in }
    }
}

虽然可以用一个未被读取的占位@State触发刷新,但这种冗余状态既占用内存,也会让代码可读性变差:

struct TodayView: View {
    @State private var dummy = false

    var body: some View {
        Text(Date.now.formatted(date: .complete, time: .omitted))
            .onReceive(NotificationCenter.default.publisher(for: .NSCalendarDayChanged)) { _ in
                dummy.toggle()
            }
    }
}

希望找到无需冗余状态、直接通过通知触发重渲染的方案。


解决方案

方案1:使用空ObservableObject作为刷新触发器

创建一个仅负责发送刷新信号的ObservableObject,通过调用其objectWillChange方法触发视图更新,逻辑清晰且无冗余状态:

struct TodayView: View {
    // 使用@StateObject持有触发器,确保生命周期与视图一致
    @StateObject private var refreshTrigger = RefreshTrigger()
    
    var body: some View {
        Text(Date.now.formatted(date: .complete, time: .omitted))
            .onReceive(NotificationCenter.default.publisher(for: .NSCalendarDayChanged)) { _ in
                // 收到通知时触发刷新信号
                refreshTrigger.triggerRefresh()
            }
    }
}

// 仅负责发送刷新通知的空ObservableObject
private class RefreshTrigger: ObservableObject {
    func triggerRefresh() {
        objectWillChange.send()
    }
}

方案2:使用空值类型作为刷新令牌(iOS 13+)

定义一个空的Equatable结构体,通过更新其@State实例触发视图刷新。空结构体内存占用极低,且命名明确:

struct TodayView: View {
    @State private var refreshToken = RefreshToken()
    
    var body: some View {
        Text(Date.now.formatted(date: .complete, time: .omitted))
            .onReceive(NotificationCenter.default.publisher(for: .NSCalendarDayChanged)) { _ in
                // 生成新的令牌实例,触发状态更新
                refreshToken = RefreshToken()
            }
    }
}

// 空的Equatable结构体,仅作为刷新标识
private struct RefreshToken: Equatable {}

方案3:iOS 17+ 原生观察器简化实现

iOS 17及以上版本可直接使用.observation修饰符,结合简化的触发器实现:

struct TodayView: View {
    let refreshTrigger = RefreshTrigger()
    
    var body: some View {
        Text(Date.now.formatted(date: .complete, time: .omitted))
            .onReceive(NotificationCenter.default.publisher(for: .NSCalendarDayChanged)) { _ in
                refreshTrigger.triggerRefresh()
            }
            .observation(refreshTrigger)
    }
}

private class RefreshTrigger: ObservableObject {
    func triggerRefresh() {
        objectWillChange.send()
    }
}

这些方案都避免了无意义的占位状态,代码意图清晰,能在收到系统通知时精准触发视图重渲染。


内容的提问来源于stack exchange,提问作者Michael Hulet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 17:01:29