web-dev-qa-db-ja.com

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())
  }
}
42
Ugo Arangino

モーダルビューで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

表示されたビューを閉じる方法は次のとおりです。

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

14
SMP

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

Xcode 11 Beta 7(これはXcodeのビルド11M392rにあります)の場合は少し異なるようです。

@Environment(\.presentationMode) var presentation


Button(action: { self.presentation.wrappedValue.dismiss() }) { Text("Dismiss") }
8
Tomm P

これを実装できます。

struct view: View {
    @Environment(\.isPresented) private var isPresented

    private func dismiss() {
        isPresented?.value = false
    }
}
7
iOSCS

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() }
        }
    }
}
6
Chuck H

Xcode 11.0ベータ7では、値がラップされ、次の関数が機能します。

func dismiss() {
    self.presentationMode.wrappedValue.dismiss()
}
2
Gareth Jones

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")
        }
    }
}
2
Ugo Arangino

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でモーダルビューを表示する方法

1
Denis

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()
        })
    }
}
0
MannaICT13

これを行う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
        }
      }

    })


  }
}    

プレゼンテーションは上から全画面表示です。横から表示したい場合は、修飾子内の遷移を先頭または末尾に変更します。不透明度やスケールなど、他のトランジションも機能します。

enter image description here

0
jnblanchard