Comment puis-je imiter la feuille inférieure de l'application Maps?


202

Quelqu'un peut-il me dire comment imiter la feuille inférieure dans la nouvelle application Maps dans iOS 10?

Dans Android, vous pouvez utiliser un BottomSheetqui imite ce comportement, mais je n'ai rien trouvé de tel pour iOS.

Est-ce une simple vue de défilement avec un encart de contenu, de sorte que la barre de recherche se trouve en bas?

Je suis assez nouveau dans la programmation iOS, donc si quelqu'un pouvait m'aider à créer cette mise en page, ce serait très apprécié.

Voici ce que j'entends par «feuille de fond»:

capture d'écran de la feuille inférieure repliée dans Maps

capture d'écran de la feuille inférieure développée dans Maps


1
Vous devez le construire vous-même, je le crains. C'est une vue avec une vue d'arrière-plan flou et certains reconnaisseurs de gestes.
Khanh Nguyen

@KhanhNguyen ouais je sais que je dois le construire moi-même, mais ce que je me demande c'est quelles vues sont utilisées pour archiver cet effet et comment elles sont ordonnées et ainsi de suite
qwertz

C'est la vue de l'effet visuel avec effet de flou. Voir cette question SO
Khanh Nguyen

1
Je pense que vous pouvez ajouter un reconnaisseur de mouvement panoramique à la vue de dessous et déplacer la vue en conséquence (en observant les changements en stockant les coordonnées y entre les événements du reconnaisseur de mouvement et en calculant la différence).
Khanh Nguyen

2
J'aime tes questions. Je prévois de mettre en œuvre quelque chose comme ça. Si quelqu'un pouvait écrire un exemple, ce serait bien.
wviana

Réponses:


330

Je ne sais pas exactement comment la feuille inférieure de la nouvelle application Maps répond aux interactions des utilisateurs. Mais vous pouvez créer une vue personnalisée qui ressemble à celle des captures d'écran et l'ajouter à la vue principale.

Je suppose que vous savez comment:

1- créer des contrôleurs de vue soit par story-boards soit en utilisant des fichiers xib.

2- Utilisez googleMaps ou MapKit d'Apple.

Exemple

1- Créez 2 contrôleurs de vue, par exemple, MapViewController et BottomSheetViewController . Le premier contrôleur hébergera la carte et le second est la feuille inférieure elle-même.

Configurer MapViewController

Créez une méthode pour ajouter la vue de feuille inférieure.

func addBottomSheetView() {
    // 1- Init bottomSheetVC
    let bottomSheetVC = BottomSheetViewController()

    // 2- Add bottomSheetVC as a child view 
    self.addChildViewController(bottomSheetVC)
    self.view.addSubview(bottomSheetVC.view)
    bottomSheetVC.didMoveToParentViewController(self)

    // 3- Adjust bottomSheet frame and initial position.
    let height = view.frame.height
    let width  = view.frame.width
    bottomSheetVC.view.frame = CGRectMake(0, self.view.frame.maxY, width, height)
}

Et appelez-le dans la méthode viewDidAppear:

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    addBottomSheetView()
}

Configurer BottomSheetViewController

1) Préparez le fond

Créez une méthode pour ajouter des effets de flou et d'éclat

func prepareBackgroundView(){
    let blurEffect = UIBlurEffect.init(style: .Dark)
    let visualEffect = UIVisualEffectView.init(effect: blurEffect)
    let bluredView = UIVisualEffectView.init(effect: blurEffect)
    bluredView.contentView.addSubview(visualEffect)

    visualEffect.frame = UIScreen.mainScreen().bounds
    bluredView.frame = UIScreen.mainScreen().bounds

    view.insertSubview(bluredView, atIndex: 0)
}

appeler cette méthode dans votre vueWillAppear

override func viewWillAppear(animated: Bool) {
   super.viewWillAppear(animated)
   prepareBackgroundView()
}

Assurez-vous que la couleur d'arrière-plan de la vue de votre contrôleur est clearColor.

2) Animer l'apparence de la feuille de fond

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    UIView.animateWithDuration(0.3) { [weak self] in
        let frame = self?.view.frame
        let yComponent = UIScreen.mainScreen().bounds.height - 200
        self?.view.frame = CGRectMake(0, yComponent, frame!.width, frame!.height)
    }
}

3) Modifiez votre xib comme vous le souhaitez.

4) Ajoutez Pan Gesture Recognizer à votre vue.

Dans votre méthode viewDidLoad, ajoutez UIPanGestureRecognizer.

override func viewDidLoad() {
    super.viewDidLoad()

    let gesture = UIPanGestureRecognizer.init(target: self, action: #selector(BottomSheetViewController.panGesture))
    view.addGestureRecognizer(gesture)

}

Et implémentez votre comportement gestuel:

func panGesture(recognizer: UIPanGestureRecognizer) {
    let translation = recognizer.translationInView(self.view)
    let y = self.view.frame.minY
    self.view.frame = CGRectMake(0, y + translation.y, view.frame.width, view.frame.height)
     recognizer.setTranslation(CGPointZero, inView: self.view)
}

Feuille inférieure défilante:

Si votre vue personnalisée est une vue de défilement ou toute autre vue héritée de, vous avez donc deux options:

Première:

Concevez la vue avec une vue d'en-tête et ajoutez le panGesture à l'en-tête. (mauvaise expérience utilisateur) .

Seconde:

1 - Ajoutez le panGesture à la vue de feuille inférieure.

2 - Implémentez UIGestureRecognizerDelegate et définissez le délégué panGesture sur le contrôleur.

3- Implémentez shouldRecognizeSimultaneousWith avec la fonction déléguée et désactivez la propriété scrollView isScrollEnabled dans deux cas:

  • La vue est partiellement visible.
  • La vue est totalement visible, la propriété scrollView contentOffset est 0 et l'utilisateur fait glisser la vue vers le bas.

Sinon, activez le défilement.

  func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
      let gesture = (gestureRecognizer as! UIPanGestureRecognizer)
      let direction = gesture.velocity(in: view).y

      let y = view.frame.minY
      if (y == fullView && tableView.contentOffset.y == 0 && direction > 0) || (y == partialView) {
          tableView.isScrollEnabled = false
      } else {
        tableView.isScrollEnabled = true
      }

      return false
  }

REMARQUE

Dans le cas où vous définissez .allowUserInteraction comme une option d'animation, comme dans l'exemple de projet, vous devez donc activer le défilement à la fermeture de la fin de l'animation si l'utilisateur fait défiler vers le haut.

Exemple de projet

J'ai créé un exemple de projet avec plus d'options sur ce dépôt qui peut vous donner de meilleures informations sur la façon de personnaliser le flux.

Dans la démo, la fonction addBottomSheetView () contrôle quelle vue doit être utilisée comme feuille inférieure.

Exemples de captures d'écran du projet

- Vue partielle

entrez la description de l'image ici

- Vue complète

entrez la description de l'image ici

- Vue déroulante

entrez la description de l'image ici


3
C'est en fait un bon tutoriel pour les childViews et les subViews. Merci :)
Edison

@AhmedElassuty comment puis-je ajouter tableview dans xib et charger en tant que feuille de fond? Si vous pouviez aider, ce serait super Merci d'avance!
nikhil nangia

3
@nikhilnangia Je viens de mettre à jour le dépôt avec un autre viewController "ScrollableBottomSheetViewController.swift" qui contient une tableView. Je mettrai à jour la réponse dès que possible. Vérifiez aussi github.com/AhmedElassuty/IOS-BottomSheet/issues/1
Ahmad Elassuty

12
J'ai remarqué que la feuille inférieure d'Apple Maps gère la transition du geste de glissement de la feuille au geste de défilement de la vue de défilement en un seul mouvement, c'est-à-dire que l'utilisateur n'a pas besoin d'arrêter un geste et d'en commencer un nouveau pour commencer à faire défiler la vue. Votre exemple ne fait pas cela. Avez-vous des idées sur la façon dont cela pourrait être accompli? Merci, et merci beaucoup pour avoir publié l'exemple dans un repo!
Stoph

2
@ RafaelaLourenço Je suis vraiment contente que vous ayez trouvé ma réponse utile! Je vous remercie!
Ahmad Elassuty

56

J'ai publié une bibliothèque basée sur ma réponse ci-dessous.

Il imite la superposition d'application des raccourcis. Consultez cet article pour plus de détails.

Le composant principal de la bibliothèque est le OverlayContainerViewController. Il définit une zone dans laquelle un contrôleur de vue peut être déplacé de haut en bas, masquant ou révélant le contenu en dessous.

let contentController = MapsViewController()
let overlayController = SearchViewController()

let containerController = OverlayContainerViewController()
containerController.delegate = self
containerController.viewControllers = [
    contentController,
    overlayController
]

window?.rootViewController = containerController

Mettre OverlayContainerViewControllerDelegateen œuvre pour spécifier le nombre d'encoches souhaité:

enum OverlayNotch: Int, CaseIterable {
    case minimum, medium, maximum
}

func numberOfNotches(in containerViewController: OverlayContainerViewController) -> Int {
    return OverlayNotch.allCases.count
}

func overlayContainerViewController(_ containerViewController: OverlayContainerViewController,
                                    heightForNotchAt index: Int,
                                    availableSpace: CGFloat) -> CGFloat {
    switch OverlayNotch.allCases[index] {
        case .maximum:
            return availableSpace * 3 / 4
        case .medium:
            return availableSpace / 2
        case .minimum:
            return availableSpace * 1 / 4
    }
}

Réponse précédente

Je pense qu'il y a un point important qui n'est pas traité dans les solutions proposées: la transition entre le parchemin et la traduction.

Transition des cartes entre le défilement et la traduction

Dans Maps, comme vous l'avez peut-être remarqué, lorsque la tableView atteint contentOffset.y == 0, la feuille inférieure glisse vers le haut ou vers le bas.

Le point est délicat car nous ne pouvons pas simplement activer / désactiver le défilement lorsque notre geste de panoramique commence la traduction. Cela arrêterait le défilement jusqu'à ce qu'une nouvelle touche commence. C'est le cas dans la plupart des solutions proposées ici.

Voici mon essai de mettre en oeuvre cette motion.

Point de départ: l'application Maps

Pour commencer notre enquête, nous allons visualiser la hiérarchie de la vue des cartes (début Maps sur un simulateur et sélectionnez Debug> Attach to process by PID or Name> Mapsdans Xcode 9).

Mappage de la hiérarchie des vues de débogage

Cela ne dit pas comment fonctionne la motion, mais cela m'a aidé à en comprendre la logique. Vous pouvez jouer avec le lldb et le débogueur de hiérarchie de vues.

Nos piles de contrôleur de vue

Créons une version de base de l'architecture Maps ViewController.

Nous commençons par un BackgroundViewController(notre vue de la carte):

class BackgroundViewController: UIViewController {
    override func loadView() {
        view = MKMapView()
    }
}

Nous mettons la tableView dans un espace dédié UIViewController:

class OverlayViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    lazy var tableView = UITableView()

    override func loadView() {
        view = tableView
        tableView.dataSource = self
        tableView.delegate = self
    }

    [...]
}

Maintenant, nous avons besoin d'un VC pour intégrer la superposition et gérer sa traduction. Pour simplifier le problème, nous considérons qu'il peut traduire la superposition d'un point statique OverlayPosition.maximumà un autre OverlayPosition.minimum.

Pour l'instant, il n'a qu'une seule méthode publique pour animer le changement de position et il a une vue transparente:

enum OverlayPosition {
    case maximum, minimum
}

class OverlayContainerViewController: UIViewController {

    let overlayViewController: OverlayViewController
    var translatedViewHeightContraint = ...

    override func loadView() {
        view = UIView()
    }

    func moveOverlay(to position: OverlayPosition) {
        [...]
    }
}

Enfin, nous avons besoin d'un ViewController pour intégrer le tout:

class StackViewController: UIViewController {

    private var viewControllers: [UIViewController]

    override func viewDidLoad() {
        super.viewDidLoad()
        viewControllers.forEach { gz_addChild($0, in: view) }
    }
}

Dans notre AppDelegate, notre séquence de démarrage ressemble à:

let overlay = OverlayViewController()
let containerViewController = OverlayContainerViewController(overlayViewController: overlay)
let backgroundViewController = BackgroundViewController()
window?.rootViewController = StackViewController(viewControllers: [backgroundViewController, containerViewController])

La difficulté derrière la traduction de superposition

Maintenant, comment traduire notre superposition?

La plupart des solutions proposées utilisent un outil de reconnaissance des mouvements panoramiques dédié, mais nous en avons déjà un: le mouvement panoramique de la vue tableau. De plus, nous devons garder le défilement et la traduction synchronisés et UIScrollViewDelegatetous les événements dont nous avons besoin!

Une implémentation naïve utiliserait un deuxième mouvement panoramique et tenterait de réinitialiser la vue contentOffsetde la table lorsque la traduction se produit:

func panGestureAction(_ recognizer: UIPanGestureRecognizer) {
    if isTranslating {
        tableView.contentOffset = .zero
    }
}

Mais ça ne marche pas. La tableView met à jour son contentOffsetlorsque sa propre action de reconnaissance de mouvement panoramique se déclenche ou lorsque son rappel displayLink est appelé. Il n'y a aucune chance que notre module de reconnaissance se déclenche juste après ceux pour réussir à remplacer le contentOffset. Notre seule chance est soit de participer à la phase de mise en page (en remplaçant layoutSubviewsles appels de la vue de défilement à chaque image de la vue de défilement), soit de répondre à la didScrollméthode du délégué appelé à chaque contentOffsetmodification de la. Essayons celui-ci.

L'implémentation de la traduction

Nous ajoutons un délégué à notre OverlayVCpour envoyer les événements de scrollview à notre gestionnaire de traduction, le OverlayContainerViewController:

protocol OverlayViewControllerDelegate: class {
    func scrollViewDidScroll(_ scrollView: UIScrollView)
    func scrollViewDidStopScrolling(_ scrollView: UIScrollView)
}

class OverlayViewController: UIViewController {

    [...]

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        delegate?.scrollViewDidScroll(scrollView)
    }

    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        delegate?.scrollViewDidStopScrolling(scrollView)
    }
}

Dans notre conteneur, nous suivons la traduction à l'aide d'une énumération:

enum OverlayInFlightPosition {
    case minimum
    case maximum
    case progressing
}

Le calcul de la position actuelle ressemble à ceci:

private var overlayInFlightPosition: OverlayInFlightPosition {
    let height = translatedViewHeightContraint.constant
    if height == maximumHeight {
        return .maximum
    } else if height == minimumHeight {
        return .minimum
    } else {
        return .progressing
    }
}

Nous avons besoin de 3 méthodes pour gérer la traduction:

Le premier nous indique si nous devons commencer la traduction.

private func shouldTranslateView(following scrollView: UIScrollView) -> Bool {
    guard scrollView.isTracking else { return false }
    let offset = scrollView.contentOffset.y
    switch overlayInFlightPosition {
    case .maximum:
        return offset < 0
    case .minimum:
        return offset > 0
    case .progressing:
        return true
    }
}

Le second effectue la traduction. Il utilise la translation(in:)méthode du mouvement panoramique du scrollView.

private func translateView(following scrollView: UIScrollView) {
    scrollView.contentOffset = .zero
    let translation = translatedViewTargetHeight - scrollView.panGestureRecognizer.translation(in: view).y
    translatedViewHeightContraint.constant = max(
        Constant.minimumHeight,
        min(translation, Constant.maximumHeight)
    )
}

Le troisième anime la fin de la traduction lorsque l'utilisateur relâche son doigt. Nous calculons la position en utilisant la vitesse et la position actuelle de la vue.

private func animateTranslationEnd() {
    let position: OverlayPosition =  // ... calculation based on the current overlay position & velocity
    moveOverlay(to: position)
}

L'implémentation des délégués de notre superposition ressemble simplement à:

class OverlayContainerViewController: UIViewController {

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard shouldTranslateView(following: scrollView) else { return }
        translateView(following: scrollView)
    }

    func scrollViewDidStopScrolling(_ scrollView: UIScrollView) {
        // prevent scroll animation when the translation animation ends
        scrollView.isEnabled = false
        scrollView.isEnabled = true
        animateTranslationEnd()
    }
}

Dernier problème: répartir les touches du conteneur de superposition

La traduction est maintenant assez efficace. Mais il y a encore un dernier problème: les touches ne sont pas apportées à notre vue d'arrière-plan. Ils sont tous interceptés par la vue du conteneur de superposition. Nous ne pouvons pas mettre isUserInteractionEnabledà falseparce qu'il désactiverait aussi l'interaction dans notre présentation de tableau. La solution est celle qui est utilisée massivement dans l'application Maps, PassThroughView:

class PassThroughView: UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)
        if view == self {
            return nil
        }
        return view
    }
}

Il se supprime de la chaîne répondeur.

Dans OverlayContainerViewController:

override func loadView() {
    view = PassThroughView()
}

Résultat

Voici le résultat:

Résultat

Vous pouvez trouver le code ici .

S'il vous plaît, si vous voyez des bugs, faites le moi savoir! Notez que votre implémentation peut bien sûr utiliser un deuxième geste de panoramique, spécialement si vous ajoutez un en-tête dans votre superposition.

Mise à jour 23/08/18

Nous pouvons remplacer scrollViewDidEndDraggingpar willEndScrollingWithVelocityplutôt que enabling/ disablingle défilement lorsque l'utilisateur termine de glisser:

func scrollView(_ scrollView: UIScrollView,
                willEndScrollingWithVelocity velocity: CGPoint,
                targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    switch overlayInFlightPosition {
    case .maximum:
        break
    case .minimum, .progressing:
        targetContentOffset.pointee = .zero
    }
    animateTranslationEnd(following: scrollView)
}

Nous pouvons utiliser une animation de ressort et permettre l'interaction de l'utilisateur pendant l'animation pour améliorer le flux de mouvement:

func moveOverlay(to position: OverlayPosition,
                 duration: TimeInterval,
                 velocity: CGPoint) {
    overlayPosition = position
    translatedViewHeightContraint.constant = translatedViewTargetHeight
    UIView.animate(
        withDuration: duration,
        delay: 0,
        usingSpringWithDamping: velocity.y == 0 ? 1 : 0.6,
        initialSpringVelocity: abs(velocity.y),
        options: [.allowUserInteraction],
        animations: {
            self.view.layoutIfNeeded()
    }, completion: nil)
}

Cette approche n'est pas interactive lors des étapes d'animation. Par exemple, dans Maps, je peux attraper la feuille avec mon doigt pendant son expansion. Je peux ensuite parcourir l'animation en effectuant un panoramique vers le haut ou vers le bas. Il reviendra à l'endroit le plus proche. Je crois que cela peut être résolu en utilisant UIViewPropertyAnimator(qui a la capacité de faire une pause), puis utilisez la fractionCompletepropriété pour effectuer le gommage.
Aust

1
Vous avez raison @aust. J'ai oublié de pousser mon changement précédent. Ça devrait être bien maintenant. Merci.
GaétanZ

C'est une excellente idée, mais je vois tout de suite un problème. Dans la translateView(following:)méthode, vous calculez la hauteur en fonction de la traduction, mais cela peut commencer à partir d'une valeur différente de 0,0 (par exemple, la vue de la table défile un peu et la superposition est maximisée lorsque vous commencez à faire glisser) . Cela entraînerait le saut initial. Vous pouvez résoudre ce problème par le scrollViewWillBeginDraggingrappel où vous vous souviendrez de l'initiale contentOffsetde la vue de table et l'utiliserez dans le translateView(following:)calcul de.
Jakub Truhlář

1
@ GaétanZ Je souhaitais également attacher le débogueur à Maps.app sur le simulateur, mais j'obtiens une erreur "Impossible de joindre au pid:" 9276 "" suivi de "Assurez-vous que" Maps "n'est pas déjà en cours d'exécution et <nomutilisateur> a l'autorisation de le déboguer. " - Comment avez-vous trompé Xcode pour permettre l'attachement à Maps.app?
matm

1
@matm @ GaétanZ a travaillé pour moi sur Xcode 10.1 / Mojave -> il suffit de désactiver pour désactiver la protection de l'intégrité du système (SIP). Vous devez: 1) redémarrer votre Mac; 2) Maintenez cmd + R; 3) Dans le menu, sélectionnez Utilitaires puis Terminal; 4) Dans le type de fenêtre de terminal: csrutil disable && reboot. Vous aurez alors tous les pouvoirs que root devrait normalement vous donner, y compris la possibilité de vous attacher à un processus Apple.
valdyr

18

Essayez Poulie :

Pulley est une bibliothèque de tiroirs facile à utiliser destinée à imiter le tiroir dans l'application Maps d'iOS 10. Il expose une API simple qui vous permet d'utiliser n'importe quelle sous-classe UIViewController comme contenu de tiroir ou contenu principal.

Aperçu de la poulie

https://github.com/52inc/Pulley


1
Prend-il en charge le défilement dans la liste sous-jacente?
Richard Lindhout

@RichardLindhout - Après avoir exécuté la démo, il semble qu'il prend en charge le défilement, mais pas la transition en douceur du déplacement du tiroir au défilement de la liste.
Stoph

L'exemple semble masquer le lien juridique Apples en bas à gauche.
Gerd Castan

1
Question super simple, comment avez-vous obtenu l'indicateur en haut de la feuille du bas?
Un Israfil

12

Vous pouvez essayer ma réponse https://github.com/SCENEE/FloatingPanel . Il fournit un contrôleur de vue de conteneur pour afficher une interface "feuille de fond".

Il est facile à utiliser et cela ne vous dérange pas de la manipulation de reconnaissance de gestes! Vous pouvez également suivre une vue de défilement (ou la vue frère) dans une feuille inférieure si nécessaire.

Ceci est un exemple simple. Veuillez noter que vous devez préparer un contrôleur de vue pour afficher votre contenu dans une feuille inférieure.

import UIKit
import FloatingPanel

class ViewController: UIViewController {
    var fpc: FloatingPanelController!

    override func viewDidLoad() {
        super.viewDidLoad()

        fpc = FloatingPanelController()

        // Add "bottom sheet" in self.view.
        fpc.add(toParent: self)

        // Add a view controller to display your contents in "bottom sheet".
        let contentVC = ContentViewController()
        fpc.set(contentViewController: contentVC)

        // Track a scroll view in "bottom sheet" content if needed.
        fpc.track(scrollView: contentVC.tableView)    
    }
    ...
}

Voici un autre exemple de code pour afficher une feuille inférieure pour rechercher un emplacement comme Apple Maps.

Exemple d'application comme Apple Maps


fpc.set (contentViewController: newsVC), méthode manquante dans la bibliothèque
Faris Muhammed

1
Question super simple, comment avez-vous obtenu l'indicateur en haut de la feuille du bas?
Un Israfil

1
@AIsrafil Je pense que c'est juste une UIView
ShadeToD

Je pense que c'est une très bonne solution, mais il y a un problème dans iOS 13 avec la présentation du contrôleur de vue modale avec ce panneau flottant.
ShadeToD

12

J'ai écrit ma propre bibliothèque pour obtenir le comportement souhaité dans l'application ios Maps. Il s'agit d'une solution orientée protocole. Vous n'avez donc pas besoin d'hériter d'une classe de base à la place, créez un contrôleur de feuille et configurez comme vous le souhaitez. Il prend également en charge la navigation / présentation interne avec ou sans UINavigationController.

Voir le lien ci-dessous pour plus de détails.

https://github.com/OfTheWolf/UBottomSheet

feuille de fond


CECI devrait être la réponse choisie, car vous pouvez toujours cliquer sur le VC derrière le popup.
Jay

Question super simple, comment avez-vous obtenu l'indicateur en haut de la feuille du bas?
Un Israfil

1

J'ai récemment créé un composant appelé SwipeableViewsous-classe de UIView, écrit en Swift 5.1. Il prend en charge les 4 directions, dispose de plusieurs options de personnalisation et peut animer et interpoler différents attributs et éléments (tels que les contraintes de mise en page, la couleur d'arrière-plan / de teinte, la transformation affine, le canal alpha et le centre de vue, tous démontrés avec la vitrine respective). Il prend également en charge la coordination de balayage avec la vue de défilement interne si elle est définie ou détectée automatiquement. Devrait être assez facile et simple à utiliser (j'espère 🙂)

Lien sur https://github.com/LucaIaco/SwipeableView

preuve de concept:

entrez la description de l'image ici

J'espère que ça aide


0

Vous pouvez peut-être essayer ma réponse https://github.com/AnYuan/AYPannel , inspirée de Pulley. Transition en douceur du déplacement du tiroir au défilement de la liste. J'ai ajouté un geste de panoramique sur la vue de défilement du conteneur et j'ai défini shouldRecognizeSimultaneousWithGestureRecognizer pour retourner YES. Plus de détails dans mon lien github ci-dessus. Je souhaite aider.


6
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien de référence. Les réponses de lien uniquement peuvent devenir invalides si la page liée change. Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien de référence. Les réponses de lien uniquement peuvent devenir invalides si la page liée change.
pirho
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.