Come posso imitare il foglio inferiore dall'app Mappe?


202

Qualcuno può dirmi come posso imitare il foglio inferiore nella nuova app Maps in iOS 10?

In Android, puoi utilizzare un modello BottomSheetche imita questo comportamento, ma non sono riuscito a trovare nulla di simile per iOS.

È una semplice visualizzazione a scorrimento con un inserto di contenuto, in modo che la barra di ricerca sia in fondo?

Sono abbastanza nuovo nella programmazione iOS, quindi se qualcuno potesse aiutarmi a creare questo layout, sarebbe molto apprezzato.

Questo è ciò che intendo per "foglio inferiore":

screenshot del foglio inferiore compresso in Maps

screenshot del foglio inferiore espanso in Maps


1
Devi costruirlo da solo, temo. È una vista con sfondo sfocato e alcuni riconoscitori di gesti.
Khanh Nguyen,

@KhanhNguyen sì, lo so che devo costruirlo da solo, ma quello che mi chiedo è quali viste sono usate per archiviare questo effetto e come sono ordinate e così via
qwertz

È una vista ad effetto visivo con effetto sfocatura. Vedi questa domanda SO
Khanh Nguyen,

1
Penso che puoi aggiungere un riconoscitore di gesti panoramici alla vista inferiore e spostare la vista di conseguenza (osservando le modifiche memorizzando le coordinate y tra gli eventi del riconoscitore di gesti e calcolando la differenza).
Khanh Nguyen,

2
Mi piacciono le tue domande. Sto programmando di implementare qualcosa del genere. Se qualcuno potesse scrivere qualche esempio sarebbe bello.
wviana,

Risposte:


330

Non so come il foglio inferiore della nuova app Maps risponda esattamente alle interazioni dell'utente. Ma puoi creare una vista personalizzata che assomiglia a quella delle schermate e aggiungerla alla vista principale.

Presumo che tu sappia come:

1- creare controller di visualizzazione tramite storyboard o utilizzando file xib.

2- usa googleMaps o MapKit di Apple.

Esempio

1- Creare 2 controller di visualizzazione, ad es. MapViewController e BottomSheetViewController . Il primo controller ospiterà la mappa e il secondo è il foglio inferiore stesso.

Configura MapViewController

Crea un metodo per aggiungere la vista del foglio inferiore.

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

E chiamalo nel metodo viewDidAppear:

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

Configura BottomSheetViewController

1) Prepara lo sfondo

Crea un metodo per aggiungere effetti di sfocatura e vividezza

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

chiama questo metodo nel tuo viewWillAppear

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

Assicurarsi che il colore di sfondo della vista del controller sia trasparente.

2) Animare l'aspetto bottomSheet

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) Modifica il tuo xib come vuoi.

4) Aggiungi Pan Gesture Recognizer alla tua vista.

Nel tuo metodo viewDidLoad aggiungi UIPanGestureRecognizer.

override func viewDidLoad() {
    super.viewDidLoad()

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

}

E implementa il tuo comportamento gestuale:

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

Foglio inferiore scorrevole:

Se la vista personalizzata è una vista di scorrimento o qualsiasi altra vista che eredita da, quindi hai due opzioni:

Primo:

Progetta la vista con una vista dell'intestazione e aggiungi il panGesture all'intestazione. (esperienza utente negativa) .

Secondo:

1 - Aggiungi panGesture alla vista del foglio inferiore.

2 - Implementare UIGestureRecognizerDelegate e impostare il delegato panGesture sul controller.

3- Implementare shouldRecognizeSimultaneouslyWith con funzione delegata e disabilitare la proprietà isViewerScrollEnabled di scrollView in due casi:

  • La vista è parzialmente visibile.
  • La vista è totalmente visibile, la proprietà scrollView contentOffset è 0 e l'utente sta trascinando la vista verso il basso.

Altrimenti abilita lo scorrimento.

  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
  }

NOTA

Nel caso in cui imposti .allowUserInteraction come opzione di animazione, come nel progetto di esempio, quindi devi abilitare lo scorrimento alla chiusura del completamento dell'animazione se l'utente sta scorrendo verso l'alto.

Progetto di esempio

Ho creato un progetto di esempio con più opzioni su questo repository che potrebbe fornire migliori spunti su come personalizzare il flusso.

Nella demo, la funzione addBottomSheetView () controlla quale vista deve essere utilizzata come foglio inferiore.

Schermate del progetto di esempio

- Vista parziale

inserisci qui la descrizione dell'immagine

- Vista completa

inserisci qui la descrizione dell'immagine

- Vista scorrevole

inserisci qui la descrizione dell'immagine


3
Questo è in realtà un buon tutorial per childView e subview. Grazie :)
Edison,

@AhmedElassuty come posso aggiungere tableview in xib e caricarlo come foglio inferiore? Se potessi aiutare sarebbe fantastico Grazie in anticipo!
nikhil nangia,

3
@nikhilnangia Ho appena aggiornato il repository con un'altra viewController "ScrollableBottomSheetViewController.swift" che contiene tableView. Aggiornerò la risposta il prima possibile. Controlla anche questo github.com/AhmedElassuty/IOS-BottomSheet/issues/1
Ahmad Elassuty

12
Ho notato che il foglio inferiore di Apple Maps gestisce la transizione dal gesto di trascinamento del foglio al gesto di scorrimento della vista di scorrimento con un movimento regolare, vale a dire che l'utente non deve interrompere un gesto e avviarne uno nuovo per iniziare a scorrere la vista. Il tuo esempio non lo fa. Hai qualche idea su come ciò possa essere realizzato? Grazie e grazie mille per aver pubblicato l'esempio in un repository!
Stoph,

2
@ RafaelaLourenço Sono davvero felice che tu abbia trovato utile la mia risposta! Grazie!
Ahmad Elassuty,

55

Ho pubblicato una libreria basata sulla mia risposta di seguito.

Imita la sovrapposizione dell'applicazione Scorciatoie. Vedi questo articolo per i dettagli.

Il componente principale della libreria è il OverlayContainerViewController. Definisce un'area in cui un controller di visualizzazione può essere trascinato su e giù, nascondendo o rivelando il contenuto sottostante.

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

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

window?.rootViewController = containerController

Attuare OverlayContainerViewControllerDelegateper specificare il numero di tacche desiderate:

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

Risposta precedente

Penso che ci sia un punto significativo che non viene trattato nelle soluzioni suggerite: la transizione tra lo scroll e la traduzione.

Transizione delle mappe tra scorrimento e traduzione

In Maps, come avrai notato, quando tableView raggiunge contentOffset.y == 0, il foglio inferiore scorre verso l'alto o verso il basso.

Il punto è complicato perché non possiamo semplicemente abilitare / disabilitare lo scorrimento quando il nostro gesto di panoramica inizia la traduzione. Arresterebbe la pergamena fino all'inizio di un nuovo tocco. Questo è il caso nella maggior parte delle soluzioni proposte qui.

Ecco il mio tentativo di attuare questo movimento.

Punto di partenza: app Maps

Per iniziare la nostra indagine, cerchiamo di visualizzare la vista gerarchia di Maps (Mappe avviare su un simulatore e selezionare Debug> Attach to process by PID or Name> Mapsin Xcode 9).

Gerarchia della vista di debug di Maps

Non dice come funziona il movimento, ma mi ha aiutato a comprenderne la logica. Puoi giocare con lldb e il debugger della gerarchia di visualizzazione.

Il nostro controller di visualizzazione si accumula

Creiamo una versione di base dell'architettura Maps ViewController.

Iniziamo con un BackgroundViewController(la nostra vista sulla mappa):

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

Mettiamo il tableView in un apposito UIViewController:

class OverlayViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    lazy var tableView = UITableView()

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

    [...]
}

Ora, abbiamo bisogno di un VC per incorporare l'overlay e gestirne la traduzione. Per semplificare il problema, riteniamo che possa tradurre l'overlay da un punto statico OverlayPosition.maximuma un altro OverlayPosition.minimum.

Per ora ha solo un metodo pubblico per animare il cambio di posizione e ha una vista trasparente:

enum OverlayPosition {
    case maximum, minimum
}

class OverlayContainerViewController: UIViewController {

    let overlayViewController: OverlayViewController
    var translatedViewHeightContraint = ...

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

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

Finalmente abbiamo bisogno di un ViewController per incorporare il tutto:

class StackViewController: UIViewController {

    private var viewControllers: [UIViewController]

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

Nel nostro AppDelegate, la nostra sequenza di avvio è simile a:

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

La difficoltà dietro la traduzione overlay

Ora, come tradurre il nostro overlay?

La maggior parte delle soluzioni proposte utilizza un riconoscitore di gesti di panoramica dedicato, ma ne abbiamo già uno: il gesto di panoramica della vista tabella. Inoltre, dobbiamo mantenere sincronizzati lo scorrimento e la traduzione e UIScrollViewDelegatetutti gli eventi di cui abbiamo bisogno!

Un'implementazione ingenua userebbe un secondo gesto Pan e proverebbe a reimpostare la contentOffsetvista della tabella quando si verifica la traduzione:

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

Ma non funziona TableView si aggiorna contentOffsetquando si attiva la propria azione di riconoscimento dei gesti di pan o quando viene chiamato il callback displayLink. Non vi è alcuna possibilità che il nostro riconoscitore si attivi subito dopo quelli per sovrascrivere correttamente contentOffset. La nostra unica possibilità è quella di prendere parte alla fase del layout (sovrascrivendo layoutSubviewsle chiamate della vista di scorrimento in ciascun frame della vista di scorrimento) o di rispondere al didScrollmetodo del delegato chiamato ogni volta che contentOffsetviene modificato. Proviamo questo.

L'implementazione della traduzione

Aggiungiamo un delegato al nostro OverlayVCper inviare gli eventi dello scrollview al nostro gestore di traduzione, il 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)
    }
}

Nel nostro contenitore, teniamo traccia della traduzione usando un enum:

enum OverlayInFlightPosition {
    case minimum
    case maximum
    case progressing
}

Il calcolo della posizione corrente è simile a:

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

Abbiamo bisogno di 3 metodi per gestire la traduzione:

Il primo ci dice se dobbiamo iniziare la traduzione.

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

Il secondo esegue la traduzione. Utilizza il translation(in:)metodo del gesto panoramica di 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)
    )
}

Il terzo anima la fine della traduzione quando l'utente rilascia il dito. Calcoliamo la posizione utilizzando la velocità e la posizione corrente della vista.

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

L'implementazione delegata del nostro overlay si presenta semplicemente come:

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

Problema finale: invio dei tocchi del contenitore di overlay

La traduzione è ora abbastanza efficiente. Ma c'è ancora un problema finale: i tocchi non vengono consegnati alla nostra vista di sfondo. Sono tutti intercettati dalla vista del contenitore di overlay. Non possiamo impostarlo isUserInteractionEnabledsu falseperché disabiliterebbe anche l'interazione nella nostra vista tabella. La soluzione è quella utilizzata in modo massiccio nell'app 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
    }
}

Si rimuove dalla catena del risponditore.

In OverlayContainerViewController:

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

Risultato

Ecco il risultato:

Risultato

Puoi trovare il codice qui .

Per favore, se vedi qualche bug, fammi sapere! Nota che l'implementazione può ovviamente usare un secondo gesto di panoramica, specialmente se aggiungi un'intestazione nel tuo overlay.

Aggiornamento 23/08/18

Possiamo sostituire scrollViewDidEndDraggingcon willEndScrollingWithVelocityanziché enabling/ disablinglo scorrimento quando l'utente termina il trascinamento:

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

Possiamo usare un'animazione di primavera e consentire l'interazione dell'utente durante l'animazione per migliorare il flusso di movimento:

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

Questo approccio non è interattivo durante le fasi di animazione. Ad esempio, in Maps, posso catturare il foglio con il dito mentre si espande. Posso quindi strofinare l'animazione facendo una panoramica verso l'alto o verso il basso. Tornerà nel punto più vicino. Credo che questo possa essere risolto usando UIViewPropertyAnimator(che ha la capacità di mettere in pausa), quindi utilizzare la fractionCompleteproprietà per eseguire lo scrub.
agosto

1
Hai ragione @aust. Ho dimenticato di spingere la mia modifica precedente. Dovrebbe essere buono ora. Grazie.
GaétanZ,

Questa è un'ottima idea, tuttavia vedo subito un problema. Nel translateView(following:)metodo si calcola l'altezza in base alla traduzione, ma potrebbe iniziare da un valore diverso da 0,0 (ad esempio, la vista della tabella viene fatta scorrere un po 'e la sovrapposizione viene ingrandita quando si inizia a trascinare) . Ciò comporterebbe il salto iniziale. Potresti risolverlo con il scrollViewWillBeginDraggingcallback in cui ricorderesti l'iniziale contentOffsetdella vista della tabella e usarla nel translateView(following:)calcolo del '.
Jakub Truhlář,

1
@ GaétanZ Ero interessato a collegare il debugger anche a Maps.app su Simulator, ma viene visualizzato l'errore "Impossibile collegarsi a pid:" 9276 "" seguito da "Assicurati che" Maps "non sia già in esecuzione e <nomeutente> dispone dell'autorizzazione per esegui il debug ". - Come hai ingannato Xcode per consentire il collegamento a Maps.app?
matm

1
@matm @ GaétanZ ha funzionato per me su Xcode 10.1 / Mojave -> è sufficiente disabilitare per disabilitare System Integrity Protection (SIP). Devi: 1) riavviare il mac; 2) Tenere premuto cmd + R; 3) Dal menu selezionare Utilità quindi Terminale; 4) Nel tipo di finestra Terminale: csrutil disable && reboot. Avrai quindi tutti i poteri che il root dovrebbe normalmente darti, inclusa la possibilità di collegarti a un processo Apple.
valdyr,

18

Prova la puleggia :

Pulley è una libreria di cassetti di facile utilizzo pensata per imitare il cassetto nell'app Mappe di iOS 10. Espone una semplice API che consente di utilizzare qualsiasi sottoclasse UIViewController come contenuto del cassetto o contenuto principale.

Anteprima della puleggia

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


1
Supporta lo scorrimento nell'elenco sottostante?
Richard Lindhout,

@RichardLindhout - Dopo aver eseguito la demo sembra che supporti lo scorrimento, ma non la transizione graduale dallo spostamento del cassetto allo scorrimento dell'elenco.
Stoph,

L'esempio sembra nascondere il link legale di Apple in basso a sinistra.
Gerd Castan,

1
Domanda semplicissima, come hai ottenuto l'indicatore nella parte superiore del foglio inferiore?
Un Israfil

12

Puoi provare la mia risposta https://github.com/SCENEE/FloatingPanel . Fornisce un controller di visualizzazione contenitore per visualizzare un'interfaccia "foglio inferiore".

È facile da usare e non ti dispiace alcuna gestione del riconoscimento dei gesti! Inoltre, è possibile tenere traccia di una vista di scorrimento (o della vista fratello) in un foglio inferiore, se necessario.

Questo è un semplice esempio. Si noti che è necessario preparare un controller di visualizzazione per visualizzare i contenuti in un foglio inferiore.

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

Ecco un altro codice di esempio per visualizzare un foglio in basso per cercare una posizione come Apple Maps.

App di esempio come Apple Maps


fpc.set (contentViewController: newsVC), metodo mancante nella libreria
Faris Muhammed

1
Domanda semplicissima, come hai ottenuto l'indicatore nella parte superiore del foglio inferiore?
Un Israfil

1
@AIsrafil Penso che sia solo un UIView
ShadeToD

Penso che sia davvero una bella soluzione, ma in iOS 13 c'è un problema con la presentazione del controller di visualizzazione modale con questo pannello mobile.
ShadeToD

12

Ho scritto la mia libreria per raggiungere il comportamento previsto nell'app ios Maps. È una soluzione orientata al protocollo. Quindi non è necessario ereditare alcuna classe base invece creare un controller di foglio e configurarlo come si desidera. Supporta anche la navigazione / presentazione interna con o senza UINavigationController.

Vedi sotto il link per maggiori dettagli.

https://github.com/OfTheWolf/UBottomSheet

foglio di ubottom


QUESTA dovrebbe essere la risposta selezionata, poiché puoi ancora fare clic sul VC dietro il popup.
Jay,

Domanda semplicissima, come hai ottenuto l'indicatore nella parte superiore del foglio inferiore?
Un Israfil

1

Di recente ho creato un componente chiamato SwipeableViewcome sottoclasse di UIView, scritto in Swift 5.1. Supporta tutte e 4 le direzioni, ha diverse opzioni di personalizzazione e può animare e interpolare diversi attributi ed elementi (come vincoli di layout, colore di sfondo / tinta, trasformazione affine, canale alfa e centro di visualizzazione, tutti dimostrati con il rispettivo caso). Supporta anche il coordinamento dello scorrimento con la vista di scorrimento interna se impostata o rilevata automaticamente. Dovrebbe essere abbastanza facile e semplice da usare (spero 🙂)

Link a https://github.com/LucaIaco/SwipeableView

verifica teorica:

inserisci qui la descrizione dell'immagine

Spero che sia d'aiuto


0

Forse puoi provare la mia risposta https://github.com/AnYuan/AYPannel , ispirata a Pulley. Passaggio graduale dallo spostamento del cassetto allo scorrimento dell'elenco. Ho aggiunto un gesto di panoramica sulla vista di scorrimento del contenitore e impostato shouldRecognizeSimultaneouslyWithGestureRecognizer per restituire YES. Maggiori dettagli nel mio link github sopra. Desidero aiutare.


6
Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia. Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia.
Pirho,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.