para mejor mapas internet gratis google funciona cual conexion celular apple ios swift uiview uiscrollview mapkit

ios - mejor - ¿Cómo puedo imitar la hoja inferior de la aplicación Mapas?



mapas iphone no funciona (6)

Actualización 4 de diciembre de 2018

Lancé una biblioteca basada en esta solución https://github.com/applidium/ADOverlayContainer .

Imita la superposición de la aplicación Atajos. Vea este artículo para más detalles.

El componente principal de la biblioteca es OverlayContainerViewController . Define un área donde un controlador de vista se puede arrastrar hacia arriba y hacia abajo, ocultando o revelando el contenido debajo de él.

let contentController = MapsViewController() let overlayController = SearchViewController() let containerController = OverlayContainerViewController() containerController.delegate = self containerController.viewControllers = [ contentController, overlayController ] window?.rootViewController = containerController

Implemente OverlayContainerViewControllerDelegate para especificar el número de muescas deseadas:

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

Respuesta anterior

Creo que hay un punto significativo que no se trata en las soluciones sugeridas: la transición entre el desplazamiento y la traducción.

En Maps, como habrás notado, cuando tableView alcanza contentOffset.y == 0 , la hoja inferior se desliza hacia arriba o hacia abajo.

El punto es complicado porque no podemos simplemente habilitar / deshabilitar el desplazamiento cuando nuestro gesto panorámico comienza la traducción. Pararía el desplazamiento hasta que comience un nuevo toque. Este es el caso en la mayoría de las soluciones propuestas aquí.

Aquí está mi intento de implementar esta moción.

Punto de partida: aplicación de mapas

Para comenzar nuestra investigación, visualicemos la jerarquía de vistas de Maps (inicie Maps en un simulador y seleccione Debug > Attach to process by PID or Name > Maps en Xcode 9).

No dice cómo funciona el movimiento, pero me ayudó a entender la lógica del mismo. Puedes jugar con el lldb y el depurador de jerarquía de vistas.

Nuestras pilas ViewController

Creemos una versión básica de la arquitectura Maps ViewController.

Comenzamos con un BackgroundViewController (nuestra vista de mapa):

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

Ponemos el tableView en un UIViewController dedicado:

class OverlayViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { lazy var tableView = UITableView() override func loadView() { view = tableView tableView.dataSource = self tableView.delegate = self } [...] }

Ahora, necesitamos un VC para incrustar la superposición y administrar su traducción. Para simplificar el problema, consideramos que puede traducir la superposición de un punto estático OverlayPosition.maximum a otro OverlayPosition.minimum .

Por ahora solo tiene un método público para animar el cambio de posición y tiene una vista transparente:

enum OverlayPosition { case maximum, minimum } class OverlayContainerViewController: UIViewController { let overlayViewController: OverlayViewController var translatedViewHeightContraint = ... override func loadView() { view = UIView() } func moveOverlay(to position: OverlayPosition) { [...] } }

Finalmente necesitamos un ViewController para incrustar todo:

class StackViewController: UIViewController { private var viewControllers: [UIViewController] override func viewDidLoad() { super.viewDidLoad() viewControllers.forEach { gz_addChild($0, in: view) } } }

En nuestro AppDelegate, nuestra secuencia de inicio se ve así:

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

La dificultad detrás de la traducción superpuesta

Ahora, ¿cómo traducir nuestra superposición?

La mayoría de las soluciones propuestas usan un reconocedor de gestos panorámicos dedicado, pero en realidad ya tenemos uno: el gesto panorámico de la vista de tabla. Además, necesitamos mantener sincronizados el desplazamiento y la traducción, ¡y UIScrollViewDelegate tiene todos los eventos que necesitamos!

Una implementación ingenua usaría un segundo gesto panorámico e intentaría restablecer el contenido contentOffset de la vista de tabla cuando se produzca la traducción:

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

Pero no funciona. TableView actualiza su contentOffset cuando se contentOffset su propia acción de reconocimiento de gestos panorámicos o cuando se llama a su devolución de llamada displayLink. No hay posibilidad de que nuestro reconocedor se active inmediatamente después de aquellos para anular con éxito el contentOffset . Nuestra única posibilidad es participar en la fase de diseño (anulando las layoutSubviews de layoutSubviews de las vistas de desplazamiento en cada cuadro de la vista de desplazamiento) o responder al método didScroll del delegado llamado cada vez que se modifica contentOffset . Probemos con este.

La implementación de la traducción

OverlayVC un delegado a nuestro OverlayVC para enviar los eventos de scrollview a nuestro controlador de traducción, 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) } }

En nuestro contenedor, hacemos un seguimiento de la traducción usando una enumeración:

enum OverlayInFlightPosition { case minimum case maximum case progressing }

El cálculo de la posición actual se ve así:

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

Necesitamos 3 métodos para manejar la traducción:

El primero nos dice si necesitamos comenzar la traducción.

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

El segundo realiza la traducción. Utiliza el método de translation(in:) del gesto de desplazamiento de 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) ) }

El tercero anima el final de la traducción cuando el usuario suelta su dedo. Calculamos la posición usando la velocidad y la posición actual de la vista.

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

La implementación de delegado de nuestra superposición simplemente se ve así:

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 final: despache los toques del contenedor de superposición

La traducción ahora es bastante eficiente. Pero todavía hay un problema final: los toques no se entregan a nuestra vista de fondo. Todos son interceptados por la vista del contenedor de superposición. No podemos establecer isUserInteractionEnabled en false porque también deshabilitaría la interacción en nuestra vista de tabla. La solución es la que se usa masivamente en la aplicación Mapas, 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 } }

Se elimina de la cadena de respuesta.

En OverlayContainerViewController :

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

Resultado

Aquí está el resultado:

Puedes encontrar el código here .

Por favor, si ve algún error, ¡avíseme! Tenga en cuenta que su implementación puede, por supuesto, usar un segundo gesto de desplazamiento, especialmente si agrega un encabezado en su superposición.

Actualización 23/08/18

Podemos reemplazar scrollViewDidEndDragging con willEndScrollingWithVelocity lugar de enable / disable el desplazamiento cuando el usuario termina de arrastrar:

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

Podemos usar una animación de primavera y permitir la interacción del usuario mientras animamos para que el movimiento fluya mejor:

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

¿Alguien puede decirme cómo puedo imitar la hoja inferior en la nueva aplicación Maps en iOS 10?

En Android, puede usar una BottomSheet que imita este comportamiento, pero no pude encontrar algo así para iOS.

¿Es una simple vista de desplazamiento con un contenido insertado, de modo que la barra de búsqueda está en la parte inferior?

Soy bastante nuevo en la programación de iOS, por lo que si alguien pudiera ayudarme a crear este diseño, sería muy apreciado.

Esto es lo que quiero decir con "hoja inferior":


Nada de lo anterior funciona para mí porque la panorámica tanto de la vista de tabla como de la vista exterior simultáneamente no funciona sin problemas. Entonces escribí mi propio código para lograr el comportamiento previsto en la aplicación ios Maps.

https://github.com/OfTheWolf/UBottomSheet


No sé cómo responde exactamente la hoja inferior de la nueva aplicación Maps a las interacciones de los usuarios. Pero puede crear una vista personalizada que se parezca a la de las capturas de pantalla y agregarla a la vista principal.

Supongo que sabes cómo:

1- crea controladores de vista ya sea por guiones gráficos o usando archivos xib.

2- usa googleMaps o el MapKit de Apple.

Ejemplo

1- Cree 2 controladores de vista, por ejemplo, MapViewController y BottomSheetViewController . El primer controlador alojará el mapa y el segundo es la hoja inferior.

Configurar MapViewController

Cree un método para agregar la vista de la hoja inferior.

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

Y llámalo en el método viewDidAppear:

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

Configurar BottomSheetViewController

1) Preparar el fondo

Cree un método para agregar efectos de desenfoque y vitalidad

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

llame a este método en su vista

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

Asegúrese de que el color de fondo de la vista de su controlador sea clearColor.

2) animar la apariencia de la hoja inferior

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 tu xib como quieras.

4) Agregue Pan Gesture Recognizer a su vista.

En su método viewDidLoad, agregue UIPanGestureRecognizer.

override func viewDidLoad() { super.viewDidLoad() let gesture = UIPanGestureRecognizer.init(target: self, action: #selector(BottomSheetViewController.panGesture)) view.addGestureRecognizer(gesture) }

E implemente su comportamiento gestual:

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

Hoja inferior desplazable:

Si su vista personalizada es una vista de desplazamiento o cualquier otra vista que herede, entonces tiene dos opciones:

Primero:

Diseñe la vista con una vista de encabezado y agregue panGesture al encabezado. (mala experiencia del usuario) .

Segundo:

1 - Agregue panGesture a la vista de la hoja inferior.

2 - Implemente el UIGestureRecognizerDelegate y configure el delegado panGesture en el controlador.

3- Implemente shouldRecognizeSimuallylyWith delegate y deshabilita la propiedad scrollView isScrollEnabled en dos casos:

  • La vista es parcialmente visible.
  • La vista es totalmente visible, la propiedad scrollView contentOffset es 0 y el usuario arrastra la vista hacia abajo.

De lo contrario, habilite el desplazamiento.

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

En caso de que establezca .allowUserInteraction como una opción de animación, como en el proyecto de muestra, debe habilitar el desplazamiento en el cierre de finalización de la animación si el usuario se desplaza hacia arriba.

Proyecto de muestra

Creé un proyecto de muestra con más opciones en this repositorio que puede brindarle una mejor perspectiva sobre cómo personalizar el flujo.

En la demostración, la función addBottomSheetView () controla qué vista debe usarse como una hoja inferior.

Ejemplos de capturas de pantalla del proyecto

- Vista parcial

- Vista completa

- Vista desplazable


Prueba la Pulley :

Pulley es una biblioteca de cajones fácil de usar destinada a imitar el cajón en la aplicación Maps de iOS 10. Expone una API simple que le permite usar cualquier subclase de UIViewController como contenido del cajón o contenido principal.

Pulley


Puedes probar mi respuesta https://github.com/SCENEE/FloatingPanel . Proporciona un controlador de vista de contenedor para mostrar una interfaz de "hoja inferior".

¡Es fácil de usar y no le importa el manejo del reconocedor de gestos! También puede rastrear una vista de desplazamiento (o la vista de hermanos) en una hoja inferior si es necesario.

Este es un ejemplo simple. Tenga en cuenta que debe preparar un controlador de vista para mostrar su contenido en una hoja inferior.

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

Here hay otro código de ejemplo para mostrar una hoja inferior para buscar una ubicación como Apple Maps.


Quizás puedas probar mi respuesta https://github.com/AnYuan/AYPannel , inspirada en Pulley. Transición suave de mover el cajón a desplazarse por la lista. Agregué un gesto panorámico en la vista de desplazamiento del contenedor y configuré shouldRecognizeSim simultáneamenteWithGestureRecognizer para devolver YES. Más detalles en mi enlace de github arriba. Deseo ayudar