SwiftUI:NavigationLink 在变量更新时弹出

Posted

技术标签:

【中文标题】SwiftUI:NavigationLink 在变量更新时弹出【英文标题】:SwiftUI: NavigationLink pops back when variable is updated 【发布时间】:2022-01-14 08:53:00 【问题描述】:

我遇到了一个问题,即更新变量时导航链接会弹出。这有点令人困惑,因为这种行为不会发生在应用程序的其他地方,但我正在做同样的事情。

首先,有一个符合 Identifiable 的 Vehicle 结构体。在内容视图中,它显示在滚动视图中的 NavigationView 中。

ScrollView(showsIndicators: false) 
            VStack 
                ForEach(user.vehicles)  vehicle in
                    VehicleListItem(user: $user,
                                    vehicle: vehicle)

                
            
        

该列表项有一个导航链接,可将用户带到不同的视图。 Vehicle 作为绑定传递给下一个视图。

NavigationLink(destination: 
                VehicleView(user: $user,
                            vehicle: $user.vehicles[user.vehicles.firstIndex(where: $0.id == vehicle.id)!],
                            make: vehicle.make,
                            model: vehicle.model,
                            year: vehicle.year
                )
            , label: 
                Image(systemName: "car")
                    .font(.system(size: 100))
            ).padding(.bottom, 20)

在车辆视图中,用户可以编辑车辆信息(品牌、型号、年份),并且工作正常。当用户对 Vehicle 进行更改时,它不会弹回之前的视图。

Section() 
            VehicleHandler(make: $make, model: $model, year: $year)
            
            Button(action: 
                //Update
                vehicle.make = make
                vehicle.model = model
                vehicle.year = year
                
            , label: 
                ButtonView(label: "Update")
            ).disabled(make.isEmpty ||
                        model.isEmpty ||
                        (make == vehicle.make && model == vehicle.model && year == vehicle.year))
        

车辆处理程序

struct VehicleHandler: View 

enum Field: Hashable 
    case make
    case model


@FocusState private var field: Field?

@Binding var make: String
@Binding var model: String
@Binding var year: Int //Set to current year

var body: some View 
    TextField("Make", text: $make)
        .submitLabel(.next)
        .focused($field, equals: .make)
        .onSubmit 
            field = .model
        
    
    TextField("Model", text: $model)
        .submitLabel(.done)
        .focused($field, equals: .model)

    
    Picker("Year", selection: $year, content: 
        ForEach((1900...Date().year()).reversed(), id: \.self)  year in
            Text(String(year))
                .tag(year as Int)
        

    )

现在,用户可以添加维护记录。一旦他们添加了一条记录,它就会使用 ForEach 显示。记录也符合可识别性。

//Only show 5
            ForEach(vehicle.maintenanceRecords.prefix(5))  record in
                NavigationLink(destination: 
                    MaintenanceView(user: $user,
                                    record: $vehicle.maintenanceRecords[vehicle.getMaintenanceIndex(id: record.id)],
                                    date: record.date,
                                    mileage: record.mileage ?? 0,
                                    note: record.note,
                                    cost: record.cost ?? 0,
                                    tasks: record.tasks)
                , label: 
                    Text("\(record.date.formattedNoYear()) - \(record.note)")
                )
            

同样的概念被用于编辑维护记录。记录作为绑定传递,记录信息被传递以更新各个字段。当用户按下更新时,它会更新记录。此时,只要您进行编辑并按更新,它就会弹回 VehicleView。当我不使用 ID 时,我曾经遇到过这个问题,但是,每个结构都符合 Identifiable 并且 ID 没有被修改。我已经检查以确保它们保持不变,其中包括车辆的 ID 和记录。有谁知道为什么它会一直出现在这里,但是当车辆信息更新时却没有?如果您需要更多信息,请告诉我。

            RecordHandler(user: $user,
                      date: $date,
                      mileage: $mileage,
                      note: $note,
                      cost: $cost,
                      task: $task,
                      tasks: $tasks)
        
        Section() 
            Button(action: 
                
                //Update record
                record.date = date
                record.note = note
                record.tasks = tasks
                
                if mileage.isZero == false 
                    record.mileage = mileage
                
                
                if cost.isZero == false 
                    record.cost = cost
                
                
                //Clear task
                task = ""
            , label: 
                ButtonView(label: "Update")
            ).disabled(note.isEmpty ||
                        (date == record.date && mileage == record.mileage && note == record.note && cost == record.cost && tasks == record.tasks)
            )
        

记录处理程序

struct RecordHandler: View 

enum Field: Hashable 
    case note
    case mileage
    case cost
    case task


@FocusState var field: Field?

@Binding var user: User
@Binding var date: Date
@Binding var mileage: Float
@Binding var note: String
@Binding var cost: Float
@Binding var task: String
@Binding var tasks: [Vehicle.Record.Task]

@State var suggestion: String = ""

var body: some View 
    Section() 
        DatePicker("Date", selection: $date)
        TextField("Note", text: $note)
            .submitLabel(.next)
            .focused($field, equals: .note)
            .onSubmit 
                field = .mileage
            
        FieldWithLabel(label: "Mileage", value: $mileage, formatter: NumberFormatter.number)
            .submitLabel(.next)
            .focused($field, equals: .mileage)
            .onSubmit 
                field = .cost
            
        FieldWithLabel(label: "Cost", value: $cost, formatter: NumberFormatter.currency)
            .submitLabel(.next)
            .focused($field, equals: .cost)
            .onSubmit 
                field = .task
            
    
    
    //For task
    Section() 
        HStack 
            TextField("Task", text: $task)
                .submitLabel(.done)
                .focused($field, equals: .task)
                .onSubmit 
                    addTask()
                    field = .task
                
                .toolbar(content: 
                    ToolbarItemGroup(placement: .keyboard) 
                        Spacer()

                        Button("Close") 
                            UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
                        
                    
                )
                .onChange(of: task, perform:  value in
                    //See if any of the saved task are similar
                    if user.tasks.contains(where: $0.contains(value)) 
                        //Set suggestion
                        suggestion = user.tasks.first(where: $0.contains(value))!
                     else 
                        //Leave it empty if there's nothing
                        suggestion = ""
                    
                )
            Spacer()
            
            Button(action: 
                addTask()
            , label: 
                Image(systemName: "plus.circle.fill")
                    .font(.system(size: 22))
            ).buttonStyle(BorderlessButtonStyle())
            
        
        
        //To show task suggestions
        if suggestion.isEmpty == false 
            Button(action: 
                //Set task to suggestion
                task = suggestion
                
                //Add task
                addTask()
                
                //Focus field
                field = .task
            , label: 
                HStack 
                    Text("Suggestion")
                    Spacer()
                    Text(suggestion)
                
            )
        

        
        ForEach(tasks)  task in
            Text(task.name)
        
        .onDelete(perform: delete)
    



func addTask() 
    //Create
    let task = Vehicle.Record.Task(name: task)
    
    //Add to array
    tasks.append(task)
    
    //Clear
    self.task = ""


func delete(at offsets: IndexSet) 
    tasks.remove(atOffsets: offsets)

【问题讨论】:

欢迎来到 SO - 请使用 tour 并阅读 How to Ask 以改进、编辑和格式化您的问题。如果没有Minimal Reproducible Example,就无法帮助您进行故障排除。 【参考方案1】:

我遇到了同样的问题,今天设法解决了。尝试将选项 .navigationViewStyle(.stack) 添加到您的 NavigationView

struct ContentView: View 
    var body: some View 
        NavigationView 
            //All your content and navigation links
        .navigationViewStyle(.stack)
    

【讨论】:

所以我将其设置为,但是,我将其应用于导航视图内的列表而不是导航视图本身。一旦我移动它,它就开始工作了。 对我来说,将内容包装在 NavigationView 中解决了它。谢谢你的提示。不需要.navigationViewStyle(.stack)

以上是关于SwiftUI:NavigationLink 在变量更新时弹出的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:NavigationLink 奇怪的行为

SwiftUI 上的 NavigationLink 两次推送视图

SwiftUI:NavigationLink 内的水平 ScrollView 会中断导航

有没有办法向 NavigationLink 添加额外的功能? SwiftUI

无法单击列表中的 NavigationLink (SwiftUI)

需要帮助使 NavigationLink 在 SwiftUI 中工作