SwiftUI:如何在另一个视图中更新传递的数组项

Posted

技术标签:

【中文标题】SwiftUI:如何在另一个视图中更新传递的数组项【英文标题】:SwiftUI: How to update passing array item in the other view 【发布时间】:2020-02-26 19:24:39 【问题描述】:

我正在尝试使用在 Textfield 中键入的新值更新数组项,但未使用编辑值更新列表。

我的代码是:

型号:

    struct WalletItem: Identifiable

        let id = UUID()
        var name:String
        var cardNumber:String
        var type:String
        var cvc:String
        let pin:String
        var dateOfExpiry:String
    

模型视图:

    class Wallet: ObservableObject

        @Published var wallets = [
            WalletItem(name: "BSB", cardNumber: "123456789", type: "master card", cvc: "1234", pin: "1234", dateOfExpiry: "2016-06-29"),
            WalletItem(name: "Alpha bank", cardNumber: "123456789", type: "master card", cvc: "1234", pin: "1234", dateOfExpiry: "2017-03-12"),
            WalletItem(name: "MTБ", cardNumber: "123456789", type: "master card", cvc: "1234", pin: "1234", dateOfExpiry: "2020-11-12"),
        ]

    

第一次查看:

    struct WalletListView: View 

        // Properties
        // ==========

        @ObservedObject var wallet = Wallet()
        @State var isNewItemSheetIsVisible = false


        var body: some View 
            NavigationView 
                List(wallet.wallets)  walletItem in
                    NavigationLink(destination: EditWalletItem(walletItem: walletItem))
                            Text(walletItem.name)
                        
                
                .navigationBarTitle("Cards", displayMode: .inline)
                .navigationBarItems(
                    leading: Button(action:  self.isNewItemSheetIsVisible = true
                    ) 
                        HStack 
                            Image(systemName: "plus.circle.fill")
                            Text("Add item")
                        
                    
                )
            
            .sheet(isPresented: $isNewItemSheetIsVisible) 
                NewWalletItem(wallet: self.wallet)
            
         

    

次要观点:

    struct EditWalletItem: View 

        @State var walletItem: WalletItem


        @Environment(\.presentationMode) var presentationMode

        var body: some View 
            Form
                Section(header: Text("Card Name"))
                    TextField("", text: $walletItem.name)
                

            
            .navigationBarItems(leading:
                Button(action: 
                    self.presentationMode.wrappedValue.dismiss()
                )
                
                    Text("Back")
                , trailing:
                Button(action: 

                    self.presentationMode.wrappedValue.dismiss()
                )
                
                    Text("Save")
            )

        

    

P.S:如果我使用@Binding 而不是@State,我在第一个视图中会出现错误:初始化程序init(_:) 要求Binding<String> 符合StringProtocol

【问题讨论】:

【参考方案1】:

这里是修改过的部分(测试并适用于 Xcode 11.2 / ios 13.2):

1) 确定过度绑定

struct EditWalletItem: View 
    @Binding var walletItem: WalletItem

2) 传递地点

List(Array(wallet.wallets.enumerated()), id: \.element.id)  (i, walletItem) in
    NavigationLink(destination: EditWalletItem(walletItem: self.$wallet.wallets[i]))
            Text(walletItem.name)
        

【讨论】:

【参考方案2】:

ForEach(Array(list.enumerated())) 只有在列表是 Array 而不是 ArraySlice 时才能正常工作,并且它具有复制列表的缺点。

更好的方法是使用.indexed() 助手:

struct IndexedCollection<Base: RandomAccessCollection>: RandomAccessCollection 
    typealias Index = Base.Index
    typealias Element = (index: Index, element: Base.Element)
    let base: Base
    var startIndex: Index  self.base.startIndex 
    var endIndex: Index  self.base.endIndex 

    func index(after i: Index) -> Index 
        self.base.index(after: i)
    

    func index(before i: Index) -> Index 
        self.base.index(before: i)
    

    func index(_ i: Index, offsetBy distance: Int) -> Index 
        self.base.index(i, offsetBy: distance)
    

    subscript(position: Index) -> Element 
        (index: position, element: self.base[position])
    


extension RandomAccessCollection 
    func indexed() -> IndexedCollection<Self> 
        IndexedCollection(base: self)
    

例子:

// SwiftUIPlayground
// https://github.com/ralfebert/SwiftUIPlayground/

import Foundation
import SwiftUI

struct Position 
    var id = UUID()
    var count: Int
    var name: String


class BookingModel: ObservableObject 

    @Published var positions: [Position]

    init(positions: [Position] = []) 
        self.positions = positions
    



struct EditableListExample: View 

    @ObservedObject var bookingModel = BookingModel(
        positions: [
            Position(count: 1, name: "Candy"),
            Position(count: 0, name: "Bread"),
        ]
    )

    var body: some View 
        // >>> Passing a binding into an Array via index:
        List(bookingModel.positions.indexed(), id: \.element.id)  i, _ in
            PositionRowView(position: self.$bookingModel.positions[i])
        
    


struct PositionRowView: View 

    @Binding var position: Position

    var body: some View 
        Stepper(
            value: $position.count,
            label: 
                Text("\(position.count)x \(position.name)")
            
        )
    



struct EditableListExample_Previews: PreviewProvider 
    static var previews: some View 
        EditableListExample()
    

另见:

How does the Apple-suggested .indexed() property work in a ForEach?

【讨论】:

以上是关于SwiftUI:如何在另一个视图中更新传递的数组项的主要内容,如果未能解决你的问题,请参考以下文章

如何将字符串数组从视图模型传递到 swiftUI 中播放歌曲

SwiftUI如何在更新已发布数组中的对象而不是数组本身时更新视图

如何切换数组中对象的属性并更新 SwiftUI 视图?

SwiftUI 将数组的元素作为绑定传递给子视图

SwiftUI - 如何在另一个视图中响应 TextField onCommit?

将数据从模型传递到 SwiftUI 中的视图