【问题标题】:SwiftUI: Modify master view from detailSwiftUI:从细节修改主视图
【发布时间】:2021-04-02 23:47:47
【问题描述】:

我想使用 swiftUI 编写我的第一个 iOS 应用程序。我有很多编程经验,但都是很久以前的事了。我以为我可以快速学习 swift,但我错了。世界变了……

我有一个包含参与者列表的主视图(一个名为“activePersons”的数组),每个参与者都有一个详细视图(“EditPerson”),应该允许从列表中修改或删除参与者。为了添加新人,列表中的最后一项不是人,而是指向允许选择新人的详细视图(“NewPerson”)的符号。

目前,我已将人员列表定义为全局变量。当然,这不会导致主视图更新。

我已经尝试过使用@State 包装器,但我的所有尝试都失败了。当然,我可以在 ContentView 中将 activePersons 定义为状态变量,但是我无法从详细视图中对其进行修改。

非常感谢任何帮助!

这是我的代码(摘录):

import SwiftUI

struct Person: Identifiable {
    let id: Int
    let name: String
}

var activePersons: [Person] = [ Person(id: 1, name: "Dummy1")]
var otherPersons: [Person] = [  Person(id: 1, name: "Dummy2"),
                                Person(id: 2, name: "Dummy3"),
                                Person(id: 3, name: "Dummy4")]
...

ContentView.swift 内容如下:

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        TabView {
            NavigationView {

                List {
                    ForEach(0..<activePersons.count, id: \.self) { value in
                        NavigationLink(
                            destination: EditPerson(),
                            label: {
                                VStack (alignment: .leading) {
                                    Text(activePersons[value].name)
                                        .font(.system(size: 26, weight: .regular))
                         
                                }.padding(15)
                            })
                    }
                    NavigationLink(
                        destination: NewPerson(),
                        label: {
                            Image(systemName: "person.crop.circle.badge.plus")
                                .font(.system(size: 26, weight: .light))
                                .foregroundColor(Color(red: 0.7, green: 0.7, blue: 0.7))
                                .padding(15)
                        })
                }.environment(\.defaultMinListRowHeight, 30)
                .navigationTitle("Participants")
            }
            .tabItem{
                Image(systemName: "square.and.pencil")
                Text("Planning")
            }
...

NewPerson.swift(修改参与者列表的详细视图之一):

import SwiftUI

struct NewPerson: View {
    @State var name: String = ""
    var body: some View {
        List {
            ForEach(0..<otherPersons.count) { value in
                Button(action: {
                    activePersons.append(otherPersons[value])
                }) {
                Text(otherPersons[value].name)
                }
            }
        }.environment(\.defaultMinListRowHeight, 30)
    }
}

【问题讨论】:

    标签: swiftui


    【解决方案1】:

    一般来说,您会希望使用 Bindings 或 ObservableObjects 将状态向下传递到子视图。以下代码说明了两者:

    
    struct Person: Identifiable, Hashable {
        let id: Int
        var name: String
    }
    
    class AppState : ObservableObject {
        @Published var activeIds : [Int] = [0,1]
        @Published var persons: [Person] = [Person(id: 0, name: "Dummy1"),
                                        Person(id: 1, name: "Dummy2"),
                                        Person(id: 2, name: "Dummy3"),
                                        Person(id: 3, name: "Dummy4")]
        
        var activePersons : [Person] {
            return activeIds.compactMap { id in
                persons.first(where: { $0.id == id
                })
            }
        }
        
        var nonActivePersons : [Person] {
            persons.filter { !activeIds.contains($0.id) }
        }
        
        func bindingForId(id: Int) -> Binding<Person> {
            .init { () -> Person in
                self.persons.first(where: { $0.id == id }) ?? Person(id: -1, name: "()")
            } set: { (newValue) in
                self.persons = self.persons.map {
                    if $0.id == id {
                        return newValue
                    } else {
                        return $0
                    }
                }
            }
    
        }
    }
    
    struct ContentView: View {
        @StateObject var state = AppState()
        
        var body: some View {
            TabView {
                NavigationView {
                    List {
                        ForEach(state.activePersons, id: \.self) { person in
                            NavigationLink(
                                destination: EditPerson(person: state.bindingForId(id: person.id)),
                                label: {
                                    VStack (alignment: .leading) {
                                        Text(person.name)
                                            .font(.system(size: 26, weight: .regular))
                             
                                    }.padding(15)
                                })
                        }
                        NavigationLink(
                            destination: NewPerson(state: state),
                            label: {
                                Image(systemName: "person.crop.circle.badge.plus")
                                    .font(.system(size: 26, weight: .light))
                                    .foregroundColor(Color(red: 0.7, green: 0.7, blue: 0.7))
                                    .padding(15)
                            })
                    }.environment(\.defaultMinListRowHeight, 30)
                    .navigationTitle("Participants")
                }
                .tabItem{
                    Image(systemName: "square.and.pencil")
                    Text("Planning")
                }
            }
        }
    }
    
    struct EditPerson : View {
        @Binding var person : Person
        
        var body: some View {
            TextField("name", text: $person.name)
        }
    }
    
    struct NewPerson: View {
        @ObservedObject var state : AppState
        var body: some View {
            List {
                ForEach(state.nonActivePersons, id: \.self) { person in
                    Button(action: {
                        state.activeIds.append(person.id)
                    }) {
                        Text(person.name)
                    }
                }
            }.environment(\.defaultMinListRowHeight, 30)
        }
    }
    
    

    在本例中,我创建了一个名为 AppState 的 ObservableObject。它具有@Published 属性——@Published 将在值更改时发出要刷新的视图信号 (https://www.hackingwithswift.com/quick-start/swiftui/observable-objects-environment-objects-and-published)

    可以看到,当NewPerson被调用时,整个状态对象都作为参数传递给了它。

    对于 EditPerson 视图,我只将 Binding 传递给 Person -- 绑定允许在 SwiftUI 中进行双向通信 (https://swiftwithmajid.com/2020/04/08/binding-in-swiftui/)

    因为你有一个Persons的数组,所以我必须做一些工作才能得到一个直接的Binding,除非你想使用数组索引,这有点脆弱。您可以在函数bindingForId 中看到我是如何获得基于 id 的绑定的。

    我还有activePersonsnonActivePersons 的计算属性——当@Published 值之一发生更改时,这些计算属性将被重新计算。


    我的另一个您可能会发现相关的答案是:How can I change an @State Variable from an other View

    【讨论】:

    • 这很好用,谢谢!然而,有一件事还不完美:当我选择一个新人时,视图会暂时切换到父视图,然后再次进入新人视图。就我的目的而言,完美的选择是,在我选择了一个新人之后,视图会更改为这个人的编辑视图。是否可以从一个详细视图切换到另一个?
    猜你喜欢
    • 2017-03-26
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多