web-dev-qa-db-fra.com

SwiftUI rejette le modal

Puisque SwiftUI est déclaratif, il n'y a pas de méthode dismiss. Comment peut-on ajouter un bouton de fermeture/fermeture au 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

Vous pouvez utiliser la variable d'environnement presentationMode dans votre vue modale et appeler self.presentaionMode.wrappedValue.dismiss() pour fermer le modal:

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

58

Voici un moyen de rejeter la vue présentée.

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

Dans Xcode Beta 5, une autre façon de procéder consiste à utiliser @State dans la vue qui lance le modal et à ajouter une liaison dans la vue modale pour contrôler la visibilité du modal. Cela ne vous oblige pas à accéder à la variable @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

Il semble que pour Xcode 11 Beta 7 (il s'agit de la version 11M392r de Xcode), c'est légèrement différent.

@Environment(\.presentationMode) var presentation


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

Vous pouvez l'implémenter.

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

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

Il existe désormais un moyen assez propre de le faire dans la version bêta 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

Dans Xcode 11.0 beta 7, la valeur est maintenant encapsulée, la fonction suivante fonctionne pour moi:

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

Puisque PresentationButton est facile à utiliser mais en masquant l'état qui mine le caractère prédictif de SwiftUI je l'ai implémenté avec un Binding accessible.

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?()
    }
}

Voici comment il est utilisé:

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

Les vues modales dans SwiftUI semblent simples jusqu'à ce que vous commenciez à les utiliser dans une vue List ou Form. J'ai créé une petite bibliothèque qui enveloppe tous les cas Edge et rend l'utilisation des vues modales identique à la paire NavigationView-NavigationLink.

La bibliothèque est open source ici: https://github.com/diniska/modal-view . Vous pouvez l'inclure dans le projet en utilisant Swift Package Manager, ou simplement en copiant le fichier unique inclus dans la bibliothèque.

La solution pour votre code serait:

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

De plus, il y a un article avec une description complète et des exemples: Comment présenter la vue modale dans SwiftUI

1
Denis

Utilisez la variable d'environnement à PresentationMode. Ce lien GitHub vous aidera peut-être à résoudre le problème https://github.com/MannaICT13/Sheet-in-SwiftUI

C'est une solution simple:

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

Une façon de le faire pourrait être de déclarer votre propre modificateur pour la présentation modale et le rejet.

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)

    }

  }
}

Ensuite, vous pouvez utiliser le modificateur sur n'importe quelle vue pour laquelle vous souhaitez indiquer comment afficher une vue et la fermer. Tout comme un popover ou un modificateur de feuille.

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

    })


  }
}    

La présentation est en plein écran par le haut, si vous souhaitez qu'elle vienne de côté, changez la transition à l'intérieur du modificateur en début ou en fin. D'autres transitions fonctionneraient aussi, comme l'opacité ou l'échelle.

enter image description here

0
jnblanchard