0

使用iOS14.4、Swift5.3.2、XCode12.2、

我尝试关闭 SwiftUI 的 GridView(参见下面的代码)。

解除功能是由 的\.presentationMode属性完成的,@Environment 如here所述。

一切正常,直到我引入一个@Binding在解雇的那一刻改变父视图的属性。(见dataStr = titles[idx]下面的代码摘录)。

我读到,\.presentationMode只有在显示子视图期间父视图未更新时,解雇 by 才有效。

但是当用户在这里点击 GridView 的一个元素时,我绝对需要在父视图上引起突变。

如何重写以便更新父视图并且解除子视图仍然有效?

struct GridView: View {
    
    @Environment(\.presentationMode) private var presentationMode
    
    @Binding var dataStr: String
    
    @State private var titles = [String]()
    
    let layout = [
        GridItem(.flexible()),
        GridItem(.flexible())
    ]
    
    var body: some View {
        ScrollView {
            LazyVGrid(columns: layout, spacing: 10) {
                ForEach(titles.indices, id: \.self) { idx in
                    VStack {
                        Text(titles[idx])
                        Image(titles[idx])
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: (UIScreen.main.bounds.width / 2) - 40)
                    }
                        .onTapGesture {

                            // WITHOUT THIS LINE OF CODE - EVERYTHING WORKS. WHY???????????????
                            dataStr = titles[idx]

                            self.presentationMode.wrappedValue.dismiss()
                        }
                }
            }
            .padding()
        }
    }
}

正如@jnpdx 所问,在这里您可以看到父视图。请找到GridView(dataStr: self.$dataStr)里面.sheet()ToolBarItem()......

import SwiftUI

struct MainView: View {
    
    @EnvironmentObject var mediaViewModel: MediaViewModel
    @EnvironmentObject var commService: CommunicationService
    
    @State private var dataStr = ""
    @State private var connectionsLabel = ""
    @State private var commumincationRole: THRole = .noMode
    @State private var showingInfo = false
    @State private var showingGrid = false
    
    init() {
        UINavigationBar.appearance().tintColor = UIColor(named: "title")        
    }
    
    var body: some View {
        NavigationView {
            if mediaViewModel.mediaList.isEmpty {
                LoadingAnimationView()                    
                    .navigationBarHidden(true)
                    .ignoresSafeArea()
            } else {
                if dataStr.isEmpty {
                    
                    MainButtonView(dataStr: $dataStr,
                        commumincationRole: $commumincationRole,
                          connectionsLabel: $connectionsLabel
                    )
                        .navigationBarHidden(false)
                        .navigationTitle("Trihow Pocket")
                        .navigationBarColor(backgroundColor: UIColor(named: "btnInactive"), titleColor: UIColor(named: "title"))
                        .toolbar {
                            ToolbarItem(placement: .navigationBarLeading) {
                                Button(action: {
                                    showingInfo.toggle()
                                }) {
                                    Image(systemName: "ellipsis")
                                }
                                .sheet(isPresented: $showingInfo) {
                                    InfoView()
                                }
                            }
                            ToolbarItem(placement: .navigationBarTrailing) {
                                Button(action: {
                                    showingGrid.toggle()
                                }) {
                                    Image(systemName: "square.grid.3x3")
                                }
                                .sheet(isPresented: $showingGrid) {

                                    // GRIDVIEW CALLING THE CHILD-VIEW IS HERE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                                    GridView(dataStr: self.$dataStr)
                                }
                            }
                    }
                } else {
                    let str = self.dataStr
                    #if os(iOS)
                    PageViewiOS(dataStr: self.$dataStr, commumincationRole: $commumincationRole)
                        .navigationBarHidden(true)
                        .onAppear() {
                            if commumincationRole == .moderatorMode {
                                commService.send(thCmd: THCmd(key: .tagID, sender: "", content: str))
                            }
                        }
                        .ignoresSafeArea()
                    #elseif os(macOS)
                    PageViewMacOS()
                        .ignoresSafeArea()
                    #endif
                }
            }
        }
        .onTHComm_PeerAction(service: commService) { (peers) in
            
            let idsOrNames = peers.map { (peer) -> String in
                if let id = peer.id {
                    return "\(id)"
                } else if let name = peer.name {
                    return "\(name)"
                } else {
                    return ""
                }
            }
            connectionsLabel = "Connected devices: \n\(idsOrNames.lineFeedString)"
        }
        .onTHComm_ReceiveCmd(service: commService) { (thCmd) in
            if (commumincationRole == .moderatorMode) || (commumincationRole == .discoveryMode) {
                switch thCmd.key {
                case .tagID:
                    dataStr = thCmd.content
                case .closeID:
                    dataStr = ""
                default:
                    break
                }
            }
        }
        .onTHComm_LastMessageLog(service: commService) { (log) in
            print(log)
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

struct MainView_Previews: PreviewProvider {
    static var previews: some View {
        MainView()
            .environmentObject(MediaViewModel())
            .environmentObject(MultipeerConnectivityService())
    }
}
4

1 回答 1

0

在@jnpdx的帮助下,我找到了一种解决方法。

将绑定属性(即dataStr在我的示例中)包装到延迟块中,该块在 50 毫秒后执行:

.onTapGesture {
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(50)) {
        dataStr = thumNames[idx]
    }
    self.presentationMode.wrappedValue.dismiss()
}

当然,这种解决方法只适用于我的情况,因为我不再需要保持 Child-View 处于打开状态。在其他情况下,可能需要在关闭子视图之前更新父视图(即这里的更新dataStr可以在子视图的关闭时刻完成)。

对于子视图在关闭之前进行父视图更新的任何情况,我仍然想知道如何处理解雇问题。这些是 SwiftUI 的解除功能从那时起不再起作用的情况。父视图的任何突变都会导致子视图以separate某种方式dismisss不再起作用。

知道在这种情况下该怎么做吗?

于 2021-03-16T08:14:38.487 回答