SwiftUI: поддержка нескольких модальных

Я пытаюсь настроить вид, который может отображать несколько модальных режимов в зависимости от того, какая кнопка нажата.

Когда я добавляю только один sheet, все работает:

.sheet(isPresented: $showingModal1) { ... }

Но когда я добавляю другой лист, работает только последний.

.sheet(isPresented: $showingModal1) { ... }
.sheet(isPresented: $showingModal2) { ... }

ОБНОВИТЬ

Я пытался заставить это работать, но я не уверен, как объявить тип для modal. Я получаю сообщение об ошибке Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements.

struct ContentView: View {
    @State var modal: View?
    var body: some View {
        VStack {
            Button(action: {
                self.modal = ModalContentView1()
            }) {
                Text("Show Modal 1")
            }
            Button(action: {
                self.modal = ModalContentView2()
            }) {
                Text("Show Modal 2")
            }
        }.sheet(item: self.$modal, content: { modal in
            return modal
        })
    }
}

struct ModalContentView1: View {
    var body: some View {
        Text("Modal 1")
    }
}

struct ModalContentView2: View {
    var body: some View {
        Text("Modal 2")
    }
}

Ответ 1

Может быть, я упустил момент, но вы можете достичь этого либо с помощью одного вызова .sheet(), либо нескольких вызовов.

Многократный .sheet() подход:

import SwiftUI

struct MultipleSheets: View {
    @State private var sheet1 = false
    @State private var sheet2 = false
    @State private var sheet3 = false

    var body: some View {
        VStack {

            Button(action: {
                self.sheet1 = true
            }, label: { Text("Show Modal #1") })
            .sheet(isPresented: $sheet1, content: { Sheet1() })

            Button(action: {
                self.sheet2 = true
            }, label: { Text("Show Modal #2") })
            .sheet(isPresented: $sheet2, content: { Sheet2() })

            Button(action: {
                self.sheet3 = true
            }, label: { Text("Show Modal #3") })
            .sheet(isPresented: $sheet3, content: { Sheet3() })

        }
    }
}

struct Sheet1: View {
    var body: some View {
        Text("This is Sheet #1")
    }
}

struct Sheet2: View {
    var body: some View {
        Text("This is Sheet #2")
    }
}

struct Sheet3: View {
    var body: some View {
        Text("This is Sheet #3")
    }
}

Единый подход .sheet():

struct MultipleSheets: View {
    @State private var showModal = false
    @State private var modalSelection = 1

    var body: some View {
        VStack {

            Button(action: {
                self.modalSelection = 1
                self.showModal = true
            }, label: { Text("Show Modal #1") })

            Button(action: {
                self.modalSelection = 2
                self.showModal = true
            }, label: { Text("Show Modal #2") })

            Button(action: {
                self.modalSelection = 3
                self.showModal = true
            }, label: { Text("Show Modal #3") })

        }
        .sheet(isPresented: $showModal, content: {
            if self.modalSelection == 1 {
                Sheet1()
            }

            if self.modalSelection == 2 {
                Sheet2()
            }

            if self.modalSelection == 3 {
                Sheet3()
            }
        })

    }
}

struct Sheet1: View {
    var body: some View {
        Text("This is Sheet #1")
    }
}

struct Sheet2: View {
    var body: some View {
        Text("This is Sheet #2")
    }
}

struct Sheet3: View {
    var body: some View {
        Text("This is Sheet #3")
    }
}

Ответ 2

Это работает:

.background(EmptyView().sheet(isPresented: $showingModal1) { ... }
   .background(EmptyView().sheet(isPresented: $showingModal2) { ... }))

Обратите внимание, как они вложены backgrounds. Не два фона один за другим.

Спасибо DevAndArtist за это.

Ответ 3

Я лично подражал бы некоторому API NavigationLink. Затем вы можете создать хешируемое перечисление и решить, какой модальный лист вы хотите представить.

extension View {
  func sheet<Content, Tag>(
    tag: Tag,
    selection: Binding<Tag?>,
    content: @escaping () -> Content
  ) -> some View where Content: View, Tag: Hashable {
    let binding = Binding(
      get: {
        selection.wrappedValue == tag
      },
      set: { isPresented in
        if isPresented {
          selection.wrappedValue = tag
        } else {
          selection.wrappedValue = .none
        }
      }
    )
    return background(EmptyView().sheet(isPresented: binding, content: content))
  }
}

enum ActiveSheet: Hashable {
  case first
  case second
}

struct First: View {
  var body: some View {
    Text("frist")
  }
}

struct Second: View {
  var body: some View {
    Text("second")
  }
}

struct TestView: View {
  @State
  private var _activeSheet: ActiveSheet?

  var body: some View {
    print(_activeSheet as Any)
    return VStack
      {
        Button("first") {
          self._activeSheet = .first
        }
        Button("second") {
          self._activeSheet = .second
        }
      }
      .sheet(tag: .first, selection: $_activeSheet) {
        First()
      }
      .sheet(tag: .second, selection: $_activeSheet) {
        Second()
      }
  }
}

Ответ 4

Два альтернативных метода

  1. Каждое представление имеет свойство @State:
import SwiftUI

struct PresentView: View {
    var body: some View {
        List(0..<5) { index in
            PresentCellView()
        }
    }
}

struct PresentCellView: View {
    @State private var isPresented = false

    var body: some View {
        Button(action: { self.isPresented.toggle()
        }, label: {
            Text("Tap to present")
        }).sheet(isPresented: $isPresented) {
            NavigationView {
                PresentingDemoView(showingModal: self.$isPresented)
                    .navigationBarItems(leading: Button(action: { self.isPresented.toggle()
                    }, label: {
                        Text("Cancel")
                    })
                )
            }
        }
    }
}

struct PresentingDemoView: View {
    @Binding var showingModal: Bool

    var body: some View {
        Text("Presenting Demo View")
    }
}

struct PresentView_Previews: PreviewProvider {
    static var previews: some View {
        PresentView()
    }
}
  1. поделиться одним .sheet:
struct PresentView: View {

    @State private var isPresented = false
    @State private var presentingIndex = 0 // or class/structure object

    var body: some View {
        List(0..<5) { index in //or VStack,HStack...
            Button(action: {
                self.isPresented.toggle()
                self.presentingIndex = index
            }, label: {
                Text("Tap to present:\(index)")
            })
        }
        .sheet(isPresented: $isPresented) {
            NavigationView {
                PresentingDemoView(showingModal: self.$isPresented, index: self.$presentingIndex)
                    .navigationBarItems(leading: Button(action: { self.isPresented.toggle()
                    }, label: {
                        Text("Cancel")
                    })
                )
            }
        }
    }
}

struct PresentingDemoView: View {
    @Binding var showingModal: Bool
    @Binding var index: Int

    var body: some View {
        Text("Presenting Demo View -- \(index)")
    }
}

struct PresentView_Previews: PreviewProvider {
    static var previews: some View {
        PresentView()
    }
}