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

VisionOS Swift TodoList应用多列表无法选中问题求助

问题:VisionOS TodoList应用新增列表无法选中

我用Swift开发VisionOS的简易待办应用,创建第一个列表时能正常命名、选中并添加待办项,但新增更多列表后,这些新列表没法选中。原本以为Button(list.title) { selectedTodoList = list }能实现选中功能,怀疑是子视图TodoListView干扰了功能。

相关代码

ContentView

import SwiftUI

struct ContentView: View {
    @State private var TodoLists: [TodoList] = []
    @State private var selectedTodoList: TodoList? = nil
    @State private var showAddListAlert: Bool = false
    @State private var newListTitle: String = ""
    @State private var nextListID: Int = 0
    
    var body: some View {
        NavigationSplitView {
            List(TodoLists) { list in
                Button(list.title) {
                    selectedTodoList = list
                }
            }
            .navigationTitle("Todo lists")
            .toolbar {
                Button("Add") {
                    showAddListAlert.toggle()
                }
            }
            .alert("Add Todo List", isPresented: $showAddListAlert) {
                TextField("Todo List Title", text: $newListTitle)
                Button("Cancel", role: .cancel, action: {})
                Button("Create") {
                    let list = TodoList(title: newListTitle.isEmpty ? "List \(nextListID)" : newListTitle, items: [], id: UUID())
                    TodoLists.append(list)
                    nextListID += 1
                }
            }
        } detail: {
            if let selectedTodoList {
                TodoListView(list: selectedTodoList)
            }
        }
    }
}

TodoListView

import SwiftUI

struct TodoListView: View {
    @State var list: TodoList
    @State private var showAddTodoAlert: Bool = false
    @State private var newTodoTitle: String = ""
    
    var body: some View {
        List(list.items) { item in
            Text(item.title)
        }
        .navigationTitle("Details for \(list.title)")
        .toolbar {
          Button("Add Todo") {
            showAddTodoAlert.toggle()
          }
        }
        .alert("Add Todo", isPresented: $showAddTodoAlert) {
            TextField("Todo Title", text: $newTodoTitle)
            Button("Cancel", role: .cancel, action: {})
            Button("Create") {
                let todo = TodoItem(title: newTodoTitle, isDone: false)
                list.items.append(todo)
            }
        }
        .id(list.id)
    }
}

问题原因与修复方案

核心问题

  1. 值传递导致状态脱节:TodoListView用@State var list: TodoList是值传递,子视图修改的是原列表对象的副本,父视图的TodoLists数组里的原数据没更新。而selectedTodoList指向的是父视图里的原对象,这种数据不一致会导致SwiftUI视图更新异常,进而影响列表选中的响应。
  2. 缺少绑定同步机制:子视图修改列表后没通知父视图更新数据源,导致列表选中逻辑依赖的数据状态过时。

修复步骤

1. 子视图改用绑定传递数据

把TodoListView的@State var list: TodoList改成@Binding var list: TodoList,让子视图的修改直接同步到父视图:

struct TodoListView: View {
    @Binding var list: TodoList // 替换为Binding
    @State private var showAddTodoAlert: Bool = false
    @State private var newTodoTitle: String = ""
    
    var body: some View {
        List(list.items) { item in
            Text(item.title)
        }
        .navigationTitle("Details for \(list.title)")
        .toolbar {
          Button("Add Todo") {
            showAddTodoAlert.toggle()
          }
        }
        .alert("Add Todo", isPresented: $showAddTodoAlert) {
            TextField("Todo Title", text: $newTodoTitle)
            Button("Cancel", role: .cancel, action: {})
            Button("Create") {
                let todo = TodoItem(title: newTodoTitle, isDone: false)
                list.items.append(todo)
            }
        }
        .id(list.id)
    }
}

2. 父视图传递绑定给子视图

在ContentView的detail区域,通过数组索引传递绑定,确保子视图操作的是父视图数组里的原对象:

detail: {
    if let selectedIndex = TodoLists.firstIndex(where: { $0.id == selectedTodoList?.id }) {
        TodoListView(list: $TodoLists[selectedIndex])
    } else {
        Text("Select a todo list")
    }
}

3. 优化选中逻辑(推荐)

把selectedTodoList改成存储选中列表的ID而非整个对象,避免对象引用带来的状态问题:

// 替换原selectedTodoList为selectedListID
@State private var selectedListID: UUID? = nil

// 列表按钮的选中逻辑修改为:
Button(list.title) {
    selectedListID = list.id
}

// 对应detail区域修改:
detail: {
    if let selectedID = selectedListID, let index = TodoLists.firstIndex(where: { $0.id == selectedID }) {
        TodoListView(list: $TodoLists[index])
    } else {
        Text("Select a todo list")
    }
}

额外说明

  • @Binding是SwiftUI中实现父子视图数据同步的核心方式,能确保数据修改在整个视图链中保持一致。
  • 存储选中ID而非对象实例,能避免因对象复制、引用不一致导致的视图更新异常,在动态数组场景下更稳定可靠。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 11:23:18