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

SwiftUI中如何标记列表项为收藏并创建专属收藏列表

SwiftUI 目录收藏功能修复方案

原代码核心问题

  • 视图使用不可变数据:Program里的items是let常量,无法修改isFaved状态
  • ViewModel关联错误:绑定到ContentView的扩展,但实际使用的是Program视图,且filteredItems类型混乱(同时声明Set<Int>和[Directory])
  • 数据存储类型不匹配:Directory.id是UUID类型,原Database却存Int类型,导致无法正确匹配
  • 缺少收藏交互按钮:视图中没有添加切换收藏状态的UI元素
  • 收藏列表逻辑缺失:未实现收藏列表的过滤与展示逻辑

修正后的完整代码

1. Directory 模型

import Foundation

struct Directory: Identifiable, Codable {
    var id = UUID()
    let name: String
    var isFaved: Bool
    var children: [Directory]?
    
    init(id: UUID = UUID(), name: String, children: [Directory]? = nil, isFaved: Bool = false) {
        self.id = id
        self.name = name
        self.children = children
        self.isFaved = isFaved
    }
    
    // 递归更新子项的收藏状态
    func updatingChild(id: UUID, isFaved: Bool) -> Directory {
        if self.id == id {
            return Directory(id: self.id, name: self.name, children: self.children, isFaved: isFaved)
        }
        let updatedChildren = self.children?.map { $0.updatingChild(id: id, isFaved: isFaved) }
        return Directory(id: self.id, name: self.name, children: updatedChildren, isFaved: self.isFaved)
    }
    
    // 递归获取所有收藏项
    var allFavedItems: [Directory] {
        var result = [Directory]()
        if isFaved {
            result.append(self)
        }
        children?.forEach { result.append(contentsOf: $0.allFavedItems) }
        return result
    }
}

struct DirectoryData {
    static var items: [Directory] = [
        Directory(name:"Item 1", children: [
            Directory(name: "Item 11"),
            Directory(name:"Item 12")
        ]),
        Directory(name: "Item 2", children: [
            Directory(name: "Item 21", children: [
                Directory(name: "Item 211"),
                Directory(name: "Item 212")
            ]),
            Directory(name: "Item 22", children: [
                Directory(name: "Item 221"),
                Directory(name: "Item 222")
            ])
        ])
    ]
}

2. 数据存储工具类 Database

import Foundation

final class Database {
    private let FAV_KEY = "fav_key"
    
    func saveFavedIDs(_ ids: Set<UUID>) {
        let idStrings = ids.map { $0.uuidString }
        UserDefaults.standard.set(idStrings, forKey: FAV_KEY)
    }
    
    func loadFavedIDs() -> Set<UUID> {
        guard let idStrings = UserDefaults.standard.array(forKey: FAV_KEY) as? [String] else {
            return []
        }
        return Set(idStrings.compactMap { UUID(uuidString: $0) })
    }
}

3. 视图模型 ViewModel

import Foundation

class DirectoryViewModel: ObservableObject {
    @Published var items: [Directory]
    @Published var showingFavs = false
    private let database = Database()
    private var favedIDs: Set<UUID>
    
    init() {
        self.favedIDs = database.loadFavedIDs()
        // 初始化时同步本地存储的收藏状态到数据
        self.items = DirectoryData.items.map { item in
            self.updateItemFavState(item)
        }
    }
    
    // 更新单个项的收藏状态
    private func updateItemFavState(_ item: Directory) -> Directory {
        let isFaved = favedIDs.contains(item.id)
        let updatedChildren = item.children?.map { updateItemFavState($0) }
        return Directory(id: item.id, name: item.name, children: updatedChildren, isFaved: isFaved)
    }
    
    // 切换收藏状态
    func toggleFav(for itemID: UUID) {
        if favedIDs.contains(itemID) {
            favedIDs.remove(itemID)
        } else {
            favedIDs.insert(itemID)
        }
        // 递归更新数据
        items = items.map { $0.updatingChild(id: itemID, isFaved: favedIDs.contains(itemID)) }
        // 保存到本地
        database.saveFavedIDs(favedIDs)
    }
    
    // 获取所有收藏项
    var favedItems: [Directory] {
        items.flatMap { $0.allFavedItems }
    }
}

4. 主视图 Program

import SwiftUI

struct Program: View {
    @StateObject private var viewModel = DirectoryViewModel()
    
    var body: some View {
        NavigationStack {
            List {
                if viewModel.showingFavs {
                    Section(header: Text("收藏列表")) {
                        ForEach(viewModel.favedItems) { item in
                            NavigationLink(destination: Text("\(item.name) 详情")) {
                                HStack {
                                    Text(item.name)
                                    Spacer()
                                    Image(systemName: "star.fill")
                                        .foregroundColor(.yellow)
                                }
                            }
                        }
                    }
                } else {
                    Section(header: Text("全部目录")) {
                        ForEach($viewModel.items) { $item in
                            DisclosureGroup(item.name) {
                                OutlineGroup(item.children ?? [], children: \.children) { child in
                                    NavigationLink(destination: Text("\(child.name) 详情")) {
                                        HStack {
                                            Text(child.name)
                                            Spacer()
                                            Button(action: {
                                                viewModel.toggleFav(for: child.id)
                                            }) {
                                                Image(systemName: child.isFaved ? "star.fill" : "star")
                                                    .foregroundColor(child.isFaved ? .yellow : .gray)
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            .listStyle(InsetGroupedListStyle())
            .navigationTitle(viewModel.showingFavs ? "我的收藏" : "目录")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: {
                        viewModel.showingFavs.toggle()
                    }) {
                        Image(systemName: viewModel.showingFavs ? "list.bullet" : "star.fill")
                    }
                }
            }
        }
    }
}

struct Program_Previews: PreviewProvider {
    static var previews: some View {
        Program()
    }
}

功能说明

  • 收藏切换:每个子项右侧有星星按钮,点击可切换收藏状态,状态会同步保存到UserDefaults
  • 收藏列表:点击导航栏右侧按钮可切换到收藏列表,展示所有标记为收藏的项(包括父项和子项)
  • 状态持久化:重启App后收藏状态不会丢失
  • 递归处理:支持多层嵌套目录的收藏状态更新与遍历

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 11:11:01