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

SwiftUI中Reminder结构体每次打开视图生成新UUID的问题

问题分析与解决方案:ExploreView中Reminder的UUID重复生成问题

问题描述

我开发了一款存储名言类提醒的App,ContentView展示提醒内容,支持用户自定义创建,也能从ExploreView添加我手动录入的提醒元素。但每次把ExploreView里的元素添加到ContentView数组时,会携带该元素的UUID,可从ContentView返回ExploreView后,所有元素的UUID都变成新的了。我尝试把ExploreReminder数组转移到外部类,问题依然存在。

核心原因

  1. Reminder的UUID自动生成逻辑:你的Reminder结构体里id是默认赋值var id = UUID(),每次创建Reminder实例都会生成新的UUID。
  2. ExploreReminders重复初始化:ExploreView里用@State var exploreReminders = ExploreReminders(),当视图重新加载(比如从详情页返回)时,会重新创建ExploreReminders实例,导致里面的所有Reminder被重新初始化,UUID全部更新。

解决方案

步骤1:修改Reminder结构体,固定预设内容的UUID

让Reminder支持两种初始化方式:自定义提醒自动生成UUID,预设名言手动传入固定UUID,避免重复生成:

struct Reminder: Codable, Identifiable, Equatable, Hashable{
    var id: UUID
    var phrase: String
    var author: String

    // 自定义提醒用:自动生成UUID
    init(phrase: String, author: String) {
        self.id = UUID()
        self.phrase = phrase.addQuotationMarks()
        self.author = author
    }

    // 预设名言用:手动传入固定UUID
    init(id: UUID, phrase: String, author: String) {
        self.id = id
        self.phrase = phrase.addQuotationMarks()
        self.author = author
    }
}

注:把phrase.addQuotationMarks()的结果直接赋值,避免在初始化里修改值类型的属性引发意外。

步骤2:将ExploreReminders改为全局单例

确保整个App里只有一个ExploreReminders实例,预设数组只初始化一次:

class ExploreReminders {
    // 全局单例
    static let shared = ExploreReminders()
    
    // 给每个预设名言分配固定UUID(自己生成替换下面的示例UUID)
    let exploreReminders: [Reminder] = [
        Reminder(id: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, phrase: "Two things are infinite: the universe and human stupidity; and I'm not sure about the universe.", author: "Albert Einstein"),
        Reminder(id: UUID(uuidString: "3D4F1A7B-8C9D-4E5F-A6B7-C8D9E0F1A2B3")!, phrase: "The only way to do great work is to love what you do.", author: "Steve Jobs"),
        Reminder(id: UUID(uuidString: "1A2B3C4D-5E6F-7A8B-9C0D-1E2F3A4B5C6D")!, phrase: "Life is what happens when you're busy making other plans.", author: "John Lennon"),
        Reminder(id: UUID(uuidString: "9F8E7D6C-5B4A-3210-FEDC-BA9876543210")!, phrase: "In three words I can sum up everything I've learned about life: it goes on.", author: "Robert Frost"),
        Reminder(id: UUID(uuidString: "A1B2C3D4-E5F6-A7B8-C9D0-E1F2A3B4C5D6")!, phrase: "You only live once, but if you do it right, once is enough.", author: "Mae West"),
        Reminder(id: UUID(uuidString: "FEDCBA98-7654-3210-FEDC-BA9876543210")!, phrase: "Yesterday is history, tomorrow is a mystery, today is a gift of God, which is why we call it the present.", author: "Bil Keane")
    ]
    
    // 私有化初始化,禁止外部创建新实例
    private init() {}
}

可以用Xcode的Editor > Insert UUID生成专属固定UUID,替换示例中的字符串。

步骤3:修改ExploreView使用单例实例

去掉@State修饰的exploreReminders,直接使用全局单例:

struct ExploreView: View {
    // 直接调用单例,无需@State
    let exploreReminders = ExploreReminders.shared
    @EnvironmentObject var allReminders: AllReminder
    @State private var userSearch = ""
    
    var searchResults: [Reminder] {
        if userSearch.isEmpty{
            return exploreReminders.exploreReminders
        } else{
            let editedUserSearch = userSearch.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
            return exploreReminders.exploreReminders.filter {
                let editedPhrase = $0.phrase.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
                let editedAuthor = $0.author.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
                return editedPhrase.contains(editedUserSearch) || editedAuthor.contains(editedUserSearch)
            }
        }
    }
    
    var body: some View {
        NavigationStack {
            ScrollView{
                LazyVGrid(columns:[
                    GridItem(.flexible(minimum: 100, maximum: 200), spacing: 20),
                    GridItem(.flexible(minimum: 100, maximum: 200), spacing: 20)
                ], spacing: 20) {
                    ForEach(searchResults) { reminder in
                        NavigationLink{
                            ReminderDetailView(reminder: reminder, allReminders: allReminders)
                        } label:{
                            ReminderView(reminder: reminder, allReminders: allReminders)
                                 .aspectRatio(1, contentMode: .fit)
                        }
                    }
                }
                .padding()
            }
            .navigationTitle("Explore")
            .searchable(text: $userSearch, placement: .navigationBarDrawer(displayMode: .always), prompt: "Search for Phrases or Authors")
        }
    }
}

额外优化:用环境变量传递AllReminder

确保AllReminder是可观察对象,并通过环境传递,避免视图间数据传递异常:

class AllReminder: ObservableObject {
    @Published var reminders: [Reminder] = []
    
    func addReminder(_ reminder: Reminder) {
        // 避免重复添加
        if !reminders.contains(where: { $0.id == reminder.id }) {
            reminders.append(reminder)
        }
    }
}

// 在App入口初始化并传递
@main
struct YourApp: App {
    @StateObject var allReminders = AllReminder()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(allReminders)
        }
    }
}

内容的提问来源于stack exchange,提问作者registrier rp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 03:14:59