SwiftUI:打开带有动态内容的模式表仅在第一次尝试时失败

Posted

技术标签:

【中文标题】SwiftUI:打开带有动态内容的模式表仅在第一次尝试时失败【英文标题】:SwiftUI: Opening a modal sheet with dynamic content fails only on first attempt 【发布时间】:2020-08-24 23:57:38 【问题描述】:

目标是将动态内容传递到单个模式中,就好像它是一个详细视图:

import SwiftUI

struct Item 
  let number: String
  let id = UUID()


class ItemSet: ObservableObject 
  @Published var collection: [Item]
  
  init() 
    self.collection = []
    for index in 1...100 
      self.collection.append(Item(number: "\(index)"))
    
  


struct ContentView: View 
  @ObservedObject var items: ItemSet
  @State private var selectedItem: Item?
  @State private var showingFull = false
  
  init() 
    self.items = ItemSet()
    self.selectedItem = nil
  
    
  var columns = [
    GridItem(.adaptive(minimum: 150), spacing: 5.0)
  ]
  
  var body: some View 
    ScrollView 
      LazyVGrid(columns: columns) 
        ForEach(items.collection, id: \.id) item in
          Text(item.number)
            .frame(height: 100)
            .onTapGesture 
              self.selectedItem = item
              self.showingFull = true
            
            .sheet(isPresented: $showingFull) 
              if let item = selectedItem 
                Text(item.number)
              
            
        
      
    
  


struct ContentView_Previews: PreviewProvider 
  static var previews: some View 
    ContentView()
  


由于某种原因,第一次点击单元格时,模态框是空的,就好像在呈现模态框内容之前状态没有更新,但之后的任何时间都按预期工作。我是否遗漏了一些明显的东西,还是应该提交雷达?

【问题讨论】:

对我来说唯一可行的解​​决方案是将selectedItem从视图中的@State移动到模型中的@Published,并将.sheet移出ForEach . 这能回答你的问题吗? SwiftUI: show different sheet items conditionally 【参考方案1】:

这是多表冲突,因为您将同一张表附加到 ForEach 中的每个单元格并绑定到单个状态,因此单击它们会立即激活。解决方法是将工作表移出ForEach

这是一个更正的部分。使用 Xcode 12 / ios 14 测试

  var body: some View 
    ScrollView 
      LazyVGrid(columns: columns) 
        ForEach(items.collection, id: \.id) item in
          Text(item.number)
            .frame(height: 100)
            .onTapGesture 
              self.selectedItem = item
              self.showingFull = true
            
        
      
    
    .sheet(isPresented: $showingFull)       // << here !!
      if let item = selectedItem 
        Text(item.number)
      
    
  

【讨论】:

啊,是的,将工作表放入 ForEach 是一个非常愚蠢的错误。但是,我刚刚尝试了您的确切正文代码,得到了相同的结果:第一个模态中没有任何内容,然后一切正常。我在 Xcode beta 5 上,所以也许这只是一个回归。我现在要归档雷达... 在模拟器或真机上试用,预览可能会失败。 在物理 iPhone11,6、动态 SwiftUI 预览和模拟 iPhone 11 上试用;都产生了相同的描述结果。 beta 6 的结果相同;反馈归档为 FB8531587 有什么解决办法吗?【参考方案2】:

我发现的一个可能的解决方案是将工作表和状态移动为它们自己的结构:

struct ContentView: View 
  @ObservedObject var items: ItemSet
  
  init() 
    self.items = ItemSet()
  
    
  var columns = [
    GridItem(.adaptive(minimum: 150), spacing: 5.0)
  ]
  
  var body: some View 
     ScrollView 
       LazyVGrid(columns: columns) 
         ForEach(items.collection, id: \.id) item in
          TextItem(item: item)
         
       
     
   


struct TextItem: View 
  let item: Item
  @State private var showingFull = false

  var body: some View 
    Text(item.number)
      .frame(height: 100)
      .onTapGesture 
        self.showingFull = true
      
      .sheet(isPresented: $showingFull) 
        Text(item.number)
      
  

我不知道这个解决方案在正确的 SwiftUI 方面有多“正确”,但在更复杂和繁重的布局中使用时,我没有注意到任何性能问题。

【讨论】:

这确实有帮助。【参考方案3】:

遇到同样的问题并通过切换到sheet(item) 并删除布尔检查以显示工作表来解决它

struct TextItem: View 
  @State let item: Item

  var body: some View 
    Text(item.number)
      .frame(height: 100)
      .onTapGesture 
        self.showingFull = true
      
      .sheet($item) 
        Text($0.number)
      
  

【讨论】:

以上是关于SwiftUI:打开带有动态内容的模式表仅在第一次尝试时失败的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 模态演示仅在 navigationBarItems 中有效

带有表单的 SwiftUI 模态表:设备旋转后左/右填充错误

SwiftUI:如何仅在设备处于横向模式时启用 ScrollView?

每次打开组件时如何调用函数?

SwiftUI 模式表在关闭后重新打开

Google Chrome:样式表仅出现在“检查元素”之后