Peter Hlavatík
Peter Hlavatík

Reputation: 147

Changing the back button of UINavigaitonBar with MVVM+C

I am using MVVM+C pattern to build my app. Currently I am facing a problem with changing the native back button title and image of navigation bar to the custom image without the title. I've tried a lots of solutions what I was able to find, but nothing set the different title or even an image. I've ended up with this code in AppDelegate.swift:

let navigationController: UINavigationController = .init()
    
    if #available(iOS 13.0, *) {
        let appearence = UINavigationBarAppearance()
        appearence.configureWithOpaqueBackground()
        appearence.backgroundColor = .backgroundColor
        appearence.shadowColor = nil
        appearence.shadowImage = nil
        
        navigationController.navigationBar.standardAppearance = appearence
        navigationController.navigationBar.scrollEdgeAppearance = navigationController.navigationBar.standardAppearance
    } else {
        navigationController.navigationBar.isTranslucent = false
        navigationController.navigationBar.barTintColor = .backgroundColor
        navigationController.navigationBar.shadowImage = nil
        navigationController.navigationBar.shadowColor = nil
    }
    // This code is not working at all, always get "Back" as a default with default image =====

    let backButtonBackgroundImage = UIImage(named: "backButton")
    navigationController.navigationBar.backIndicatorImage = backButtonBackgroundImage
    navigationController.navigationBar.backIndicatorTransitionMaskImage = backButtonBackgroundImage
    
    let backBarButtton = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
    navigationController.navigationItem.backBarButtonItem = backBarButtton
    
    // =========

    navigationController.navigationBar.tintColor = .primary
    
    window?.rootViewController = navigationController
    window?.makeKeyAndVisible()

Also, I've followed the official documentation but without any success. As default I've set the navigation bar as hidden (because is not needed for multiple times) and I am showing it in ViewWillAppear and hiding in ViewWillDisappear methods.

Is there someone who has an idea of what's going on? Thanks!

The result of this code: Back button

Expected result: Expected back button

This is what I get with the new code: enter image description here

SOLUTION: After using code from Scott I was able to change the image and look of the navigation bar but I lost the ability to swipe back. After adding this code to the UINavigationBar extension I was able to get it back:

extension UINavigationController: UIGestureRecognizerDelegate {
@objc func goBack(sender: Any?) {
    self.popViewController(animated: true)
}

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

public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    return viewControllers.count > 1
}
}

Upvotes: 0

Views: 656

Answers (1)

Scott Thompson
Scott Thompson

Reputation: 23701

Below is some Playground code that shows a UINavigationController with a custom back button that is an image.

Note that what it does is hides the system provided back button, then substitutes another button that still performs the "back" action but on a custom UINavigationController.

There may be a more efficient way to duplicate the functionality of "back" that doesn't involve a custom class and a custom target-action setup, but I couldn't find one quickly so finding that solution can be left as an exercise for the reader.

import UIKit
import SwiftUI
import PlaygroundSupport

NSSetUncaughtExceptionHandler { error in
    debugPrint(error)
}

class MyNavController : UINavigationController {
    @objc func goBack(sender: Any?) {
        self.popViewController(animated: true)
    }
}

let navDestination1 = UIViewController()
navDestination1.navigationItem.title = "Destination 1"

let navigationController = MyNavController(rootViewController: navDestination1)

if #available(iOS 13.0, *) {
    let appearence = UINavigationBarAppearance()
    appearence.configureWithOpaqueBackground()
    appearence.backgroundColor = .purple
    appearence.shadowColor = nil
    appearence.shadowImage = nil

    navigationController.navigationBar.standardAppearance = appearence
    navigationController.navigationBar.scrollEdgeAppearance = navigationController.navigationBar.standardAppearance
} else {
    navigationController.navigationBar.isTranslucent = false
    navigationController.navigationBar.barTintColor =  .purple
    navigationController.navigationBar.shadowImage = nil
}

let navDestination2 = UITableViewController()
navDestination2.navigationItem.title = "Destination 2"
navDestination2.navigationItem.hidesBackButton = true
navDestination2.navigationItem.leftBarButtonItem = UIBarButtonItem(
    image: UIImage(systemName: "multiply.circle.fill"),
    style: UIBarButtonItem.Style.done,
    target: navigationController,
    action: #selector(MyNavController.goBack))


navigationController.pushViewController(navDestination2, animated: true)

navigationController.view.bounds = CGRect(x: 0,y: 0,width: 320,height: 480)

PlaygroundSupport.PlaygroundPage.current.liveView = navigationController

Upvotes: 1

Related Questions