Виконайте дії, якщо натиснути кнопку задньої панелі UINavigationController


204

Мені потрібно виконати дію (спорожнення масиву), коли натиснута кнопка "назад" UINavigationController, а кнопка все ще викликає появу попередньої ViewControllerв стеці. Як я можу це досягти за допомогою швидкого? введіть тут опис зображення

Відповіді:


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

2
Це не відображається до попереднього контролера перегляду; він спливає на контролер кореневого виду.
скелястий

91
Як я можу мати стрілку, як звичайну кнопку назад?
TomSawyer

@rocky Ви можете спробувати наступний рядок у функції "назад": [self.navigationController dismissViewControllerAnimated: ТАК завершення: нуль];
маладжиї

2
@TomSawyer Для цього перегляньте відповідь нижче
fr33g

7
Заміна системної кнопки для зміни функції не є хорошим способом. Найкращий спосіб - відповідь нижче! stackoverflow.com/a/27715660/2307276
dpizzuto

477

Заміна кнопки на спеціальну, як запропоновано в іншій відповіді, можливо, не є чудовою ідеєю, оскільки ви втратите поведінку та стиль за замовчуванням.

Ще один варіант, який ви маєте, - це реалізувати метод viewWillDisappear на контролері перегляду та перевірити наявність властивості з назвоюMovingFromParentViewController . Якщо це властивість вірно, це означає, що контролер перегляду зникає, оскільки його видаляють (вискакують).

Має виглядати приблизно так:

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

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

Швидкий 4.2

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

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

5
@gmogames так, ви не можете цього зробити. Питання цього не задавав. Щоб мати змогу зупинити дію повернення назад, я думаю, вам дійсно потрібно перекрити кнопку.
манекоста

13
Для Свіфта 3.1 :override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if isMovingFromParentViewController { // Your code... } }
Даг Амос

23
viewWillDisappear(animated:)спрацьовує, якщо ви телефонуєте. Це, ймовірно, не те, що ви хочете. Можливо, краще скористатисяwillMove(toParentViewController:)
Джо Сюснік

у Swift 4, відсутня : перегляд функції funcWillDisappear ( анімовані: Bool)
Хав'єр Калатрава Льєверія

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 shah

3
Цьому також дзвонять під час переїзду в новий ВК, а не лише під час повернення назад.
Хосе Рамірес

Відповідно до коментаря @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()
   }           
}

Швидкий 4

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

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

Не потрібно спеціальної кнопки повернення.


Це фантастика. Старе зауваження, але все ще працює з останнім Swift.
користувач3204765

Це спрацьовує (помилково позитивний) також при відкручуванні від наступного контролера перегляду (над цим), так що насправді не виявляється натискання кнопки назад.
користувач2878850

Це ж зауваження, як і попередня особа, цей код виявляє не активацію кнопки "Назад", а поп поточного перегляду.
Вілмір

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 стрілка1backArrow@2x.png стрілка2backArrow@3x.pngстрілка3

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 тощо ...

Краще виявити, коли вимкнений ПК виходить з його батьківського.

Використовуйте будь-яку з цих двох функцій

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.


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) буде викликатися, навіть якщо представлення охоплюється поданням іншого контролера перегляду (не тільки при натисканні кнопки <Назад), отже, потрібно перевірити isMovingFromParentViewController.
Білл Фет

5

Перш ніж залишити контролер струму, мені потрібно показати попередження. Тому я зробив це так:

  1. Додати додавання до UINavigationControllerсUINavigationBarDelegate
  2. Додайте селектор до навігації контролераShouldPopOnBack (завершення :)

Це спрацювало)

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 з чітким кольором, встановити його кадр, призначити тапгесту і розмістити в будь-якому місці екрана. Тоді чому ми не можемо розмістити його на навігаційній панелі. Щоб бути щирим, я не рекомендую те, що написав. Якщо проблема є, безумовно, є причина, але це не питання кольору. Забудьте код дотримуйтесь логіки, і вам вдасться. :)
АРШВІН ДЕНУЄВ ЛАЛ


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
}

2

просто зробіть керування + перетягніть елемент рядка нижче функції. працювати як шарм

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

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

    if self.isMovingToParent {

        //your code backView
    }
}

1

Для Swift 5 ми можемо перевірити, чи зникне він

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

    if self.isMovingFromParent {
        delegate?.passValue(clickedImage: selectedImage)
    }
}

1

Швидкий 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 та отримати індекси для доступу до перегляду _UIButtonBarButton, передати на UIControl, видалити цільову дію та додати власні цільові дії:

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

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

}

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, я включив спосіб отримати шлях до індексу рядка, який користувач прослухав.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.