Ejecutar acción cuando se presiona el botón de la barra trasera de UINavigationController

Necesito ejecutar una acción (vaciar una matriz), cuando se presiona el botón Atrás de un UINavigationController , mientras que el botón sigue UINavigationController que UINavigationController el ViewController anterior en la stack. ¿Cómo podría lograr esto usando Swift? enter image description here

Una opción sería implementar su propio botón de retroceso personalizado. Debería agregar el siguiente código a su método viewDidLoad:

 - (void) viewDidLoad { [super viewDidLoad]; self.navigationItem.hidesBackButton = YES; UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStyleBordered target:self action:@selector(back:)]; self.navigationItem.leftBarButtonItem = newBackButton; } - (void) back:(UIBarButtonItem *)sender { // Perform your custom actions // ... // Go back to the previous ViewController [self.navigationController popViewControllerAnimated:YES]; } 

ACTUALIZAR:

Aquí está la versión de Swift:

  override func viewDidLoad { super.viewDidLoad() self.navigationItem.hidesBackButton = true let newBackButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Bordered, target: self, action: "back:") self.navigationItem.leftBarButtonItem = newBackButton } func back(sender: UIBarButtonItem) { // Perform your custom actions // ... // Go back to the previous ViewController self.navigationController?.popViewControllerAnimated(true) } 

ACTUALIZACIÓN 2:

Aquí está la versión para Swift 3:

  override func viewDidLoad { super.viewDidLoad() self.navigationItem.hidesBackButton = true let newBackButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.plain, target: self, action: #selector(YourViewController.back(sender:))) self.navigationItem.leftBarButtonItem = newBackButton } func back(sender: UIBarButtonItem) { // Perform your custom actions // ... // Go back to the previous ViewController _ = navigationController?.popViewController(animated: true) } 

Reemplazar el botón por uno personalizado como se sugiere en otra respuesta posiblemente no sea una buena idea, ya que perderá el estilo y el comportamiento predeterminados.

Otra opción que tiene es implementar el método viewWillDisappear en View Controller y buscar una propiedad llamada isMovingFromParentViewController . Si esa propiedad es verdadera, significa que el Controlador de Vista está desapareciendo porque se está eliminando (reventado).

Debería verse algo como:

 override func viewWillDisappear(animated : Bool) { super.viewWillDisappear(animated) if self.isMovingFromParentViewController { // Your code... } } 
 override func willMove(toParentViewController parent: UIViewController?) { super.willMove(toParentViewController: parent) if parent == nil { print("This VC is 'will' be popped. ie the back button was pressed.") } } 

Creé esta clase (rápida) para crear un botón de retroceso exactamente igual al habitual, incluida la flecha hacia atrás. Puede crear un botón con texto regular o con una imagen.

Uso

 weak var weakSelf = self // Assign back button with back arrow and text (exactly like default back button) navigationItem.leftBarButtonItems = CustomBackButton.createWithText("YourBackButtonTitle", color: UIColor.yourColor(), target: weakSelf, action: #selector(YourViewController.tappedBackButton)) // Assign back button with back arrow and image navigationItem.leftBarButtonItems = CustomBackButton.createWithImage(UIImage(named: "yourImageName")!, color: UIColor.yourColor(), target: weakSelf, action: #selector(YourViewController.tappedBackButton)) func tappedBackButton() { // Do your thing self.navigationController!.popViewControllerAnimated(true) } 

CustomBackButtonClass

(código para dibujar la flecha hacia atrás creada con el complemento Sketch & Paintcode)

 class CustomBackButton: NSObject { class func createWithText(text: String, color: UIColor, target: AnyObject?, action: Selector) -> [UIBarButtonItem] { let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FixedSpace, target: nil, action: nil) negativeSpacer.width = -8 let backArrowImage = imageOfBackArrow(color: color) let backArrowButton = UIBarButtonItem(image: backArrowImage, style: UIBarButtonItemStyle.Plain, target: target, action: action) let backTextButton = UIBarButtonItem(title: text, style: UIBarButtonItemStyle.Plain , target: target, action: action) backTextButton.setTitlePositionAdjustment(UIOffset(horizontal: -12.0, vertical: 0.0), forBarMetrics: UIBarMetrics.Default) return [negativeSpacer, backArrowButton, backTextButton] } class func createWithImage(image: UIImage, color: UIColor, target: AnyObject?, action: Selector) -> [UIBarButtonItem] { // recommended maximum image height 22 points (ie 22 @1x, 44 @2x, 66 @3x) let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FixedSpace, target: nil, action: nil) negativeSpacer.width = -8 let backArrowImageView = UIImageView(image: imageOfBackArrow(color: color)) let backImageView = UIImageView(image: image) let customBarButton = UIButton(frame: CGRectMake(0,0,22 + backImageView.frame.width,22)) backImageView.frame = CGRectMake(22, 0, backImageView.frame.width, backImageView.frame.height) customBarButton.addSubview(backArrowImageView) customBarButton.addSubview(backImageView) customBarButton.addTarget(target, action: action, forControlEvents: .TouchUpInside) return [negativeSpacer, UIBarButtonItem(customView: customBarButton)] } private class func drawBackArrow(frame frame: CGRect = CGRect(x: 0, y: 0, width: 14, height: 22), color: UIColor = UIColor(hue: 0.59, saturation: 0.674, brightness: 0.886, alpha: 1), resizing: ResizingBehavior = .AspectFit) { /// General Declarations let context = UIGraphicsGetCurrentContext()! /// Resize To Frame CGContextSaveGState(context) let resizedFrame = resizing.apply(rect: CGRect(x: 0, y: 0, width: 14, height: 22), target: frame) CGContextTranslateCTM(context, resizedFrame.minX, resizedFrame.minY) let resizedScale = CGSize(width: resizedFrame.width / 14, height: resizedFrame.height / 22) CGContextScaleCTM(context, resizedScale.width, resizedScale.height) /// Line let line = UIBezierPath() line.moveToPoint(CGPoint(x: 9, y: 9)) line.addLineToPoint(CGPoint.zero) CGContextSaveGState(context) CGContextTranslateCTM(context, 3, 11) line.lineCapStyle = .Square line.lineWidth = 3 color.setStroke() line.stroke() CGContextRestoreGState(context) /// Line Copy let lineCopy = UIBezierPath() lineCopy.moveToPoint(CGPoint(x: 9, y: 0)) lineCopy.addLineToPoint(CGPoint(x: 0, y: 9)) CGContextSaveGState(context) CGContextTranslateCTM(context, 3, 2) lineCopy.lineCapStyle = .Square lineCopy.lineWidth = 3 color.setStroke() lineCopy.stroke() CGContextRestoreGState(context) CGContextRestoreGState(context) } private class func imageOfBackArrow(size size: CGSize = CGSize(width: 14, height: 22), color: UIColor = UIColor(hue: 0.59, saturation: 0.674, brightness: 0.886, alpha: 1), resizing: ResizingBehavior = .AspectFit) -> UIImage { var image: UIImage UIGraphicsBeginImageContextWithOptions(size, false, 0) drawBackArrow(frame: CGRect(origin: CGPoint.zero, size: size), color: color, resizing: resizing) image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image } private enum ResizingBehavior { case AspectFit /// The content is proportionally resized to fit into the target rectangle. case AspectFill /// The content is proportionally resized to completely fill the target rectangle. case Stretch /// The content is stretched to match the entire target rectangle. case Center /// The content is centered in the target rectangle, but it is NOT resized. func apply(rect rect: CGRect, target: CGRect) -> CGRect { if rect == target || target == CGRect.zero { return rect } var scales = CGSize.zero scales.width = abs(target.width / rect.width) scales.height = abs(target.height / rect.height) switch self { case .AspectFit: scales.width = min(scales.width, scales.height) scales.height = scales.width case .AspectFill: scales.width = max(scales.width, scales.height) scales.height = scales.width case .Stretch: break case .Center: scales.width = 1 scales.height = 1 } var result = rect.standardized result.size.width *= scales.width result.size.height *= scales.height result.origin.x = target.minX + (target.width - result.width) / 2 result.origin.y = target.minY + (target.height - result.height) / 2 return result } } } 

SWIFT 3.0

 class CustomBackButton: NSObject { class func createWithText(text: String, color: UIColor, target: AnyObject?, action: Selector) -> [UIBarButtonItem] { let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil) negativeSpacer.width = -8 let backArrowImage = imageOfBackArrow(color: color) let backArrowButton = UIBarButtonItem(image: backArrowImage, style: UIBarButtonItemStyle.plain, target: target, action: action) let backTextButton = UIBarButtonItem(title: text, style: UIBarButtonItemStyle.plain , target: target, action: action) backTextButton.setTitlePositionAdjustment(UIOffset(horizontal: -12.0, vertical: 0.0), for: UIBarMetrics.default) return [negativeSpacer, backArrowButton, backTextButton] } class func createWithImage(image: UIImage, color: UIColor, target: AnyObject?, action: Selector) -> [UIBarButtonItem] { // recommended maximum image height 22 points (ie 22 @1x, 44 @2x, 66 @3x) let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil) negativeSpacer.width = -8 let backArrowImageView = UIImageView(image: imageOfBackArrow(color: color)) let backImageView = UIImageView(image: image) let customBarButton = UIButton(frame: CGRect(x: 0, y: 0, width: 22 + backImageView.frame.width, height: 22)) backImageView.frame = CGRect(x: 22, y: 0, width: backImageView.frame.width, height: backImageView.frame.height) customBarButton.addSubview(backArrowImageView) customBarButton.addSubview(backImageView) customBarButton.addTarget(target, action: action, for: .touchUpInside) return [negativeSpacer, UIBarButtonItem(customView: customBarButton)] } private class func drawBackArrow(_ frame: CGRect = CGRect(x: 0, y: 0, width: 14, height: 22), color: UIColor = UIColor(hue: 0.59, saturation: 0.674, brightness: 0.886, alpha: 1), resizing: ResizingBehavior = .AspectFit) { /// General Declarations let context = UIGraphicsGetCurrentContext()! /// Resize To Frame context.saveGState() let resizedFrame = resizing.apply(CGRect(x: 0, y: 0, width: 14, height: 22), target: frame) context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY) let resizedScale = CGSize(width: resizedFrame.width / 14, height: resizedFrame.height / 22) context.scaleBy(x: resizedScale.width, y: resizedScale.height) /// Line let line = UIBezierPath() line.move(to: CGPoint(x: 9, y: 9)) line.addLine(to: CGPoint.zero) context.saveGState() context.translateBy(x: 3, y: 11) line.lineCapStyle = .square line.lineWidth = 3 color.setStroke() line.stroke() context.restreGState() /// Line Copy let lineCopy = UIBezierPath() lineCopy.move(to: CGPoint(x: 9, y: 0)) lineCopy.addLine(to: CGPoint(x: 0, y: 9)) context.saveGState() context.translateBy(x: 3, y: 2) lineCopy.lineCapStyle = .square lineCopy.lineWidth = 3 color.setStroke() lineCopy.stroke() context.restreGState() context.restreGState() } private class func imageOfBackArrow(_ size: CGSize = CGSize(width: 14, height: 22), color: UIColor = UIColor(hue: 0.59, saturation: 0.674, brightness: 0.886, alpha: 1), resizing: ResizingBehavior = .AspectFit) -> UIImage { var image: UIImage UIGraphicsBeginImageContextWithOptions(size, false, 0) drawBackArrow(CGRect(origin: CGPoint.zero, size: size), color: color, resizing: resizing) image = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return image } private enum ResizingBehavior { case AspectFit /// The content is proportionally resized to fit into the target rectangle. case AspectFill /// The content is proportionally resized to completely fill the target rectangle. case Stretch /// The content is stretched to match the entire target rectangle. case Center /// The content is centered in the target rectangle, but it is NOT resized. func apply(_ rect: CGRect, target: CGRect) -> CGRect { if rect == target || target == CGRect.zero { return rect } var scales = CGSize.zero scales.width = abs(target.width / rect.width) scales.height = abs(target.height / rect.height) switch self { case .AspectFit: scales.width = min(scales.width, scales.height) scales.height = scales.width case .AspectFill: scales.width = max(scales.width, scales.height) scales.height = scales.width case .Stretch: break case .Center: scales.width = 1 scales.height = 1 } var result = rect.standardized result.size.width *= scales.width result.size.height *= scales.height result.origin.x = target.minX + (target.width - result.width) / 2 result.origin.y = target.minY + (target.height - result.height) / 2 return result } } } 

Pude lograr esto con lo siguiente:

 override func didMoveToParentViewController(parent: UIViewController?) { super.didMoveToParentViewController(parent) if parent == nil{ println("Back Button pressed.") delegate?.goingBack() } } 

No hay necesidad de un botón de retroceso personalizado.

Si está utilizando navigationController , agregue el protocolo UINavigationControllerDelegate a la clase y agregue el método de delegado de la siguiente manera:

 class ViewController:UINavigationControllerDelegate { func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) { if viewController === self { // do here what you want } } } 

Este método se invoca siempre que el controlador de navegación se deslice a una nueva pantalla. Si se presionó el botón Atrás, el nuevo controlador de vista es ViewController .

Si desea tener el botón Atrás con la flecha hacia atrás, puede usar una imagen y un código a continuación

backArrow.png arrow1 backArrow@2x.png arrow2 backArrow@3x.png arrow3

 override func viewDidLoad() { super.viewDidLoad() let customBackButton = UIBarButtonItem(image: UIImage(named: "backArrow") , style: .plain, target: self, action: #selector(backAction(sender:))) customBackButton.imageInsets = UIEdgeInsets(top: 2, left: -8, bottom: 0, right: 0) navigationItem.leftBarButtonItem = customBackButton } func backAction(sender: UIBarButtonItem) { // custom actions here navigationController?.popViewController(animated: true) } 

NO

override func willMove(toParentViewController parent: UIViewController?) { }

Recibirá una llamada incluso si va a seguir el controlador de vista en el que está anulando este método. En el que comprobar si el ” parent ” es nil no, no es una forma precisa de asegurarse de regresar al UIViewController correcto. Para determinar exactamente si el UINavigationController está navegando correctamente hacia el UIViewController que presentó este actual, deberá cumplir con el protocolo UINavigationControllerDelegate .

nota: MyViewController es simplemente el nombre de cualquier UIViewController que quiera detectar desde el cual regresar.

1) En la parte superior de su archivo, agregue UINavigationControllerDelegate .

 class MyViewController: UIViewController, UINavigationControllerDelegate { 

2) Agregue una propiedad a su clase que hará un seguimiento del UIViewController que está seguando.

 class MyViewController: UIViewController, UINavigationControllerDelegate { var previousViewController:UIViewController 

3) en el método viewDidLoad se asigna a self como el delegado para su UINavigationController .

 override func viewDidLoad() { super.viewDidLoad() self.navigationController?.delegate = self } 

3) Antes de realizar la transición , asigne el UIViewController anterior como esta propiedad.

 // In previous UIViewController override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "YourSegueID" { if let nextViewController = segue.destination as? MyViewController { nextViewController.previousViewController = self } } } 

4) y cumplir con un método en MyViewController del UINavigationControllerDelegate

 func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { if viewController == self.previousViewController { // You are going back } } 

No es difícil como creemos. Simplemente cree un marco para UIButton con un color de fondo claro, asigne una acción para el botón y colóquelo sobre el botón Atrás de la barra de navegación. Y finalmente quite el botón después de su uso.

Aquí está el código de muestra de Swift 3 hecho con UIImage en lugar de UIButton

 override func viewDidLoad() { super.viewDidLoad() let imageView = UIImageView() imageView.backgroundColor = UIColor.clear imageView.frame = CGRect(x:0,y:0,width:2*(self.navigationController?.navigationBar.bounds.height)!,height:(self.navigationController?.navigationBar.bounds.height)!) let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(back(sender:))) imageView.isUserInteractionEnabled = true imageView.addGestureRecognizer(tapGestureRecognizer) imageView.tag = 1 self.navigationController?.navigationBar.addSubview(imageView) } 

escribe el código necesita ser ejecutado

 func back(sender: UIBarButtonItem) { // Perform your custom actions} _ = self.navigationController?.popViewController(animated: true) } 

Retire la subvista después de realizar la acción

 override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) for view in (self.navigationController?.navigationBar.subviews)!{ if view.tag == 1 { view.removeFromSuperview() } } 

En mi caso, el viewWillDisappear funcionó mejor. Pero en algunos casos, uno tiene que modificar el controlador de vista anterior. Así que aquí está mi solución con acceso al controlador de vista anterior y funciona en Swift 4 :

 override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if isMovingFromParentViewController { if let viewControllers = self.navigationController?.viewControllers { if (viewControllers.count >= 1) { let previousViewController = viewControllers[viewControllers.count-1] as! NameOfDestinationViewController // whatever you want to do previousViewController.callOrModifySomething() } } } } 

Swift 3:

 override func didMove(toParentViewController parent: UIViewController?) { super.didMove(toParentViewController: parent) if parent == nil{ print("Back button was clicked") } } 

Lo logré llamando / reemplazando viewWillDisappear y luego accediendo a la stack del navigationController esta manera:

 override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) let stack = self.navigationController?.viewControllers.count if stack >= 2 { // for whatever reason, the last item on the stack is the TaskBuilderViewController (not self), so we only use -1 to access it if let lastitem = self.navigationController?.viewControllers[stack! - 1] as? theViewControllerYoureTryingToAccess { // hand over the data via public property or call a public method of theViewControllerYoureTryingToAccess, like lastitem.emptyArray() lastitem.value = 5 } } } 

Como entiendo que quieres vaciar tu array al presionar el botón “Atrás” y hacer clic en tu ViewController let anterior, ViewController let tu Array que cargues en esta pantalla sea

 let settingArray = NSMutableArray() @IBAction func Back(sender: AnyObject) { self. settingArray.removeAllObjects() self.dismissViewControllerAnimated(true, completion: nil) } 
  override public func viewDidLoad() { super.viewDidLoad() self.navigationController?.navigationBar.topItem?.title = GlobalVariables.selectedMainIconName let image = UIImage(named: "back-btn") image = image?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal) self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: image, style: UIBarButtonItemStyle.Plain, target: self, action: #selector(Current[enter image description here][1]ViewController.back) ) } func back() { self.navigationController?.popToViewController( self.navigationController!.viewControllers[ self.navigationController!.viewControllers.count - 2 ], animated: true) } 

Prueba esto .

 self.navigationItem.leftBarButtonItem?.target = "methodname" func methodname ( ) { // enter code here } 

Prueba esto también.

 override func viewWillAppear(animated: Bool) { //empty your array } 

simplemente controle + arrastre el elemento de la barra a continuación func. trabaja como un encanto

 @IBAction func done(sender: AnyObject) { if((self.presentingViewController) != nil){ self.dismiss(animated: false, completion: nil) print("done") } } 

enter image description here