Отклонить представление в SwiftUI при повторном рендеринге родителя
Используя iOS14.4, Swift5.3.2, XCode12.2,
Я пытаюсь отклонить SwiftUI GridView (см. Код ниже).
Функция увольнения выполняется свойством
@Environment
как объяснено здесь .
Все работает до того момента, пока я не представил
@Binding
свойство, которое мутирует parent-View в самый момент увольнения. (видеть
dataStr = titles[idx]
в отрывке кода ниже).
Я прочитал это увольнение
\.presentationMode
работает только в том случае, если родительский View не обновляется во время отображения дочернего View.
Но мне абсолютно необходимо вызвать мутацию в родительском представлении, когда пользователь нажимает на элемент GridView, который здесь играет.
Как я могу переписать так, чтобы parent-View обновлялся, а отключение Child-View все еще работало?
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()
}
}
}
Как спросил jn_pdx , здесь вы можете увидеть jn_pdx parent-View. Пожалуйста, найдите
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())
}
}
1 ответ
С помощью jn_pdx я нашел обходной путь.
Оберните свойство привязки (например, в моем примере) в отложенный блок, который выполняется примерно через 50 мс:
.onTapGesture {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(50)) {
dataStr = thumNames[idx]
}
self.presentationMode.wrappedValue.dismiss()
}
Конечно, этот обходной путь работает только в моем случае, потому что мне больше не нужно держать дочернее представление открытым. Могут быть и другие ситуации, когда необходимо обновить Parent-View перед закрытием Child-View (т.е. здесь обновление
dataStr
можно сделать прямо в момент закрытия Child-View).
Мне все еще интересно, как справиться с проблемами увольнения в любом случае, когда Child-View выполняет обновление Parent-View перед закрытием. Это ситуации, когда функция закрытия SwiftUI с этого момента больше не работает. Любая мутация Parent-View приводит к тому, что Child-View
separate
как-то и
dismisss
больше не работает.
Есть идеи, что делать в таком случае?