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
相关产品推荐
相关产品推荐

