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())
}
}
モーダルビューで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")
}
}
}
}
表示されたビューを閉じる方法は次のとおりです。
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")
})
}
}
Xcode Beta 5では、これを行う別の方法は、モーダルを起動するビューで@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")
}
}
}
Xcode 11 Beta 7(これはXcodeのビルド11M392rにあります)の場合は少し異なるようです。
@Environment(\.presentationMode) var presentation
Button(action: { self.presentation.wrappedValue.dismiss() }) { Text("Dismiss") }
これを実装できます。
struct view: View {
@Environment(\.isPresented) private var isPresented
private func dismiss() {
isPresented?.value = false
}
}
Beta 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.wrappedValue.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() }
}
}
}
Xcode 11.0ベータ7では、値がラップされ、次の関数が機能します。
func dismiss() {
self.presentationMode.wrappedValue.dismiss()
}
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")
}
}
}
SwiftUIのモーダルビューは、List
またはForm
ビューで使用するまでは単純なようです。すべてのEdgeケースをラップし、モーダルビューの使用を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でモーダルビューを表示する方法
PresentationModeで環境変数を使用します。このGitHubリンクは、問題の解決に役立つ場合があります https://github.com/MannaICT13/Sheet-in-SwiftUI
これは簡単な解決策です:
struct ContentView2 : View {
@Environment (\.presentationMode) var presentationMode
var body : some View {
VStack {
Text("This is ContentView2")
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Back")
})
}
}
}
struct ContentView: View {
@State var isShowingSheet : Bool = false
var body: some View {
Button(action: {
self.isShowingSheet.toggle()
}, label: {
Text("Click Here")
}).sheet(isPresented: $isShowingSheet, content: {
ContentView2()
})
}
}
これを行う1つの方法は、モーダルプレゼンテーションと却下のための独自の修飾子を宣言することです。
extension View {
func showModal<T>(_ binding: Binding<Bool>, _ view: @escaping () -> T) -> some View where T: View {
let windowHeightOffset = (UIApplication.shared.windows.first?.frame.height ?? 600) * -1
return ZStack {
self
view().frame(maxWidth: .infinity, maxHeight: .infinity).edgesIgnoringSafeArea(.all).offset(x: 0, y: binding.wrappedValue ? 0 : windowHeightOffset)
}
}
}
次に、ビューを表示してそのビューを閉じる方法を伝えたいビューで修飾子を使用できます。ポップオーバーやシートモディファイヤのように。
struct ContentView: View {
@State var showModal = false
var body: some View {
Text("Show").foregroundColor(.blue).onTapGesture {
withAnimation(.easeIn(duration: 0.75)) {
self.showModal = true
}
}.showModal($showModal, {
Text("Dismiss").foregroundColor(.blue).onTapGesture {
withAnimation(.easeIn(duration: 0.75)) {
self.showModal = false
}
}
})
}
}
プレゼンテーションは上から全画面表示です。横から表示したい場合は、修飾子内の遷移を先頭または末尾に変更します。不透明度やスケールなど、他のトランジションも機能します。