Выполнить действие при нажатии кнопки задней панели UINavigationController

204

Мне нужно выполнить действие (очистить массив), когда нажата кнопка «Назад» UINavigationController, а кнопка все еще вызывает появление предыдущего ViewControllerв стеке. Как я могу сделать это с помощью Swift? введите описание изображения здесь

StevenR
источник

Ответы:

152

Одним из вариантов будет реализация вашей собственной кнопки возврата. Вам необходимо добавить следующий код в ваш метод 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];
}

ОБНОВИТЬ:

Вот версия для 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)
    }

ОБНОВЛЕНИЕ 2:

Вот версия для 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)
    }
fr33g
источник
2
Это не появляется в предыдущем контроллере представления; он выскакивает к корневому контроллеру представления.
каменистый
91
Как я могу иметь стрелку, как обычную кнопку назад?
TomSawyer
@rocky Вы можете попробовать следующую строку в функции возврата: [self.navigationController dismissViewControllerAnimated: YES завершение: nil];
Малайзи
2
@ TomSawyer Для этого, пожалуйста, посмотрите на ответ ниже
fr33g
7
Выполнение замены системной кнопки для отмены функции не является хорошим способом. Лучший способ - ответ ниже! stackoverflow.com/a/27715660/2307276
dpizzuto
478

Замена кнопки на пользовательскую, как указано в другом ответе, возможно, не очень хорошая идея, поскольку вы потеряете поведение и стиль по умолчанию.

Еще один вариант, который у вас есть, - реализовать метод viewWillDisappear на контроллере представления и проверить свойство с именем isMovingFromParentViewController. . Если это свойство имеет значение true, это означает, что View Controller исчезает, потому что он удаляется (выталкивается).

Должно выглядеть примерно так:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingFromParentViewController {
        // Your code...
    }
}

В swift 4.2

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingFromParent {
        // Your code...
    }
}
manecosta
источник
5
@gmogames да, ты не можешь этого сделать. Вопрос не спрашивал об этом, хотя. Чтобы иметь возможность остановить действие возврата, я думаю, вам действительно нужно переопределить кнопку.
Манекоста
13
Для Swift 3.1 :override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if isMovingFromParentViewController { // Your code... } }
Даг Амос
23
viewWillDisappear(animated:)будет срабатывать, если вы получите телефонный звонок. Это скорее всего не то, что вы хотите. Вероятно, лучше использоватьwillMove(toParentViewController:)
Джо Сусник
в Swift 4 отсутствует : переопределить функцию func ViewWillDisappear ( анимация: Bool)
Хавьер Калатрава Llavería
1
Это должен быть принятый ответ. Чисто и просто.
темп
60
override func willMove(toParent parent: UIViewController?)
{
    super.willMove(toParent: parent)
    if parent == nil
    {
        print("This VC is 'will' be popped. i.e. the back button was pressed.")
    }
}
Ия
источник
2
Не работает в Swift3 / iOS10, консольная печать «вложенная поп-анимация может привести к повреждению панели навигации».
itsji10dra
1
Не зовут вообще
zulkarnain шах
3
Это также вызывается при переходе на новый VC, а не только при возвращении.
Хосе Рамирес
Согласно комментарию @JozemiteApps, он находится в документации, вызываемой непосредственно перед добавлением или удалением контроллера представления из контроллера представления контейнера. ,
nstein
2
Это должен быть принятый ответ. И когда parent == nilэто когда мы возвращаемся на parentсцену
Сильван Д Эш
32

Я смог добиться этого с помощью следующего:

Свифт 3

override func didMoveToParentViewController(parent: UIViewController?) {
   super.didMoveToParentViewController(parent)

   if parent == nil {
      println("Back Button pressed.")
      delegate?.goingBack()
   }           
}

Swift 4

override func didMove(toParent parent: UIViewController?) {
    super.didMove(toParent: parent)

    if parent == nil {
        debugPrint("Back Button pressed.")
    }
}

Нет необходимости пользовательской кнопки назад.

Сиддхарт Бхатт
источник
Это фантастика. Старое замечание, но все еще работает с последним Swift.
user3204765
Это срабатывает (ложное срабатывание) также при разматывании со следующего контроллера просмотра (поверх этого), поэтому не обнаружение нажатия кнопки назад.
user2878850
То же самое замечание, что и у предыдущего человека, этот код не обнаруживает активацию кнопки «назад», а всплывающее окно текущего просмотра.
Вилмир
31

Я создал этот (быстрый) класс, чтобы создать кнопку «Назад» в точности как обычную, включая стрелку «назад». Он может создать кнопку с обычным текстом или с изображением.

использование

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

(код для рисования стрелки назад, созданный с помощью плагина 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 (i.e. 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 (i.e. 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.restoreGState()

        /// 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.restoreGState()

        context.restoreGState()
    }

    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
        }
    }
}
Гвидо
источник
Не могли бы вы обновить свой ответ для iOS 11?
BR41N-FCK
2
Привет @ Guido, ваше решение идеально, я попробовал ваш код и заметил, что перед кнопкой «назад» есть место, даже если вы добавили кнопку с отрицательной шириной.
Pawriwes
26

Если вы хотите иметь кнопку «Назад» со стрелкой назад, вы можете использовать изображение и код ниже

backArrow.png arrow1backArrow@2x.png arrow2backArrow@3x.pngarrow3

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)
}
Лешек Сзары
источник
12

Если вы используете, navigationControllerдобавьте UINavigationControllerDelegateпротокол в класс и добавьте метод делегата следующим образом:

class ViewController:UINavigationControllerDelegate {

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

Этот метод вызывается всякий раз, когда контроллер навигации перемещается на новый экран. Если была нажата кнопка «Назад», новый контроллер представления ViewControllerсам по себе.

Аджинкя Патил
источник
Что становится ужасно, когда в качестве делегата используется класс, отличный от NSObjectProtocol.
Ник Уивер
Не всегда вызывается при нажатии кнопки назад.
Тед
9

В Swift 5 и Xcode 10.2

Пожалуйста, не добавляйте пользовательский элемент панели кнопок, используйте это поведение по умолчанию.

Нет необходимости viewWillDisappear , нет необходимости пользовательского BarButtonItem т. Д ...

Лучше определять, когда VC удален из его родителя.

Используйте любую из этих двух функций

override func willMove(toParent parent: UIViewController?) {
    super.willMove(toParent: parent)
    if parent == nil {
        callStatusDelegate?.backButtonClicked()//Here write your code
    }
}

override func didMove(toParent parent: UIViewController?) {
    super.didMove(toParent: parent)
    if parent == nil {
        callStatusDelegate?.backButtonClicked()//Here write your code
    }
}

Если вы хотите остановить стандартное поведение кнопки «Назад», добавьте пользовательский BarButtonItem.

IOS
источник
1
Обратите внимание, что это также вызывается, когда вы щелкаете программно, а не только нажимаете кнопку назад.
Ixx
7

НЕТ

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

Это будет вызвано, даже если вы переходите к контроллеру представления, в котором вы переопределяете этот метод. В какой проверке, если " parent" nilне имеет, не является точным способом быть уверенным, чтобы вернуться к правильному UIViewController. Чтобы точно определить, UINavigationControllerправильно ли возвращается назад к тому, UIViewControllerкоторый представил этот текущий, вам нужно будет выполнитьUINavigationControllerDelegate протоколу.

ДА

примечание: MyViewControllerэто просто название того, что UIViewControllerвы хотите обнаружить, возвращаясь из.

1) Вверху вашего файла добавьте UINavigationControllerDelegate.

class MyViewController: UIViewController, UINavigationControllerDelegate {

2) Добавьте в свой класс свойство, которое будет отслеживать то, от UIViewControllerчего вы переходите.

class MyViewController: UIViewController, UINavigationControllerDelegate {

var previousViewController:UIViewController

3) в MyViewController«S viewDidLoadметод правопреемника в selfкачестве делегата для вашего UINavigationController.

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

3) Перед тем как переходить , присвойте предыдущее UIViewControllerкак это свойство.

// 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) И соответствовать одному методу MyViewControllerизUINavigationControllerDelegate

func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
    if viewController == self.previousViewController {
        // You are going back
    }
}
Брэндон А
источник
1
Спасибо за полезный ответ! Читатели остерегаются настройки делегата UINavigationController для определенного контроллера представления; если у контроллера навигации уже есть делегат, вы рискуете лишить этого другого делегата обратных вызовов, которые он ожидает. В нашем приложении делегат UINavigationController является общим объектом (AppCoordinator), на который все контроллеры представления имеют указатель.
Билл Фет
7

В моем случае это viewWillDisappearсработало лучше всего. Но в некоторых случаях нужно модифицировать предыдущий контроллер представления. Итак, вот мое решение с доступом к предыдущему контроллеру представления, и оно работает в 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()
                }
            }
        }
    }
Бернд
источник
-viewDidDisappear (или -viewWillDisappear) будет вызываться, даже если представление закрывается представлением другого контроллера представления (а не только при нажатии кнопки <Back), поэтому необходимо проверить isMovingFromParentViewController.
Билл Фет
5

Прежде чем покинуть текущий контроллер, мне нужно показать предупреждение. Итак, я сделал это так:

  1. Добавить расширение с UINavigationControllerпомощьюUINavigationBarDelegate
  2. Добавьте селектор в ваш контроллер navigationShouldPopOnBack (завершение :)

Это сработало)

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        if let items = navigationBar.items, viewControllers.count < items.count {
            return true
        }

        let clientInfoVC = topViewController as? ClientInfoVC
        if clientInfoVC?.responds(to: #selector(clientInfoVC?.navigationShouldPopOnBack)) ?? false {
            clientInfoVC?.navigationShouldPopOnBack(completion: { isAllowPop in
                if isAllowPop {
                    DispatchQueue.main.async {
                        self.popViewController(animated: true)
                    }
                }
            })
        }

        DispatchQueue.main.async {
            self.popViewController(animated: true)
        }

        return false
    }
}

@objc func navigationShouldPopOnBack(completion: @escaping (Bool) -> ()) {
        let ok = UIAlertAction(title: R.string.alert.actionOk(), style: .default) { _ in
            completion(true)
        }
        let cancel = UIAlertAction(title: R.string.alert.actionCancel(), style: .cancel) { _ in
            completion(false)
        }
        let alertController = UIAlertController(title: "", message: R.string.alert.contractMessage(), preferredStyle: .alert)
        alertController.addAction(ok)
        alertController.addAction(cancel)
        present(alertController, animated: true, completion: nil)
    }
Тарас
источник
4

Это не сложно, как мы. Просто создайте рамку для UIButton с чистым фоновым цветом, назначьте действие для кнопки и поместите поверх кнопки навигации на панели навигации. И, наконец, удалите кнопку после использования.

Вот пример кода Swift 3, сделанный с UIImage вместо 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)
    }

написать код необходимо выполнить

func back(sender: UIBarButtonItem) {

    // Perform your custom actions}
    _ = self.navigationController?.popViewController(animated: true)

    }

Удалить подпункт после выполнения действия

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    for view in (self.navigationController?.navigationBar.subviews)!{
        if view.tag == 1 {
            view.removeFromSuperview()
        }
    }
АРШВИН ДЕНУЕВ ЛАЛ
источник
Спасибо приятель . :-)
АРШВИН ДЕНУЕВ ЛАЛ
Как вы создаете состояние при касании?
Quang Thang
Это не работает в iOS 11. Не, когда фоновый цвет UIImageView ясен. Установите его на другой цвет, и он работает.
Нажмите Формы
Мы можем определить UIImageView с чистым цветом, установить его рамку, назначить Tapgesture и разместить в любом месте экрана. Тогда почему мы не можем разместить его над панелью навигации. Чтобы быть искренним, я не буду рекомендовать то, что я написал. Если есть проблема, определенно есть причина, но не цвет имеет значение. Забудьте код, следуйте логике, и у вас все получится. :)
АРШВИН ДЕНУЕВ ЛАЛ
4

Swift 4.2:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingFromParent {
        // Your code...

    }
}
М-р Наджмул Хасан
источник
3

Свифт 3:

override func didMove(toParentViewController parent: UIViewController?) {
    super.didMove(toParentViewController: parent)

    if parent == nil{
        print("Back button was clicked")
    }
}
гал
источник
-did / willMove (toParentViewController :), возможно, лучше, чем проверка isMovingTfromParentViewController в -viewWillDisappear, поскольку он вызывается только тогда, когда контроллер представления фактически меняет родителей (а не когда представление покрывается представлением другого VC). Но более «правильное» решение реализовать метод делегата UINavigationController. Будьте осторожны, хотя; если у NavigationController уже есть делегат, вы рискуете лишить этого другого делегата обратных вызовов, которые он ожидает.
Билл Фет
Я тестировал с splitViewController. Там не может быть разницы между добавленным или удаленным.
claude31
2

Попробуй это .

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

Попробуйте это тоже.

override func viewWillAppear(animated: Bool) {
  //empty your array
}
AG
источник
2

просто сделайте control + перетащите элемент панели вниз под func. работать как шарм

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

введите описание изображения здесь

кодеры
источник
2

Вы можете подкласс UINavigationControllerи переопределитьpopViewController(animated: Bool) . Помимо возможности выполнения там некоторого кода, вы также можете запретить пользователю возвращаться назад, например, предлагать сохранить или отменить свою текущую работу.

Пример реализации, в котором вы можете установить параметр, popHandlerкоторый устанавливается / очищается с помощью выдвигаемых контроллеров.

class NavigationController: UINavigationController
{
    var popHandler: (() -> Bool)?

    override func popViewController(animated: Bool) -> UIViewController?
    {
        guard self.popHandler?() != false else
        {
            return nil
        }
        self.popHandler = nil
        return super.popViewController(animated: animated)
    }
}

И пример использования из выдвинутого контроллера, который отслеживает несохраненную работу.

let hasUnsavedWork: Bool = // ...
(self.navigationController as! NavigationController).popHandler = hasUnsavedWork ?
    {
        // Prompt saving work here with an alert

        return false // Prevent pop until as user choses to save or discard

    } : nil // No unsaved work, we clear popHandler to let it pop normally

Как приятное прикосновение, это также будет вызвано, interactivePopGestureRecognizerкогда пользователь пытается вернуться назад, используя жест смахивания.

Rivera
источник
превосходный ответ, спасибо Ривера
DvixExtract
2

Это мое решение

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        if let shouldBlock = self.topViewController?.shouldPopFromNavigation() {
            return shouldBlock
        }
        return true
    }
}

extension UIViewController {
    @objc func shouldPopFromNavigation() -> Bool {
        return true
    }
}

В вашем контроллере представления вы можете работать так:

@objc override func shouldPopFromNavigation() -> Bool {
        // Your dialog, example UIAlertViewController or whatever you want
        return false
    }
Хиро
источник
1

Как я понимаю , вы хотите , чтобы опустошить ваш , arrayкак вы жмете кнопку назад и поп - музыки до вашего предыдущего ViewController letвашего , Arrayкоторый вы загрузили на этом экране

let settingArray  = NSMutableArray()
@IBAction func Back(sender: AnyObject) {
    self. settingArray.removeAllObjects()
    self.dismissViewControllerAnimated(true, completion: nil)
} 
Авинаш Мишра
источник
1
    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)
    }
Shahkar
источник
1
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingToParent {

        //your code backView
    }
}
Papon Smc
источник
1

Для Swift 5 , мы можем проверить это в виду исчезнет

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingFromParent {
        delegate?.passValue(clickedImage: selectedImage)
    }
}
Санду
источник
1

Swift 5 __ Xcode 11.5

В моем случае я хотел сделать анимацию, и когда она закончилась, вернуться назад. Способ перезаписать действие по умолчанию для кнопки «Назад» и вызвать собственное действие:

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

    private func setBtnBack() {
        for vw in navigationController?.navigationBar.subviews ?? [] where "\(vw.classForCoder)" == "_UINavigationBarContentView" {
            print("\(vw.classForCoder)")
            for subVw in vw.subviews where "\(subVw.classForCoder)" == "_UIButtonBarButton" {
                let ctrl = subVw as! UIControl
                ctrl.removeTarget(ctrl.allTargets.first, action: nil, for: .allEvents)
                ctrl.addTarget(self, action: #selector(backBarBtnAction), for: .touchUpInside)
            }
        }
    }


    @objc func backBarBtnAction() {
        doSomethingBeforeBack { [weak self](isEndedOk) in
            if isEndedOk {
                self?.navigationController?.popViewController(animated: true)
            }
        }
    }


    private func doSomethingBeforeBack(completion: @escaping (_ isEndedOk:Bool)->Void ) {
        UIView.animate(withDuration: 0.25, animations: { [weak self] in
            self?.vwTxt.alpha = 0
        }) { (isEnded) in
            completion(isEnded)
        }
    }

NavigationBar просмотр иерархии

Или вы можете использовать этот метод один раз, чтобы исследовать иерархию представления NavigationBar и получить индексы для доступа к представлению _UIButtonBarButton, привести к UIControl, удалить target-action и добавить свои собственные target-actions:

    private func debug_printSubviews(arrSubviews:[UIView]?, level:Int) {
        for (i,subVw) in (arrSubviews ?? []).enumerated() {
            var str = ""
            for _ in 0...level {
                str += "\t"
            }
            str += String(format: "%2d %@",i, "\(subVw.classForCoder)")
            print(str)
            debug_printSubviews(arrSubviews: subVw.subviews, level: level + 1)
        }
    }

    // Set directly the indexs
    private func setBtnBack_method2() {
        // Remove or comment the print lines
        debug_printSubviews(arrSubviews: navigationController?.navigationBar.subviews, level: 0)   
        let ctrl = navigationController?.navigationBar.subviews[1].subviews[0] as! UIControl
        print("ctrl.allTargets: \(ctrl.allTargets)")
        ctrl.removeTarget(ctrl.allTargets.first, action: nil, for: .allEvents)
        print("ctrl.allTargets: \(ctrl.allTargets)")
        ctrl.addTarget(self, action: #selector(backBarBtnAction), for: .touchUpInside)
        print("ctrl.allTargets: \(ctrl.allTargets)")
    }
Мигель Гальего
источник
0

Я сделал это путем вызова / переопределения viewWillDisappearи затем доступа к стеку, navigationControllerкак это:

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
        }
    }
}
NerdyTherapist
источник
0

Вот как я решил это для своей проблемы

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.navigationItem.leftBarButtonItem?.action = #selector(self.back(sender:))
    self.navigationItem.leftBarButtonItem?.target = self
}

@objc func back(sender: UIBarButtonItem) {

}
amorenew
источник
0

Вот самое простое из возможных решений Swift 5, для которых не требуется создавать пользовательскую кнопку «Назад» и отказаться от всей функциональности левой кнопки UINavigationController, которую вы получаете бесплатно.

Как рекомендует Брэндон А выше, вам необходимо реализовать UINavigationControllerDelegateконтроллер представления, с которым вы хотите взаимодействовать, прежде чем вернуться к нему. Хороший способ состоит в том, чтобы создать переход, который вы можете выполнить вручную или автоматически, и повторно использовать тот же код из пользовательской кнопки «Готово» или кнопки «Назад».

Во-первых, сделайте ваш интересующий контроллер представления (тот, к которому вы хотите обнаружить возвращение) делегатом контроллера навигации в его viewDidLoad:

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

Во-вторых, добавьте расширение внизу файла, которое переопределяет navigationController(willShow:animated:)

extension PickerTableViewController: UINavigationControllerDelegate {

    func navigationController(_ navigationController: UINavigationController,
                              willShow viewController: UIViewController,
                              animated: Bool) {

        if let _ = viewController as? EditComicBookViewController {

            let selectedItemRow = itemList.firstIndex(of: selectedItemName)
            selectedItemIndex = IndexPath(row: selectedItemRow!, section: 0)

            if let selectedCell = tableView.cellForRow(at: selectedItemIndex) {
                performSegue(withIdentifier: "PickedItem", sender: selectedCell)
            }
        }
    }
}

Так как ваш вопрос включал в себя UITableViewController, я включил способ получить путь индекса строки, которую пользователь коснулся.

Джон Павли
источник