SwiftUI отклонить модал
Поскольку SwiftUI является декларативным, метод dismiss
отсутствует. Как добавить кнопку "закрыть/закрыть" в DetailView
?
struct DetailView: View {
var body: some View {
Text("Detail")
}
}
struct ContentView : View {
var body: some View {
PresentationButton(Text("Click to show"), destination: DetailView())
}
}
Ответы
Ответ 1
Вы можете использовать переменную окружения presentationMode
в вашем модальном представлении и вызвать self.presentaionMode.wrappedValue.dismiss()
, чтобы отклонить модальное:
struct ContentView: View {
@State private var showModal = false
var body: some View {
Button(action: {
self.showModal = true
}) {
Text("Show modal")
}.sheet(isPresented: self.$showModal) {
ModalView()
}
}
}
struct ModalView: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var body: some View {
Group {
Text("Modal view")
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Text("Dismiss")
}
}
}
}
Ответ 2
Вот способ отклонить представленный вид.
struct DetailView: View {
@Binding
var dismissFlag: Bool
var body: some View {
Group {
Text("Detail")
Button(action: {
self.dismissFlag.toggle()
}) {
Text("Dismiss")
}
}
}
}
struct ContentView : View {
@State var dismissFlag = false
var body: some View {
Button(action: {
self.dismissFlag.toggle()
})
{ Text("Show") }
.presentation(!dismissFlag ? nil :
Modal(DetailView(dismissFlag: $dismissFlag)) {
print("dismissed")
})
}
}
Ответ 3
Вы можете реализовать это.
struct view: View {
@Environment(\.isPresented) private var isPresented
private func dismiss() {
isPresented?.value = false
}
}
Ответ 4
Теперь в бета-версии 5 есть довольно простой способ сделать это.
import SwiftUI
struct ModalView : View {
// In Xcode 11 beta 5, 'isPresented' is deprecated use 'presentationMode' instead
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var body: some View {
Group {
Text("Modal view")
Button(action: { self.presentationMode.value.dismiss() }) { Text("Dismiss") }
}
}
}
struct ContentView : View {
@State var showModal: Bool = false
var body: some View {
Group {
Button(action: { self.showModal = true }) { Text("Show modal via .sheet modifier") }
.sheet(isPresented: $showModal, onDismiss: { print("In DetailView onDismiss.") }) { ModalView() }
}
}
}
Ответ 5
Похоже, что для Xcode 11 Beta 7 (это на XD версии 11M392r) это немного отличается.
@Environment(\.presentationMode) var presentation
Button(action: { self.presentation.wrappedValue.dismiss() }) { Text("Dismiss") }
Ответ 6
Так как PresentationButton
прост в использовании, но скрывает состояние, которое подрывает предсказательный характер SwiftUI
я реализовал его с помощью доступной Binding
.
public struct BindedPresentationButton<Label, Destination>: View where Label: View, Destination: View {
/// The state of the modal presentation, either 'visibile' or 'off'.
private var showModal: Binding<Bool>
/// A 'View' to use as the label of the button.
public var label: Label
/// A 'View' to present.
public var destination: Destination
/// A closure to be invoked when the button is tapped.
public var onTrigger: (() -> Void)?
public init(
showModal: Binding<Bool>,
label: Label,
destination: Destination,
onTrigger: (() -> Void)? = nil
) {
self.showModal = showModal
self.label = label
self.destination = destination
self.onTrigger = onTrigger
}
public var body: some View {
Button(action: toggleModal) {
label
}
.presentation(
!showModal.value ? nil :
Modal(
destination, onDismiss: {
self.toggleModal()
}
)
)
}
private func toggleModal() {
showModal.value.toggle()
onTrigger?()
}
}
Вот как это используется:
struct DetailView: View {
@Binding var showModal: Bool
var body: some View {
Group {
Text("Detail")
Button(action: {
self.showModal = false
}) {
Text("Dismiss")
}
}
}
}
struct ContentView: View {
@State var showModal = false
var body: some View {
BindedPresentationButton(
showModal: $showModal,
label: Text("Show"),
destination: DetailView(showModal: $showModal)
) {
print("dismissed")
}
}
}
Ответ 7
В xcode beta5 еще один способ сделать это - использовать @State в представлении, которое запускает модальное окно, и добавить привязку в модальное представление, чтобы контролировать видимость модального окна. Это не требует от вас доступа к переменной @Environment presentationMode.
struct MyView : View {
@State var modalIsPresented = false
var body: some View {
Button(action: {self.modalIsPresented = true}) {
Text("Launch modal view")
}
.sheet(isPresented: $modalIsPresented, content: {
MyModalView(isPresented: self.$modalIsPresented)
})
}
}
struct MyModalView : View {
@Binding var isPresented: Bool
var body: some View {
Button(action: {self.isPresented = false}) {
Text("Close modal view")
}
}
}
Ответ 8
В Xcode 11.0 beta 7 значение теперь перенесено, у меня работает следующая функция:
func dismiss() {
self.presentationMode.wrappedValue.dismiss()
}
Ответ 9
Модальные представления в SwiftUI кажутся простыми, пока вы не начнете использовать их в представлениях List
или Form
. Я создал небольшую библиотеку, которая оборачивает все крайние случаи и делает использование модальных представлений таким же, как пара NavigationView
- NavigationLink
.
Библиотека с открытым исходным кодом здесь: https://github.com/diniska/modal-view. Вы можете включить его в проект с помощью Swift Package Manager или просто скопировав один файл, который входит в библиотеку.
Решение для вашего кода будет следующим:
struct DetailView: View {
var dismiss: () -> ()
var body: some View {
Text("Detail")
Button(action: dismiss) {
Text("Click to dismiss")
}
}
}
struct ContentView : View {
var body: some View {
ModalPresenter {
ModalLink(destination: DetailView.init(dismiss:)) {
Text("Click to show")
}
}
}
}
Кроме того, есть статья с полным описанием и примерами: Как представить модальное представление в SwiftUI