Cómo implementar el barrido interactivo de celdas para activar la animation de transición como WhatsApp

Estoy buscando cómo implementar como deslizar las celdas de WhatsApp, ya he implementado la animation de deslizar celdas usando UIPanGestureRecognizer, la única izquierda está realizando la animation interactiva, añadiendo el nuevo UIViewController a la window y mostrándola en function de la velocidad del reconocimiento de gestos y Valor del eje X-.

Alguna nota adicional para ser precisa sobre lo que quiero lograr:

  • Tengo un UITableViewController , que tiene UITableViewCell s personalizado. Quiero poder arrastrar una celda de izquierda a derecha para iniciar las animaciones interactivas. (Nota: Ya he implementado la transferencia de celdas).

  • El nuevo UIViewController se UIViewController de izquierda a derecha.

  • Al deslizar la celda, la UITableViewController del UITableViewController se moverá hacia la derecha, en ese punto, quiero mostrar el UIViewController empuja UIViewController lado.

Aquí hay un GIF para más detalles sobre lo que necesito (El GIF está deslizando la celda de derecha a izquierda, necesito lo contrario):

introduzca la descripción de la imagen aquí

Sugiero usar SWRevealViewController . Es muy fácil de configurar usando su guía y se ve absolutamente genial. He encontrado que incluso funciona mejor cuando precargas el UIViewController que utilizas para ser lo que se muestra debajo.

Añade una gran experiencia de usuario para la funcionalidad que está buscando.

introduzca la descripción de la imagen aquí

También puede ser interactivo para el usuario si desea optar por esa funcionalidad. No he utilizado la function interactiva, pero es muy fácil comenzar a funcionar con solo unas pocas líneas de código:

 let storyboard = UIStoryboard(name: "Main", bundle: .main) let mainVC = storyboard.instantiateInitialViewController() let menuStoryboard = UIStoryboard(name: "Menu", bundle: sdkBundle) let menuNav = menuStoryboard.instantiateInitialViewController() as! UINavigationController let mainRevealVC = SWRevealViewController(rearViewController: menuNav, frontViewController: mainVC) mainRevealVC?.modalTransitionStyle = .crossDissolve present(mainRevealVC!, animated: true, completion: nil) 

Y para que se muestre el UIViewController revelación, simplemente llama

 // Every UIViewController will have a `self.revealViewController()` when you `import SWRevealViewController` self.revealViewController().revealToggle(animated: true) 

Estoy de acuerdo con @DonMag, un iOS slide menu podría ser tu mejor apuesta. Aquí hay un ejemplo de uno simple: SimpleSideMenu

¿Tiene que ser necesariamente un nuevo controller detrás de la vista de tabla? Déjeme tratar de explicar mi enfoque en el ejemplo de WhatsApp. Supongamos que la aplicación tiene ChatController que tiene la vista de tabla con el chat y un ChatDetailController que se revela con el golpe.

Cuando selecciona una conversación, en lugar de presentar un ChatController presente en lugar de ChatParent , crea automáticamente y agrega dos hijos. El ChatController y ChatDetailController . A continuación defina un protocolo llamado SwipeableCellDelegate con una function cellDidSwipe(toPosition position: CGPoint) y haga que el ChatParent ajuste a él. Cuando la celda se desliza, el padre puede tomar la decisión de si el chat se aleja y, de ser así, ¿cuánto? Simplemente puede mover la vista ChatController directamente a través de su propiedad .view , revelando el segundo elemento secundario, el ChatDetailController detrás de él.

Hay dos desventajas en comparación con el gif que publicaste.

  1. La barra de navigation no se desvanece del chat al detalle del chat. Sin embargo, diría que es mejor actualizar la barra de navigation cuando la animation se completa, al less personalmente no soy fan de este desvanecimiento a través del cual puedes ver a veces ambos sets de elementos de navigation. Creo que si el chat está en la pantalla, los elementos de chat deberían estar presentes y solo cuando la vista de detalles aparezca por completo si los artículos se actualizan.
  2. La segunda cosa es el despido animado del keyboard. No tengo idea de cómo cambiar el marco del keyboard para que desaparezca proporcionalmente a la distancia del usuario, pero tal vez podría descartarse automáticamente tan pronto como se detecte un golpe. Esta es una práctica estándar entre muchas aplicaciones, por lo que debería ser una solución decente.

¡La mejor de las suertes!

Hay una biblioteca muy simple pero perfecta para su situación, llamada SWNavigationController, que implementa al igual que InteractivePopGestureRecognizer de UINavigationController también interactivePushGestureRecognizer. En su caso, no desea que el impulso se active desde UIScreenEdgePangesturerecognizer, por lo que es mejor personalizar la implementación en lugar de instalar el module, que es lo que hice. Aquí puede encontrar el proyecto simple completo que hace exactamente lo que pidió.

He realizado pocas modificaciones en SWNavigationController para admitir la sustitución de UIScreenEdgePangesturerecognizer por un UIPanGestureRecognizer

 import UIKit // First in AppDelegate func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { self.window = UIWindow(frame: UIScreen.main.bounds) let firstVc = ViewController() let initialViewController: SWNavigationController = SWNavigationController(rootViewController: firstVc) self.window?.rootViewController = initialViewController self.window?.makeKeyAndVisible() return true } // Your chat viewController class ViewController: UIViewController { var backgroundColors: [IndexPath : UIColor] = [ : ] var swNavigationController: SWNavigationController { return navigationController as! SWNavigationController } /// The collectionView if you're not using UICollectionViewController lazy var collectionView: UICollectionView = { let cv: UICollectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: self.layout) cv.backgroundColor = UIColor.white cv.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell") cv.dataSource = self return cv }() override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "chat vc" view.addSubview(collectionView) let panGestureRecognizer: UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(ViewController.handlePan(_:))) panGestureRecognizer.delegate = self collectionView.addGestureRecognizer(panGestureRecognizer) // Replace navigation controller's interactivePushGestureRecognizer with our own pan recognizer. // SWNavigationController uses uiscreenedgerecognizer by default which we don't need in our case. swNavigationController.interactivePushGestureRecognizer = panGestureRecognizer } func handlePan(_ recognizer: UIPanGestureRecognizer) { guard fabs(recognizer.translation(in: collectionView).x) > fabs(recognizer.translation(in: collectionView).y) else { return } // create the new view controller upon .began if recognizer.state == .began { // disable scrolling(optional) collectionView.isScrollEnabled = false // pan location let location: CGPoint = recognizer.location(in: collectionView) // get indexPath of cell where pan is taking place if let panCellIndexPath: IndexPath = collectionView.indexPathForItem(at: location) { // clear previously pushed viewControllers swNavigationController.pushableViewControllers.removeAllObjects() // create detail view controller for pan indexPath let dvc = DetailViewController(indexPath: panCellIndexPath, backgroundColor: backgroundColors[panCellIndexPath]!) swNavigationController.pushableViewControllers.add(dvc) } } else if recognizer.state != .changed { collectionView.isScrollEnabled = true } // let navigation controller handle presenting // (you can consume the initial pan translation on x axis to drag the cell to the left until a defined threshold and call handleRightSwipe: only after that) swNavigationController.handleRightSwipe(recognizer) } } // Cell detail view controller class DetailViewController: UIViewController { var indexPath: IndexPath var backgroundColor: UIColor init(indexPath: IndexPath, backgroundColor: UIColor) { self.indexPath = indexPath self.backgroundColor = backgroundColor super.init(nibName: nil, bundle: nil) } requinetworking init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() navigationItem.title = "detail vc at: \(indexPath.row)" view.backgroundColor = backgroundColor } }