Jalankan tindakan saat tombol bilah belakang UINavigationController ditekan


204

Saya perlu menjalankan tindakan (mengosongkan array), ketika tombol kembali UINavigationControllerditekan, sedangkan tombol masih menyebabkan sebelumnya ViewControllerpada tumpukan muncul. Bagaimana saya bisa mencapai ini menggunakan swift? masukkan deskripsi gambar di sini

Jawaban:


152

Salah satu opsi akan menerapkan tombol kembali kustom Anda sendiri. Anda perlu menambahkan kode berikut ke metode viewDidLoad Anda:

- (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];
}

MEMPERBARUI:

Ini adalah versi untuk 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)
    }

PEMBARUAN 2:

Ini adalah versi untuk 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
Ini tidak muncul ke pengontrol tampilan sebelumnya; itu muncul ke controller tampilan root.
berbatu

91
Bagaimana saya bisa memiliki panah seperti tombol kembali biasa?
TomSawyer

@rocky Anda dapat mencoba fungsi back line di bawah ini: [self.navigationController dismissViewControllerAnimated: YES completion: nil];
malajisi

2
@ TomSawyer Untuk itu, silakan lihat jawaban di bawah ini
fr33g

7
Melakukan penggantian tombol sistem untuk mengganti fitur bukanlah cara yang baik. Cara terbaik adalah jawabannya di bawah ini! stackoverflow.com/a/27715660/2307276
dpizzuto

478

Mengganti tombol ke kustom seperti yang disarankan pada jawaban lain mungkin bukan ide bagus karena Anda akan kehilangan perilaku dan gaya default.

Satu opsi lain yang Anda miliki adalah menerapkan metode viewWillDisappear pada View Controller dan memeriksa properti bernama isMovingFromParentViewController . Jika properti itu benar, artinya View Controller menghilang karena sedang dihapus (muncul).

Seharusnya terlihat seperti:

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

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

Dalam cepat 4.2

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

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

5
@ gogogames ya, Anda tidak bisa melakukan itu. Tapi pertanyaannya tidak menanyakan itu. Untuk dapat menghentikan tindakan untuk kembali, saya kira Anda benar-benar perlu mengganti tombol.
manecosta

13
Untuk Swift 3.1 :override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if isMovingFromParentViewController { // Your code... } }
Doug Amos

23
viewWillDisappear(animated:)akan terpicu jika Anda mendapat panggilan telepon. Ini mungkin bukan yang Anda inginkan. Mungkin lebih baik untuk digunakanwillMove(toParentViewController:)
Joe Susnick

di Swift 4, hilang : override func viewWillDisappear ( animasi: Bool)
Javier Calatrava Llavería

1
Ini harus menjadi jawaban yang diterima. Bersih dan sederhana.
temp

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
Tidak berfungsi di Swift3 / iOS10, animasi pop 'nested pop printing konsol dapat mengakibatkan bilah navigasi rusak'.
itsji10dra

1
Tidak dipanggil sama sekali
zulkarnain shah

3
Ini juga dipanggil ketika pindah ke VC baru, bukan hanya ketika kembali.
Jose Ramirez

Sesuai komentar @JozemiteApps, itu ada di dokumen yang Dipanggil sesaat sebelum pengontrol tampilan ditambahkan atau dihapus dari pengontrol tampilan wadah. .
nstein

2
Ini harus menjadi jawaban yang diterima. Dan kapan parent == nilsaatnya kita kembali ke tempat parentkejadian
Sylvan D Ash

32

Saya dapat mencapai ini dengan yang berikut:

Cepat 3

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

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

Cepat 4

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

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

Tidak perlu tombol kembali kustom.


Ini fantastik. Komentar lama tetapi masih bekerja dengan Swift terbaru.
user3204765

Ini dipicu (false positive) juga ketika melepas dari pengontrol tampilan berikutnya (lebih dari yang ini) sehingga tidak benar-benar kembali deteksi tekan tombol.
user2878850

Komentar yang sama seperti orang sebelumnya, kode ini tidak mendeteksi aktivasi tombol kembali, tetapi pop tampilan saat ini.
Vilmir

31

Saya membuat kelas (cepat) ini untuk membuat tombol kembali persis seperti yang biasa, termasuk panah kembali. Itu dapat membuat tombol dengan teks biasa atau dengan gambar.

Pemakaian

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

(kode untuk menggambar panah belakang yang dibuat dengan plugin 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
        }
    }
}

Apakah Anda akan begitu baik untuk memperbarui jawaban Anda untuk iOS 11?
BR41N-FCK

2
Hai @guido, solusi Anda sempurna, saya mencoba kode Anda dan memperhatikan bahwa ada ruang di depan tombol kembali meskipun Anda menambahkan barbutton dengan lebar negatif.
Pawriwes

26

Jika Anda ingin memiliki tombol kembali dengan panah belakang Anda dapat menggunakan gambar dan kode di bawah ini

backArrow.png panah1backArrow@2x.png panah2backArrow@3x.pngpanah3

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

Jika Anda menggunakan navigationControllerkemudian tambahkan UINavigationControllerDelegateprotokol ke kelas dan tambahkan metode delegasi sebagai berikut:

class ViewController:UINavigationControllerDelegate {

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

Metode ini dipanggil setiap kali pengendali navigasi akan meluncur ke layar baru. Jika tombol kembali ditekan, controller tampilan baru itu ViewControllersendiri.


Yang menjadi mengerikan ketika menggunakan kelas non NSObjectProtocol sebagai delegasi.
Nick Weaver

Itu tidak selalu dipanggil ketika tombol kembali ditekan.
Ted

9

Di Swift 5 dan Xcode 10.2

Tolong jangan tambahkan item tombol bilah kustom, gunakan perilaku default ini.

Tidak perlu viewWillDisappear , tidak perlu BarButtonItem kustom dll ...

Lebih baik mendeteksi ketika VC dihapus dari orang tuanya.

Gunakan salah satu dari dua fungsi ini

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

Jika Anda ingin menghentikan perilaku default tombol kembali kemudian tambahkan BarButtonItem kustom.


1
Perhatikan bahwa ini juga dipanggil ketika Anda muncul secara terprogram, tidak hanya menekan tombol kembali.
Ixx

7

TIDAK

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

Ini akan dipanggil bahkan jika Anda segueing ke pengontrol tampilan di mana Anda menimpa metode ini. Di mana memeriksa apakah " parent" nilbukan bukan cara yang tepat untuk memastikan kembali ke yang benar UIViewController. Untuk menentukan dengan tepat apakah UINavigationControllernavigasi yang benar kembali ke UIViewControlleryang disajikan saat ini, Anda harus menyesuaikan diri dengan UINavigationControllerDelegateprotokol.

IYA

catatan: MyViewControllerhanya nama apa pun UIViewControlleryang ingin Anda deteksi untuk kembali.

1) Di bagian atas file Anda tambahkan UINavigationControllerDelegate.

class MyViewController: UIViewController, UINavigationControllerDelegate {

2) Tambahkan properti ke kelas Anda yang akan melacak dari UIViewControlleryang Anda segueing dari.

class MyViewController: UIViewController, UINavigationControllerDelegate {

var previousViewController:UIViewController

3) di MyViewController's viewDidLoadmetode menetapkan selfsebagai delegasi untuk Anda UINavigationController.

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

3) Sebelum Anda melakukan segmentasi , tetapkan yang sebelumnya UIViewControllersebagai properti ini.

// 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) Dan sesuai dengan salah satu metode dalam MyViewControllersatuUINavigationControllerDelegate

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

1
Terima kasih atas jawaban bermanfaatnya! Pembaca berhati-hatilah dalam mengatur delegasi UINavigationController ke pengontrol tampilan tertentu; jika pengendali navigasi sudah memiliki delegasi, Anda berisiko kehilangan delegasi panggilan balik yang diharapkannya. Dalam aplikasi kami, delegasi UINavigationController adalah objek yang dibagikan (AppCoordinator) tempat semua pengontrol tampilan memiliki penunjuk.
Bill Feth

7

Dalam kasus saya yang viewWillDisappearterbaik. Tetapi dalam beberapa kasus kita harus memodifikasi controller tampilan sebelumnya. Jadi, inilah solusi saya dengan akses ke view controller sebelumnya dan ini berfungsi di 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 (atau -viewWillDisappear) akan dipanggil bahkan jika tampilan dilindungi oleh tampilan pengontrol tampilan lain (bukan hanya ketika tombol <Back ditekan), maka kebutuhan untuk memeriksa isMovingFromParentViewController.
Bill Feth

5

Sebelum meninggalkan pengontrol saat ini saya harus menunjukkan peringatan. Jadi saya melakukannya dengan cara ini:

  1. Tambahkan ekstensi ke UINavigationControllerdenganUINavigationBarDelegate
  2. Tambahkan pemilih ke navigasi controller AndaShouldPopOnBack (selesai :)

Itu berhasil)

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

Tidak sulit seperti kita. Cukup buat bingkai untuk UIButton dengan warna latar belakang yang jelas, tetapkan tindakan untuk tombol dan letakkan di atas tombol navigasi belakang. Dan akhirnya lepaskan tombol setelah digunakan.

Berikut adalah contoh kode Swift 3 yang dilakukan dengan UIImage, bukan 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)
    }

menulis kode perlu dieksekusi

func back(sender: UIBarButtonItem) {

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

    }

Hapus subView setelah tindakan dilakukan

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

    for view in (self.navigationController?.navigationBar.subviews)!{
        if view.tag == 1 {
            view.removeFromSuperview()
        }
    }

Terima kasih Bung . :-)
ARSHWIN DENUEV LAL

Bagaimana Anda membuat negara saat mendarat?
quang thang

Tampaknya ini tidak berfungsi di iOS 11. Tidak ketika warna latar belakang UIImageView jelas. Atur ke warna yang berbeda dan itu berfungsi.
Ketuk Formulir

Kita dapat mendefinisikan UIImageView dengan warna yang jelas, mengatur bingkainya, menetapkan tapgesture dan menempatkannya di mana saja di layar. Lalu mengapa kita tidak bisa meletakkannya di bilah navigasi. Untuk menjadi tulus saya tidak akan merekomendasikan apa yang saya tulis. Jika ada masalah pasti ada alasan tetapi bukan masalah warna. Lupakan kodenya ikuti logika kamu akan berhasil. :)
ARSHWIN DENUEV LAL

4

Swift 4.2:

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

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

    }
}

3

Swift 3:

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

    if parent == nil{
        print("Back button was clicked")
    }
}

-did / willMove (toParentViewController :) mungkin lebih baik daripada memeriksa isMovingTfromParentViewController di -viewWillDisappear karena hanya dipanggil ketika view controller benar-benar mengubah orang tua (bukan ketika tampilan ditutupi oleh tampilan VC lain) Tetapi lebih banyak solusi "benar" adalah untuk menerapkan metode delegasi UINavigationController. Namun berhati-hatilah; jika NavigationController sudah memiliki delegasi, Anda berisiko kehilangan delegasi callback lain yang diharapkannya.
Bill Feth

Saya diuji dengan splitViewController. Di sana, tidak bisa membuat perbedaan antara ditambahkan atau dihapus.
claude31

2

Coba ini .

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

Coba ini juga.

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

2

cukup kontrol + seret bilah item ke func di bawah ini. bekerja seperti pesona

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

masukkan deskripsi gambar di sini


2

Anda dapat subkelas UINavigationControllerdan menimpa popViewController(animated: Bool). Selain dapat menjalankan beberapa kode di sana, Anda juga dapat mencegah pengguna kembali sama sekali, misalnya untuk meminta untuk menyimpan atau membuang pekerjaannya saat ini.

Implementasi sampel di mana Anda dapat mengatur popHandleryang disetel / dihapus oleh pengendali terdorong.

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

Dan contoh penggunaan dari pengontrol terdorong yang melacak pekerjaan yang belum disimpan.

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

Sebagai sentuhan yang bagus, ini juga akan dipanggil interactivePopGestureRecognizerketika pengguna mencoba untuk kembali menggunakan gerakan menggesek.


jawaban superior, Terima kasih Rivera
DvixExtract

2

Ini solusi saya

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

Di pengontrol tampilan Anda, Anda dapat menangani seperti ini:

@objc override func shouldPopFromNavigation() -> Bool {
        // Your dialog, example UIAlertViewController or whatever you want
        return false
    }

1

Seperti yang saya mengerti Anda ingin mengosongkan Anda arraysaat Anda menekan tombol kembali dan pop ke sebelumnya ViewController letAnda Arrayyang Anda muat di layar ini

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

Untuk Swift 5 , kita bisa memeriksanya karena akan menghilang

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

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

1

Swift 5 __ Xcode 11.5

Dalam kasus saya, saya ingin membuat animasi, dan ketika selesai, kembali. Cara untuk menimpa tindakan default tombol kembali dan memanggil tindakan kustom Anda adalah ini:

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

NavigasiBar hierarki tampilan

Atau Anda dapat menggunakan metode ini satu kali untuk menjelajahi hierarki tampilan NavigationBar, dan mendapatkan indeks untuk mengakses tampilan _UIButtonBarButton, dilemparkan ke UIControl, hapus tindakan target, dan tambahkan tindakan target kustom Anda:

    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

Saya menyelesaikan ini dengan memanggil / viewWillDisappearmengganti dan kemudian mengakses tumpukan navigationControllerseperti ini:

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

Ini adalah bagaimana saya menyelesaikannya untuk masalah saya sendiri

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

Ini adalah solusi Swift 5 paling sederhana yang mungkin tidak mengharuskan Anda membuat tombol kembali khusus dan menyerahkan semua fungsi tombol kiri UINavigationController yang Anda dapatkan secara gratis.

Seperti yang direkomendasikan Brandon A di atas, Anda perlu menerapkan UINavigationControllerDelegatecontroller tampilan yang ingin Anda berinteraksi sebelum kembali ke sana. Cara yang baik adalah dengan membuat segmen bersantai yang dapat Anda lakukan secara manual atau otomatis dan menggunakan kembali kode yang sama dari tombol selesai atau tombol kembali.

Pertama, buat pengontrol tampilan Anda menarik (yang ingin Anda deteksi kembali ke) delegasi pengontrol navigasi di viewDidLoad:

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

Kedua, tambahkan ekstensi di bagian bawah file yang menimpa 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)
            }
        }
    }
}

Karena pertanyaan Anda menyertakan a UITableViewController, saya menyertakan cara untuk mendapatkan jalur indeks dari baris yang diketuk pengguna.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.