Wie kann ich das untere Blatt der Karten-App nachahmen?

201

Kann mir jemand sagen, wie ich das untere Blatt in der neuen Maps-App in iOS 10 nachahmen kann?

In Android können Sie ein verwenden BottomSheet, das dieses Verhalten nachahmt, aber ich konnte so etwas für iOS nicht finden.

Ist das eine einfache Bildlaufansicht mit einem eingefügten Inhalt, sodass sich die Suchleiste unten befindet?

Ich bin ziemlich neu in der iOS-Programmierung. Wenn mir jemand beim Erstellen dieses Layouts helfen könnte, wäre das sehr dankbar.

Das meine ich mit "unteres Blatt":

Screenshot des reduzierten unteren Blattes in Karten

Screenshot des erweiterten unteren Blattes in Karten

qwertz
quelle
1
Du musst es leider selbst bauen. Es ist eine Ansicht mit unscharfer Hintergrundansicht und einigen Gestenerkennern.
Khanh Nguyen
@ KhanhNguyen Ja, ich weiß, ich muss es selbst erstellen, aber ich frage mich, welche Ansichten verwendet werden, um diesen Effekt zu erzielen und wie sie bestellt werden und so weiter
qwertz
Es ist eine visuelle Effektansicht mit Unschärfeeffekt. Siehe diese SO Frage
Khanh Nguyen
1
Ich denke, Sie können der Unteransicht einen Pan-Gestenerkenner hinzufügen und die Ansicht entsprechend verschieben (indem Sie die Änderungen beobachten, indem Sie die y-Koordinaten zwischen den Ereignissen des Gestenerkenners speichern und die Differenz berechnen).
Khanh Nguyen
2
Ich mag deine Fragen. Ich plane so etwas zu implementieren. Wenn jemand ein Beispiel schreiben könnte, wäre das nett.
Wviana

Antworten:

327

Ich weiß nicht, wie genau das untere Blatt der neuen Maps-App auf Benutzerinteraktionen reagiert. Sie können jedoch eine benutzerdefinierte Ansicht erstellen, die der in den Screenshots entspricht, und sie der Hauptansicht hinzufügen.

Ich nehme an, Sie wissen wie:

1- Erstellen Sie View Controller entweder über Storyboards oder mithilfe von XIB-Dateien.

2- Verwenden Sie googleMaps oder Apples MapKit.

Beispiel

1- Erstellen Sie zwei Ansichts-Controller, z. B. MapViewController und BottomSheetViewController . Der erste Controller hostet die Karte und der zweite ist das unterste Blatt selbst.

Konfigurieren Sie MapViewController

Erstellen Sie eine Methode zum Hinzufügen der unteren Blattansicht.

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

Und rufen Sie es in der viewDidAppear-Methode auf:

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

Konfigurieren Sie BottomSheetViewController

1) Bereiten Sie den Hintergrund vor

Erstellen Sie eine Methode, um Unschärfe- und Lebendigkeitseffekte hinzuzufügen

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

Rufen Sie diese Methode in Ihrem viewWillAppear auf

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

Stellen Sie sicher, dass die Hintergrundfarbe der Ansicht Ihres Controllers clearColor ist.

2) Animieren Sie das Erscheinungsbild des unteren Blattes

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) Ändern Sie Ihre xib nach Ihren Wünschen.

4) Fügen Sie Ihrer Ansicht Pan Gesture Recognizer hinzu.

Fügen Sie in Ihrer viewDidLoad-Methode UIPanGestureRecognizer hinzu.

override func viewDidLoad() {
    super.viewDidLoad()

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

}

Und implementieren Sie Ihr Gestenverhalten:

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

Bildlauffähiges unteres Blatt:

Wenn Ihre benutzerdefinierte Ansicht eine Bildlaufansicht oder eine andere Ansicht ist, von der geerbt wird, haben Sie zwei Möglichkeiten:

Zuerst:

Entwerfen Sie die Ansicht mit einer Kopfzeilenansicht und fügen Sie die panGesture zur Kopfzeile hinzu. (schlechte Benutzererfahrung) .

Zweite:

1 - Fügen Sie die panGesture zur unteren Blattansicht hinzu.

2 - Implementieren Sie das UIGestureRecognizerDelegate und legen Sie den panGesture-Delegaten für den Controller fest.

3- Implementieren Sie shouldRecognizeSimultaneousWith delegate function und deaktivieren Sie die Eigenschaft scrollView isScrollEnabled in zwei Fällen:

  • Die Ansicht ist teilweise sichtbar.
  • Die Ansicht ist vollständig sichtbar, die Eigenschaft scrollView contentOffset ist 0 und der Benutzer zieht die Ansicht nach unten.

Andernfalls aktivieren Sie das Scrollen.

  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
  }

HINWEIS

Wenn Sie .allowUserInteraction wie im Beispielprojekt als Animationsoption festlegen , müssen Sie das Scrollen beim Schließen der Animation aktivieren, wenn der Benutzer nach oben scrollt.

Beispielprojekt

Ich habe ein Beispielprojekt mit mehr Optionen für dieses Repo erstellt, das Ihnen möglicherweise bessere Einblicke in die Anpassung des Ablaufs gibt.

In der Demo steuert die Funktion addBottomSheetView (), welche Ansicht als unteres Blatt verwendet werden soll.

Beispielprojekt-Screenshots

- Teilansicht

Geben Sie hier die Bildbeschreibung ein

- Vollansicht

Geben Sie hier die Bildbeschreibung ein

- Bildlaufansicht

Geben Sie hier die Bildbeschreibung ein

Ahmad Elassuty
quelle
3
Dies ist eigentlich ein gutes Tutorial für childViews und subViews. Vielen Dank :)
Edison
@AhmedElassuty Wie kann ich eine Tabellenansicht in xib hinzufügen und als unteres Blatt laden? Wenn Sie helfen könnten, wäre das großartig. Vielen Dank im Voraus!
Nikil Nangia
3
@nikhilnangia Ich habe gerade das Repo mit einem anderen viewController "ScrollableBottomSheetViewController.swift" aktualisiert, der eine tableView enthält. Ich werde die Antwort so schnell wie möglich aktualisieren. Überprüfen Sie dies auch github.com/AhmedElassuty/IOS-BottomSheet/issues/1
Ahmad Elassuty
12
Ich habe festgestellt, dass das untere Blatt von Apple Maps den Übergang von der Geste zum Ziehen des Blatts zur Bildlaufgeste zur Bildlaufansicht in einer sanften Bewegung handhabt, dh der Benutzer muss nicht eine Geste stoppen und eine neue starten, um mit dem Bildlauf in der Ansicht zu beginnen. Ihr Beispiel tut dies nicht. Haben Sie irgendwelche Gedanken darüber, wie dies erreicht werden könnte? Danke und vielen Dank, dass Sie das Beispiel in einem Repo veröffentlicht haben!
Stoph
2
@ RafaelaLourenço Ich freue mich sehr, dass Sie meine Antwort hilfreich fanden! Danke dir!
Ahmad Elassuty
55

Ich habe eine Bibliothek basierend auf meiner Antwort unten veröffentlicht.

Es ahmt das Overlay der Shortcuts-Anwendung nach. Weitere Informationen finden Sie in diesem Artikel .

Die Hauptkomponente der Bibliothek ist die OverlayContainerViewController. Es definiert einen Bereich, in dem ein Ansichts-Controller nach oben und unten gezogen werden kann, um den darunter liegenden Inhalt auszublenden oder anzuzeigen.

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

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

window?.rootViewController = containerController

Implementieren Sie OverlayContainerViewControllerDelegate, um die Anzahl der gewünschten Kerben anzugeben:

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

Vorherige Antwort

Ich denke, es gibt einen wichtigen Punkt, der in den vorgeschlagenen Lösungen nicht behandelt wird: den Übergang zwischen der Schriftrolle und der Übersetzung.

Ordnet den Übergang zwischen der Schriftrolle und der Übersetzung zu

Wie Sie vielleicht bemerkt haben, gleitet contentOffset.y == 0das untere Blatt in Karten beim Erreichen der Tabellenansicht entweder nach oben oder nach unten.

Der Punkt ist schwierig, da wir den Bildlauf nicht einfach aktivieren / deaktivieren können, wenn unsere Schwenkgeste mit der Übersetzung beginnt. Es würde die Schriftrolle stoppen, bis eine neue Berührung beginnt. Dies ist bei den meisten hier vorgeschlagenen Lösungen der Fall.

Hier ist mein Versuch, diesen Antrag umzusetzen.

Ausgangspunkt: Karten App

Um unsere Untersuchung zu beginnen, lassen Sie uns die Ansicht Hierarchie der Karten zu visualisieren (Start Maps auf einem Simulator und wählen Sie Debug> Attach to process by PID or Name> Mapsin Xcode 9).

Ordnet die Hierarchie der Debug-Ansicht zu

Es sagt nicht, wie die Bewegung funktioniert, aber es hat mir geholfen, die Logik zu verstehen. Sie können mit dem lldb- und dem Ansichtshierarchie-Debugger spielen.

Unser View Controller stapelt sich

Erstellen wir eine Basisversion der Maps ViewController-Architektur.

Wir beginnen mit einem BackgroundViewController(unserer Kartenansicht):

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

Wir haben die tableView in eine dedizierte UIViewController:

class OverlayViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    lazy var tableView = UITableView()

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

    [...]
}

Jetzt benötigen wir einen VC, um das Overlay einzubetten und seine Übersetzung zu verwalten. Um das Problem zu vereinfachen, können wir die Überlagerung von einem statischen Punkt OverlayPosition.maximumauf einen anderen verschieben OverlayPosition.minimum.

Derzeit gibt es nur eine öffentliche Methode zum Animieren der Positionsänderung und eine transparente Ansicht:

enum OverlayPosition {
    case maximum, minimum
}

class OverlayContainerViewController: UIViewController {

    let overlayViewController: OverlayViewController
    var translatedViewHeightContraint = ...

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

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

Schließlich benötigen wir einen ViewController, um alles einzubetten:

class StackViewController: UIViewController {

    private var viewControllers: [UIViewController]

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

In unserem AppDelegate sieht unsere Startsequenz folgendermaßen aus:

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

Die Schwierigkeit hinter der Overlay-Übersetzung

Wie übersetzen wir nun unser Overlay?

Die meisten der vorgeschlagenen Lösungen verwenden einen dedizierten Pan-Gesten-Erkenner, aber wir haben bereits einen: die Pan-Geste der Tabellenansicht. Außerdem müssen wir die Schriftrolle und die Übersetzung synchron halten und UIScrollViewDelegatealle Ereignisse haben, die wir brauchen!

Eine naive Implementierung würde eine zweite Schwenkgeste verwenden und versuchen, contentOffsetdie Tabellenansicht zurückzusetzen, wenn die Übersetzung erfolgt:

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

Aber es funktioniert nicht. Die tableView wird aktualisiert, contentOffsetwenn eine eigene Pan-Gestenerkennungsaktion ausgelöst wird oder wenn der displayLink-Rückruf aufgerufen wird. Es besteht keine Chance, dass unser Erkenner direkt nach diesen ausgelöst wird, um das erfolgreich zu überschreiben contentOffset. Unsere einzige Möglichkeit besteht darin, entweder an der Layoutphase teilzunehmen (indem layoutSubviewsdie Aufrufe der Bildlaufansicht in jedem Frame der Bildlaufansicht überschrieben werden ) oder auf die didScrollMethode des Delegaten zu reagieren, die bei jeder contentOffsetÄnderung aufgerufen wird. Lass es uns versuchen.

Die Übersetzung Implementierung

Wir fügen OverlayVCunserem einen Übersetzer hinzu, um die Ereignisse der Bildlaufansicht an unseren Übersetzer zu senden 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)
    }
}

In unserem Container verfolgen wir die Übersetzung anhand einer Aufzählung:

enum OverlayInFlightPosition {
    case minimum
    case maximum
    case progressing
}

Die aktuelle Positionsberechnung sieht folgendermaßen aus:

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

Wir benötigen 3 Methoden, um die Übersetzung zu handhaben:

Der erste sagt uns, ob wir mit der Übersetzung beginnen müssen.

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

Der zweite führt die Übersetzung durch. Es verwendet die translation(in:)Methode der Schwenkgeste von 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)
    )
}

Der dritte animiert das Ende der Übersetzung, wenn der Benutzer seinen Finger loslässt. Wir berechnen die Position anhand der Geschwindigkeit und der aktuellen Position der Ansicht.

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

Die Delegiertenimplementierung unseres Overlays sieht einfach so aus:

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

Letztes Problem: Versenden der Berührungen des Overlay-Containers

Die Übersetzung ist jetzt ziemlich effizient. Es gibt jedoch noch ein letztes Problem: Die Berührungen werden nicht in unsere Hintergrundansicht übertragen. Sie werden alle von der Ansicht des Overlay-Containers abgefangen. Wir können nicht festlegen isUserInteractionEnabled, falseda dies auch die Interaktion in unserer Tabellenansicht deaktivieren würde. Die Lösung wird massiv in der Maps-App verwendet 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
    }
}

Es entfernt sich von der Responderkette.

In OverlayContainerViewController:

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

Ergebnis

Hier ist das Ergebnis:

Ergebnis

Den Code finden Sie hier .

Bitte, wenn Sie irgendwelche Fehler sehen, lassen Sie es mich wissen! Beachten Sie, dass Ihre Implementierung natürlich eine zweite Schwenkgeste verwenden kann, insbesondere wenn Sie Ihrem Overlay einen Header hinzufügen.

Update 23/08/18

Wir können ersetzen scrollViewDidEndDraggingmit willEndScrollingWithVelocityanstatt enabling/ disablingScroll , wenn der Benutzer Enden ziehen:

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

Wir können eine Federanimation verwenden und Benutzerinteraktion während der Animation ermöglichen, um den Bewegungsfluss zu verbessern:

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)
}
GaétanZ
quelle
Dieser Ansatz ist während der Animationsphasen nicht interaktiv. In Karten kann ich beispielsweise das Blatt beim Erweitern mit dem Finger erfassen. Ich kann die Animation dann scrubben, indem ich nach oben oder unten schwenke. Es wird zu dem Punkt zurückkehren, der am nächsten ist. Ich glaube, dies kann gelöst werden, indem UIViewPropertyAnimator(was die Fähigkeit hat anzuhalten) und dann die fractionCompleteEigenschaft verwendet wird, um das Scrub durchzuführen.
aust
1
Du hast recht @aust. Ich habe vergessen, meine vorherige Änderung zu pushen. Es sollte jetzt gut sein. Vielen Dank.
GaétanZ
Dies ist eine großartige Idee, aber ich kann sofort ein Problem erkennen. Bei der translateView(following:)Methode berechnen Sie die Höhe basierend auf der Übersetzung, dies kann jedoch von einem anderen Wert als 0,0 ausgehen (z. B. wird die Tabellenansicht ein wenig gescrollt und die Überlagerung wird maximiert, wenn Sie mit dem Ziehen beginnen) . Das würde zum ersten Sprung führen. Sie können dies durch den scrollViewWillBeginDraggingRückruf lösen, bei dem Sie sich an die Initiale contentOffsetder Tabellenansicht erinnern und diese für die translateView(following:)Berechnung verwenden.
Jakub Truhlář
1
@ GaétanZ Ich war daran interessiert, den Debugger auch an Maps.app in Simulator anzuhängen, erhalte jedoch die Fehlermeldung "An pid konnte nicht angehängt werden:" 9276 ", gefolgt von" Sicherstellen, dass "Maps" noch nicht ausgeführt wird und <Benutzername> die Berechtigung dazu hat debuggen Sie es. " - Wie haben Sie Xcode ausgetrickst, um das Anhängen an Maps.app zu ermöglichen?
Matm
1
@matm @ GaétanZ hat für mich an Xcode 10.1 / Mojave gearbeitet -> muss nur deaktiviert werden, um den System Integrity Protection (SIP) zu deaktivieren. Sie müssen: 1) Starten Sie Ihren Mac neu; 2) Halten Sie cmd + R gedrückt. 3) Wählen Sie im Menü Dienstprogramme und dann Terminal. 4) Geben Sie im Fenster Terminal Folgendes ein : csrutil disable && reboot. Sie haben dann alle Befugnisse, die Ihnen root normalerweise geben sollte, einschließlich der Möglichkeit, sich an einen Apple-Prozess anzuschließen.
Valdyr
18

Versuchen Sie Riemenscheibe :

Pulley ist eine benutzerfreundliche Schubladenbibliothek, die die Schublade in der Maps-App von iOS 10 imitieren soll. Es stellt eine einfache API bereit, mit der Sie jede UIViewController-Unterklasse als Schubladeninhalt oder primären Inhalt verwenden können.

Riemenscheibenvorschau

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

Rajee Jones
quelle
1
Unterstützt es das Scrollen in der zugrunde liegenden Liste?
Richard Lindhout
@RichardLindhout - Nach dem Ausführen der Demo scheint das Scrollen unterstützt zu werden, aber nicht der reibungslose Übergang vom Verschieben der Schublade zum Scrollen der Liste.
Stoph
Das Beispiel scheint den rechtlichen Link für Äpfel unten links auszublenden.
Gerd Castan
1
Super einfache Frage, wie haben Sie den Indikator oben auf dem unteren Blatt erhalten?
Ein Israfil
12

Sie können meine Antwort https://github.com/SCENEE/FloatingPanel versuchen . Es bietet einen Container View Controller zur Anzeige einer "Bottom Sheet" -Schnittstelle.

Es ist einfach zu bedienen und es macht Ihnen nichts aus, mit Gestenerkennern umzugehen! Bei Bedarf können Sie auch die Ansicht einer Bildlaufansicht (oder der Geschwisteransicht) in einem unteren Blatt verfolgen.

Dies ist ein einfaches Beispiel. Bitte beachten Sie, dass Sie einen View Controller vorbereiten müssen, um Ihren Inhalt in einem unteren Blatt anzuzeigen.

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

Hier ist ein weiterer Beispielcode zum Anzeigen eines unteren Blattes zum Suchen eines Speicherorts wie Apple Maps.

Beispiel-App wie Apple Maps

SZENEE
quelle
fpc.set (contentViewController: newsVC), Methode fehlt in der Bibliothek
Faris Muhammed
1
Super einfache Frage, wie haben Sie den Indikator oben auf dem unteren Blatt erhalten?
Ein Israfil
1
@AIsrafil Ich denke, es ist nur ein UIView
ShadeToD
Ich denke, es ist eine wirklich schöne Lösung, aber in iOS 13 gibt es ein Problem mit der Darstellung des Modal View Controllers mit diesem schwebenden Panel.
ShadeToD
11

Ich habe meine eigene Bibliothek geschrieben, um das beabsichtigte Verhalten in der ios Maps-App zu erreichen. Es ist eine protokollorientierte Lösung. Sie müssen also keine Basisklasse erben, sondern einen Sheet Controller erstellen und nach Ihren Wünschen konfigurieren. Es unterstützt auch die innere Navigation / Präsentation mit oder ohne UINavigationController.

Siehe untenstehenden Link für weitere Details.

https://github.com/OfTheWolf/UBottomSheet

Grundblatt

ugur
quelle
Dies sollte die ausgewählte Antwort sein, da Sie immer noch auf die VC hinter dem Popup klicken können.
Jay
Super einfache Frage, wie haben Sie den Indikator oben auf dem unteren Blatt erhalten?
Ein Israfil
1

Ich habe kürzlich eine Komponente namens SwipeableViewUnterklasse von erstellt UIView, die in Swift 5.1 geschrieben wurde. Es unterstützt alle 4 Richtungen, verfügt über mehrere Anpassungsoptionen und kann verschiedene Attribute und Elemente animieren und interpolieren (z. B. Layouteinschränkungen, Hintergrund- / Farbtonfarbe, affine Transformation, Alphakanal und Ansichtszentrum, alle mit dem jeweiligen Vitrinen-Demo demonstriert). Es unterstützt auch die Wischkoordination mit der inneren Bildlaufansicht, wenn diese gesetzt oder automatisch erkannt wird. Sollte ziemlich einfach und unkompliziert zu bedienen sein (ich hoffe 🙂)

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

konzeptioneller Beweiß:

Geben Sie hier die Bildbeschreibung ein

Ich hoffe es hilft

Luca Iaco
quelle
0

Vielleicht können Sie meine Antwort https://github.com/AnYuan/AYPannel ausprobieren , inspiriert von Pulley. Reibungsloser Übergang vom Verschieben der Schublade zum Scrollen der Liste. Ich habe der Container-Bildlaufansicht eine Schwenkgeste hinzugefügt und shouldRecognizeSimultaneousWithGestureRecognizer so eingestellt, dass YES zurückgegeben wird. Mehr Details in meinem Github-Link oben. Möchte helfen.

Tag
quelle
6
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert. Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Pirho