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")
      }
    }
  }
}

enter image description here

Ответ 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")
            })
    }
}

enter image description here

Ответ 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