多个工作表(isPresented :) 在 SwiftUI 中不起作用

tur*_*ted 129 modal-dialog swift swiftui

我有这个 ContentView 有两个不同的模式视图,所以我sheet(isPresented:)同时使用这两个视图,但似乎只有最后一个被呈现。我怎么能解决这个问题?或者无法在 SwiftUI 的视图上使用多个工作表?

struct ContentView: View {
    
    @State private var firstIsPresented = false
    @State private var secondIsPresented = false
    
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                Button("First modal view") {
                    self.firstIsPresented.toggle()
                }
                Button ("Second modal view") {
                    self.secondIsPresented.toggle()
                }
            }
            .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
            .sheet(isPresented: $firstIsPresented) {
                    Text("First modal view")
            }
            .sheet(isPresented: $secondIsPresented) {
                    Text("Only the second modal view works!")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码编译时没有警告(Xcode 11.2.1)。

ram*_*nok 153

UPD

Xcode 12.5.0 Beta 3(2021 年 3 月 3 日)开始,这个问题不再有意义,因为现在可以有多个.sheet(isPresented:).fullScreenCover(isPresented:)连续,并且问题中提供的代码可以正常工作。

尽管如此,我发现这个答案仍然有效,因为它很好地组织了工作表并使代码干净且更具可读性 - 你有一个事实来源而不是几个独立的布尔值

实际答案

最好的方法,这也适用于iOS 14

enum ActiveSheet: Identifiable {
    case first, second
    
    var id: Int {
        hashValue
    }
}

struct YourView: View {
    @State var activeSheet: ActiveSheet?

    var body: some View {
        VStack {
            Button {
                activeSheet = .first
            } label: {
                Text("Activate first sheet")
            }

            Button {
                activeSheet = .second
            } label: {
                Text("Activate second sheet")
            }
        }
        .sheet(item: $activeSheet) { item in
            switch item {
            case .first:
                FirstView()
            case .second:
                SecondView()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在此处阅读更多信息:https : //developer.apple.com/documentation/swiftui/view/sheet(item : ondismiss : content:)

隐藏刚刚设置的工作表 activeSheet = nil

奖励: 如果您希望您的工作表全屏显示,请使用完全相同的代码,而不是.sheet编写.fullScreenCover

  • 这个解决方案在 iOS 14 上运行得非常好,而且非常优雅。如果(现在允许),我建议使用开关,特别是如果有两个以上的视图以使其更干净 (4认同)
  • @TobiasTimpe,您可以传递一个闭包,将“activeSheet”设置为“nil”(例如“SheetToShow(onCompleteBlock: { self.activeSheet = nil })”,然后从工作表中调用该闭包)。您还可以将“activeSheet”传递给工作表并将其设置为工作表内的“nil”,但我建议不要将此参数暴露给其他视图 (2认同)

Roh*_*ana 76

请尝试以下代码

更新答案(iOS 14、Xcode 12)

enum ActiveSheet {
   case first, second
   var id: Int {
      hashValue
   }
}

struct ContentView: View {

    @State private var showSheet = false
    @State private var activeSheet: ActiveSheet? = .first

    var body: some View {
    
        NavigationView {
            VStack(spacing: 20) {
                Button("First modal view") {
                    self.showSheet = true
                    self.activeSheet = .first
                }
                Button ("Second modal view") {
                    self.showSheet = true
                    self.activeSheet = .second
                }
            }
            .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
            .sheet(isPresented: $showSheet) {
                if self.activeSheet == .first {
                    Text("First modal view")
                }
                else {
                    Text("Only the second modal view works!")
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这似乎在 iOS14 上不起作用。即使在将“showSheet”指定为 true 之前将“@State”变量更改为其他变量,它也会尝试加载默认的活动工作表(即 .first) (25认同)
  • 这个解决方案仅在更改“@State private var activeSheet:ActiveSheet = .first”之后才对我起作用“@State private var activeSheet:ActiveSheet?”,否则它将在任一按钮上显示.first,直到单击第一个然后第二个。 (2认同)
  • @ramzesenok 的解决方案在 iOS 14 中效果很好。现在测试版中允许新方法表(item:) 和 switch 吗? (2认同)

Tyl*_*230 43

还可以将工作表添加到放置在视图背景中的 EmptyView。这可以多次执行:

  .background(EmptyView()
        .sheet(isPresented: isPresented, content: content))
Run Code Online (Sandbox Code Playgroud)


Asp*_*eri 40

您的情况可以通过以下方式解决(使用 Xcode 11.2 测试)

var body: some View {

    NavigationView {
        VStack(spacing: 20) {
            Button("First modal view") {
                self.firstIsPresented.toggle()
            }
            .sheet(isPresented: $firstIsPresented) {
                    Text("First modal view")
            }
            Button ("Second modal view") {
                self.secondIsPresented.toggle()
            }
            .sheet(isPresented: $secondIsPresented) {
                    Text("Only the second modal view works!")
            }
        }
        .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
    }
}
Run Code Online (Sandbox Code Playgroud)


use*_*617 9

您只需将按钮和 .sheet 调用组合在一起即可完成此操作。如果您有一个前导和一个尾随,那就很简单了。但是,如果您在前导或尾随中有多个导航栏项,则需要将它们包装在 HStack 中,并将每个按钮及其表单调用包装在 VStack 中。

这是两个尾随按钮的示例:

            trailing:
            HStack {
                VStack {
                    Button(
                        action: {
                            self.showOne.toggle()
                    }
                    ) {
                        Image(systemName: "camera")
                    }
                    .sheet(isPresented: self.$showOne) {
                        OneView().environment(\.managedObjectContext, self.managedObjectContext)
                    }
                }//showOne vstack

                VStack {
                    Button(
                        action: {
                            self.showTwo.toggle()
                    }
                    ) {
                        Image(systemName: "film")
                    }
                    .sheet(isPresented: self.$showTwo) {
                        TwoView().environment(\.managedObjectContext, self.managedObjectContext)
                    }
                }//show two vstack
            }//nav bar button hstack
Run Code Online (Sandbox Code Playgroud)


SoN*_*ice 8

在其中创建自定义按钮视图和调用表解决了这个问题。

struct SheetButton<Content>: View where Content : View {

    var text: String
    var content: Content
    @State var isPresented = false

    init(_ text: String, @ViewBuilder content: () -> Content) {
        self.text = text
        self.content = content()
    }

    var body: some View {
        Button(text) {
            self.isPresented.toggle()
        }
        .sheet(isPresented: $isPresented) {
            self.content
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

ContentView 会更干净。

struct ContentView: View {

    var body: some View {

        NavigationView {
            VStack(spacing: 20) {
                SheetButton("First modal view") {
                    Text("First modal view")
                }
                SheetButton ("Second modal view") {
                    Text("Only the second modal view works!")
                }
            }
            .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当打开工作表取决于列表行内容时,此方法也可以正常工作。

struct ContentView: View {

    var body: some View {

        NavigationView {
            List(1...10, id: \.self) { row in
                SheetButton("\(row) Row") {
                    Text("\(row) modal view")
                }
            }
            .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Aus*_*lon 5

从 iOS 和 iPadOS 14.5 Beta 3 开始,无论何时公开发布,多张工作表都会按预期工作,并且不需要其他答案中的任何解决方法。从发行说明:

用户界面

已在 iOS 和 iPadOS 14.5 Beta 3 中解决

您现在可以在同一视图层次结构中应用多个sheet(isPresented:onDismiss:content:)fullScreenCover(item:onDismiss:content:)修改器。(74246633)