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

StevenR
sumber

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)
    }
fr33g
sumber
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...
    }
}
manecosta
sumber
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.")
    }
}
Iya
sumber
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.

Siddharth Bhatt
sumber
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
        }
    }
}
guido
sumber
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 panah1[email protected] panah2[email protected]panah3

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)
}
Leszek Szary
sumber
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.

Ajinkya Patil
sumber
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.

iOS
sumber
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
    }
}
Brandon A
sumber
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()
                }
            }
        }
    }
Bernd
sumber
-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)
    }
Taras
sumber
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()
        }
    }
ARSHWIN DENUEV LAL
sumber
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...

    }
}
Ny. Najmul Hasan
sumber
3

Swift 3:

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

    if parent == nil{
        print("Back button was clicked")
    }
}
Gal
sumber
-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
}
AG
sumber
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

coders
sumber
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.

Rivera
sumber
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
    }
Hiro
sumber
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)
} 
Avinash Mishra
sumber
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
sumber
1
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingToParent {

        //your code backView
    }
}
Papon Smc
sumber
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)
    }
}
Sandu
sumber
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)")
    }
Miguel Gallego
sumber
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
        }
    }
}
NerdyTherapist
sumber
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) {

}
amorenew
sumber
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.

John Pavley
sumber