J'essaie d'implémenter un bouton qui présente une autre scène avec une animation "Slide from Botton".
PresentationButton ressemblait à un bon candidat, j'ai donc essayé:
import SwiftUI
struct ContentView : View {
var body: some View {
NavigationView {
PresentationButton(destination: Green().frame(width: 1000.0)) {
Text("Click")
}.navigationBarTitle(Text("Navigation"))
}
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
Group {
ContentView()
.previewDevice("iPhone X")
.colorScheme(.dark)
ContentView()
.colorScheme(.dark)
.previewDevice("iPad Pro (12.9-inch) (3rd generation)"
)
}
}
}
#endif
Je veux que la vue verte couvre tout l'écran et que le modal ne soit pas "déplaçable pour fermer".
Est-il possible d'ajouter un modificateur à PresentationButton pour le rendre plein écran et non glissable?
J'ai également essayé un bouton de navigation, mais: - il ne "glisse pas du bas" - il crée un "bouton retour" sur la vue détaillée, ce que je ne veux pas
merci!
Malheureusement, au Bêta 2 Bêta 3, ce n'est pas possible en SwiftUI pur. Vous pouvez voir que Modal
n'a pas de paramètres pour quelque chose comme UIModalPresentationStyle.fullScreen
. De même pour PresentationButton .
Je suggère de déposer un radar.
Le plus proche que vous pouvez actuellement faire est quelque chose comme:
@State var showModal: Bool = false
var body: some View {
NavigationView {
Button(action: {
self.showModal = true
}) {
Text("Tap me!")
}
}
.navigationBarTitle(Text("Navigation!"))
.overlay(self.showModal ? Color.green : nil)
}
Bien sûr, à partir de là, vous pouvez ajouter la transition que vous souhaitez dans la superposition.
Bien que mon autre réponse soit actuellement correcte, les gens veulent probablement pouvoir le faire maintenant. Nous pouvons utiliser le Environment
pour passer un contrôleur de vue aux enfants. Gist ici
struct ViewControllerHolder {
weak var value: UIViewController?
}
struct ViewControllerKey: EnvironmentKey {
static var defaultValue: ViewControllerHolder { return ViewControllerHolder(value: UIApplication.shared.windows.first?.rootViewController ) }
}
extension EnvironmentValues {
var viewController: UIViewControllerHolder {
get { return self[ViewControllerKey.self] }
set { self[ViewControllerKey.self] = newValue }
}
}
Ajouter une extension à UIViewController
extension UIViewController {
func present<Content: View>(style: UIModalPresentationStyle = .automatic, @ViewBuilder builder: () -> Content) {
// Must instantiate HostingController with some sort of view...
let toPresent = UIHostingController(rootView: AnyView(EmptyView()))
toPresent.modalPresentationStyle = style
// ... but then we can reset rootView to include the environment
toPresent.rootView = AnyView(
builder()
.environment(\.viewController, ViewControllerHolder(value: toPresent))
)
self.present(toPresent, animated: true, completion: nil)
}
}
Et chaque fois que nous en avons besoin, utilisez-le:
struct MyView: View {
@Environment(\.viewController) private var viewControllerHolder: ViewControllerHolder
private var viewController: UIViewController? {
self.viewControllerHolder.value
}
var body: some View {
Button(action: {
self.viewController?.present(style: .fullScreen) {
MyView()
}
}) {
Text("Present me!")
}
}
}
[EDIT] Bien qu'il soit préférable de faire quelque chose comme @Environment(\.viewController) var viewController: UIViewController?
cela conduit à un cycle de rétention. Par conséquent, vous devez utiliser le support.
Je luttais donc avec cela et je n'aimais pas la fonction de superposition ni la version enveloppée de ViewController car cela me donnait un bug de mémoire et je suis très nouveau sur iOS et je ne connais que SwiftUI et pas UIKit.
J'ai développé crédits ce qui suit avec juste SwiftUI qui est probablement ce que fait une superposition mais pour mes besoins, elle est beaucoup plus flexible:
struct FullscreenModalView<Presenting, Content>: View where Presenting: View, Content: View {
@Binding var isShowing: Bool
let parent: () -> Presenting
let content: () -> Content
@inlinable public init(isShowing: Binding<Bool>, parent: @escaping () -> Presenting, @ViewBuilder content: @escaping () -> Content) {
self._isShowing = isShowing
self.parent = parent
self.content = content
}
var body: some View {
GeometryReader { geometry in
ZStack {
self.parent().zIndex(0)
if self.$isShowing.wrappedValue {
self.content()
.background(Color.primary.colorInvert())
.edgesIgnoringSafeArea(.all)
.frame(width: geometry.size.width, height: geometry.size.height)
.transition(.move(Edge: .bottom))
.zIndex(1)
}
}
}
}
}
Ajout d'une extension à View
:
extension View {
func modal<Content>(isShowing: Binding<Bool>, @ViewBuilder content: @escaping () -> Content) -> some View where Content: View {
FullscreenModalView(isShowing: isShowing, parent: { self }, content: content)
}
}
Utilisation: utilisez une vue personnalisée et passez la variable showModal
en tant que Binding<Bool>
pour supprimer le modal de la vue elle-même.
struct ContentView : View {
@State private var showModal: Bool = false
var body: some View {
ZStack {
Button(action: {
withAnimation {
self.showModal.toggle()
}
}, label: {
HStack{
Image(systemName: "eye.fill")
Text("Calibrate")
}
.frame(width: 220, height: 120)
})
}
.modal(isShowing: self.$showModal, content: {
Text("Hallo")
})
}
}
J'espère que ça aide!
Salutations krjw
Cette version corrige l'erreur de compilation présente dans XCode 11.1 et garantit que le contrôleur est présenté dans le style transmis.
import SwiftUI
struct ViewControllerHolder {
weak var value: UIViewController?
}
struct ViewControllerKey: EnvironmentKey {
static var defaultValue: ViewControllerHolder {
return ViewControllerHolder(value: UIApplication.shared.windows.first?.rootViewController)
}
}
extension EnvironmentValues {
var viewController: UIViewController? {
get { return self[ViewControllerKey.self].value }
set { self[ViewControllerKey.self].value = newValue }
}
}
extension UIViewController {
func present<Content: View>(style: UIModalPresentationStyle = .automatic, @ViewBuilder builder: () -> Content) {
let toPresent = UIHostingController(rootView: AnyView(EmptyView()))
toPresent.modalPresentationStyle = style
toPresent.rootView = AnyView(
builder()
.environment(\.viewController, toPresent)
)
self.present(toPresent, animated: true, completion: nil)
}
}
Pour utiliser cette version, le code est inchangé par rapport à la version précédente.
struct MyView: View {
@Environment(\.viewController) private var viewControllerHolder: UIViewController?
private var viewController: UIViewController? {
self.viewControllerHolder.value
}
var body: some View {
Button(action: {
self.viewController?.present(style: .fullScreen) {
MyView()
}
}) {
Text("Present me!")
}
}
}