问题描述
使用 iOS14.4、Swift5.3.2、XCode12.2,
我尝试关闭 SwiftUI 的 GridView(见下面的代码)。
解除功能由 \.presentationMode
as explained here 的 @Environment
属性完成。
一切正常,直到我引入了一个 @Binding
属性,该属性在关闭的那一刻改变了父视图。 (请参阅下面代码摘录中的 dataStr = titles[idx]
)。
我读到 \.presentationMode
的解雇仅适用于在显示子视图期间父视图未更新的情况。
但是当用户点击此处播放的 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())
}
}
解决方法
在 @jnpdx 的帮助下,我找到了解决方法。
将绑定属性(即我的示例中的 dataStr
)包装到一个延迟块中,该块在大约 50 毫秒后执行:
.onTapGesture {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(50)) {
dataStr = thumNames[idx]
}
self.presentationMode.wrappedValue.dismiss()
}
当然,这个解决方法只适用于我的情况,因为我不再需要保持子视图打开。可能还有其他情况需要在关闭子视图之前更新父视图(即,此处 dataStr
的更新可以在子视图关闭时刻完成)。
对于子视图在关闭之前更新父视图的任何情况,我仍然想知道如何处理解雇问题。这些是 SwiftUI 的关闭功能从那时起不再起作用的情况。父视图的任何变化都会导致子视图以某种方式 separate
并且 dismisss
不再起作用。
知道在这种情况下该怎么做吗?