Swift Custom Back Button and Destination - ios

I am currently using SWRevealViewController for a sidebar menu in my app. When I click one of the options, the destination view controller doesn't have a 'back' button because it hasn't come from a proper view controller (i.e. page to go back to).
Therefore I am wanting to manually create a back button on the destination view controller which will go back to the home view controller.
I have looked at the code here: How do I manually set the "Back" destination in iOS apps
But I am struggling to implement this in Swift (one error after another!). Any help? Thanks!
EDIT
I have tried the suggestion below, but the back button just doesn't appear. This may have something to with the fact I have the navbar hidden in other views and do the following on the destination view:
override func viewDidLoad() {
super.viewDidLoad()
navigationController.setNavigationBarHidden(false, animated:true)
var myBackButton:UIButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
myBackButton.addTarget(self, action: "popToRoot:", forControlEvents: UIControlEvents.TouchUpInside)
var myCustomBackButtonItem:UIBarButtonItem = UIBarButtonItem(customView: myBackButton)
self.navigationItem.leftBarButtonItem = myCustomBackButtonItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func popToRoot(sender:UIBarButtonItem){
self.navigationController.popToRootViewControllerAnimated(true)
}
Not sure why the back button won't show up?
Edit
This is the prepareForSegue from my sidebar view controller. If there is a way to check for the segue identifier 'test' then I can set the back button from here?
- (void) prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender
{
// Set the title of navigation bar by using the menu items
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
UINavigationController *destViewController = (UINavigationController*)segue.destinationViewController;
destViewController.title = [[_menuItems objectAtIndex:indexPath.row] capitalizedString];
if ( [segue isKindOfClass: [SWRevealViewControllerSegue class]] ) {
SWRevealViewControllerSegue *swSegue = (SWRevealViewControllerSegue*) segue;
swSegue.performBlock = ^(SWRevealViewControllerSegue* rvc_segue, UIViewController* svc, UIViewController* dvc) {
UINavigationController* navController = (UINavigationController*)self.revealViewController.frontViewController;
[navController setViewControllers: #[dvc] animated: NO ];
[self.revealViewController setFrontViewPosition: FrontViewPositionLeft animated: YES];
};
}
}

You can write that in swift like this
Write this to add button on navigationController
navigationController.setNavigationBarHidden(false, animated:true)
var myBackButton:UIButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
myBackButton.addTarget(self, action: "popToRoot:", forControlEvents: UIControlEvents.TouchUpInside)
myBackButton.setTitle("YOUR TITLE", forState: UIControlState.Normal)
myBackButton.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
myBackButton.sizeToFit()
var myCustomBackButtonItem:UIBarButtonItem = UIBarButtonItem(customView: myBackButton)
self.navigationItem.leftBarButtonItem = myCustomBackButtonItem
this will pops to rootViewController
func popToRoot(sender:UIBarButtonItem){
self.navigationController.popToRootViewControllerAnimated(true)
}

Swift 3.0 Solution:
self.navigationController?.setNavigationBarHidden(false, animated:false)
//Create back button of type custom
let myBackButton:UIButton = UIButton.init(type: .custom)
myBackButton.addTarget(self, action: #selector(ViewController.popToRoot(sender:)), for: .touchUpInside)
myBackButton.setTitle("Back".localized(), for: .normal)
myBackButton.setTitleColor(.blue, for: .normal)
myBackButton.sizeToFit()
//Add back button to navigationBar as left Button
let myCustomBackButtonItem:UIBarButtonItem = UIBarButtonItem(customView: myBackButton)
self.navigationItem.leftBarButtonItem = myCustomBackButtonItem
func popToRoot(sender:UIBarButtonItem){
_ = self.navigationController?.popToRootViewController(animated: true)
}

In Swift 2.1
I am customizing Navigation Bar Items this way
I can change Navigation bar background image, title text, text color
I can show/hide back button
I can Change Title/ Icon or custom action method
In AppDelegate I have
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
//Cutomize navigation bar
let navBgImage:UIImage = UIImage(named: "dummy_navigation_bg_image")!
UINavigationBar.appearance().setBackgroundImage(navBgImage, forBarMetrics: .Default)
UINavigationBar.appearance().tintColor = UIColor.whiteColor()
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName : UIColor.whiteColor()];
return true
}
In ViewController I update Navigation Items in viewWillAppear method this way
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
setupNavigationBar()
}
// MARK: - NavigationBar methods
func setupNavigationBar ()
{
//current tab screen title
tabBarController?.title = "Manage"
//Hide back button or left bar button
tabBarController?.navigationItem.leftBarButtonItem = UIBarButtonItem(title: " ", style: .Plain, target: nil, action: nil)
//custom right bar button
var image = UIImage(named: "dummy_settings_icon")
image = image?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
tabBarController?.navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, style: UIBarButtonItemStyle.Plain, target: self, action: "settingButtonAction")
}
func settingButtonAction()
{
print("Setting Button tapped")
performSegueWithIdentifier("loadAccountSettingsSegue", sender: nil)
}

Here's an updated version for Swift 2/2.1:
self.navigationController? .setNavigationBarHidden(false, animated:true)
let backButton = UIButton(type: UIButtonType.Custom)
backButton.addTarget(self, action: "methodCall:", forControlEvents: UIControlEvents.TouchUpInside)
backButton.setTitle("TITLE", forState: UIControlState.Normal)
backButton.sizeToFit()
let backButtonItem = UIBarButtonItem(customView: backButton)
self.navigationItem.leftBarButtonItem = backButtonItem

Swfit 5 / XCode 11.4
override func viewDidLoad() {
super.viewDidLoad()
navigationController!.setNavigationBarHidden(false, animated: true)
let myBackButton:UIButton = UIButton(type: UIButton.ButtonType.custom) as UIButton
myBackButton.addTarget(self, action: #selector(popToRoot), for: UIControl.Event.touchUpInside)
myBackButton.setTitle("YOUR TITLE", for: UIControl.State.normal)
myBackButton.setTitleColor(UIColor.white, for: UIControl.State.normal)
myBackButton.sizeToFit()
let myCustomBackButtonItem:UIBarButtonItem = UIBarButtonItem(customView: myBackButton)
self.navigationItem.leftBarButtonItem = myCustomBackButtonItem
}
Also, popToRoot you should mark as #objc
#objc func popToRoot() {
//enter action here
}

Related

Hide Back Button Title on iOS 13

Our application has a deployment target of iOS 10. We're hiding all back button titles (for the entire app) with the following code
let attributes: [NSAttributedString.Key : Any] = [
.font : UIFont.systemFont(ofSize: 0.001),
.foregroundColor: UIColor.clear
]
let barButtonItemAppearance = UIBarButtonItem.appearance(whenContainedInInstancesOf: [UINavigationBar.self])
barButtonItemAppearance.setTitleTextAttributes(attributes, for: .normal)
barButtonItemAppearance.setTitleTextAttributes(attributes, for: .highlighted)
This worked up until iOS 13, where the back button titles are no longer hidden.
I saw the WWDC 2019 video about the new apperance APIs, but as far as I can tell it only told me how to use the new APIs for a single navigation bar (and not for the entire app).
How can I achieve this?
Add in viewDidLoad() method of the parent(previous in the stack) controller.
self.navigationItem.backBarButtonItem = UIBarButtonItem(title:"", style:.plain, target:nil, action:nil)
In the next screen, only the arrow image will be presented.
or you can put this code before pushViewController method, for example
func presentNextScreen(_ controller: UIViewController){
// Suppress title in the back button on the next screen.
self.navigationItem.backBarButtonItem = UIBarButtonItem(title:"", style:.plain, target:nil, action:nil)
self.navigationController?.pushViewController(controller, animated: true)
}
For me, the easiest way to hide the title in the back button is to configure the appearance of UINavigationBar
if #available(iOS 13.0, *) {
let standartAppearence = UINavigationBarAppearance()
standartAppearence.configureWithDefaultBackground()
let backButtonAppearence = UIBarButtonItemAppearance()
let titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.clear]
backButtonAppearence.normal.titleTextAttributes = titleTextAttributes
backButtonAppearence.highlighted.titleTextAttributes = titleTextAttributes
standartAppearence.backButtonAppearance = backButtonAppearence
UINavigationBar.appearance().standardAppearance = standartAppearence
UINavigationBar.appearance().compactAppearance = standartAppearence
UINavigationBar.appearance().scrollEdgeAppearance = standartAppearence
}
In addition, here you can add any other configuration of the navigation bar
A workaround:
Create a base class: BaseViewController.
class BaseViewController: UIViewController {
func hideBackButton() {
self.navigationItem.leftBarButtonItem = nil
}
func hideCloseButton() {
self.navigationItem.rightBarButtonItem = nil
}
override public func viewDidLoad() {
super.viewDidLoad()
addNavigationButtons()
}
func addNavigationButtons() {
let backButton = UIBarButtonItem(image: UIImage(named: "back"), style: .plain, target: self, action: #selector(goBack))
self.navigationItem.leftBarButtonItem = backButton
let closeButton = UIBarButtonItem(image: UIImage(named: "close"), style: .plain, target: self, action: #selector(closeTheFlow))
self.navigationItem.rightBarButtonItem = closeButton
}
#objc func goBack() {
//Go Back
}
#objc func closeTheFlow() {
//Close
}
Make every ViewController a subclass of BaseViewController.
You could do anything with the right and left buttons in the entire app.
I hide title gloabal. a trick
UIBarButtonItem.appearance().setBackButtonTitlePositionAdjustment(UIOffset(horizontal: -UIScreen.main.bounds.size.width, vertical: 0), for: UIBarMetrics.default)

Back UIBarButtonItem appearance remove text and change image

I've watched a lot of questions like this and didn't find an answer for my question.
That how i do now:
APPDELEGATE (didFinishLaunchingWithOptions)
// Text
let barButtonItem = UIBarButtonItem.appearance()
barButtonItem.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: UIControlState.normal)
barButtonItem.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: UIControlState.highlighted)
// Image
let backImage = UIImage(named: "arrow_left"
UINavigationBar.appearance().backIndicatorImage = backImage
UINavigationBar.appearance().backIndicatorTransitionMaskImage = backImage
And this almost fit to what i need, but the screen title shifted to the right as there is an invisible back button text. And it definetly is (the root controller's title has 9 characters length):
The question is: How to change image, hide text and keep standart back action for every appearance of back button in ios 9.0 ?
There are three ways to do what you want.
I recommend: Create your own Navigation Class by extending and UINavigationController and override backbuttonItem (UIBarButtonItem) property to customise it according to your requirement. And use the same Navigation Controller class in your project.
Create a custom backBarButton by extending UIBarButtonItem and manually set the same as a back button of default Navigation Controller class, in all view controller.
Hide default navigation bar from root controller and create your own navigation bar using UIView and UIButton in all view controllers. (I always use this choice, that makes customization of navigation bar very easy for me. And I can set my view according to my requirement)
Here is how you can add Custom button for your navigation bar
let btnleft : UIButton = UIButton(frame: CGRect(x:0, y:0, width:35, height:35))
btnleft.contentMode = .center
btnleft.setImage(Set_Local_Image("arrow_left"), for: .normal)
btnleft.addTarget(self, action: #selector(YOUR_ACTION), for: .touchDown)
let backBarButon: UIBarButtonItem = UIBarButtonItem(customView: btnleft)
self.navigationItem.setLeftBarButtonItems([menuBarButon], animated:false)
instead of "arrow_left" You can use any image you want
For Default back action you can create function(YOUR_ACTION) and use in selector of back button
navController.popViewController(animated: true)
I can suggest you 2 options. Both require BaseViewController class as a superclass of all your view controllers.
If you are ok with native back button image, just want to remove back button text you can use this subclass:
class BaseViewController: UIViewController {
var navigationTitle: String = ""
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if !navigationTitle.isEmpty {
navigationItem.title = navigationTitle
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationTitle = navigationItem.title ?? ""
navigationItem.title = ""
}
}
If you want to use your custom icon for back button, you should create UIBarButtonItem with your image, add target, selector, handle action of the button. Sample BaseViewController class below:
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let backImage = UIImage(named: "arrow_left")
navigationItem.hidesBackButton = true
guard let navigationController = navigationController else {
return
}
if navigationController.viewControllers.count > 1 {
// we have to set back button only when we have at least 1 controller to go back
navigationItem.leftBarButtonItem = UIBarButtonItem(image: backImage, style: .plain, target: self, action: #selector(backBarButtonAction(sender:)))
}
}
// MARK: Actions
func backBarButtonAction(sender: UIBarButtonItem) {
navigationController?.popViewController(animated: true)
}
}
According to https://stackoverflow.com/a/16831482/5790492 there is a way to do this without appearance.
Swift 3.0
extension UIViewController {
func setupCustomBackButton() {
if let controllersCount = navigationController?.viewControllers.count, controllersCount > 1 {
let backButton = UIButton(frame: CGRect(x: 0, y: 0, width: 12, height: 20))
backButton.setBackgroundImage(UIImage(named: "arrow_left"), for: .normal)
backButton.contentMode = .left
let backButtonItem = UIBarButtonItem(customView: backButton)
backButton.addTarget(self, action: #selector(self.popCurrentViewController), for: .touchUpInside)
navigationItem.leftBarButtonItem = backButtonItem
navigationItem.hidesBackButton = true
}
}
func popCurrentViewController() {
navigationController?.popViewController(animated: true)
}
}
UINavigationBar.appearance().setBackgroundImage(UIImage(), for:
UIBarPosition.any, barMetrics: UIBarMetrics.default)
UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().barTintColor = UIColor.main
UINavigationBar.appearance().isTranslucent = false
UINavigationBar.appearance().clipsToBounds = false
UINavigationBar.appearance().backgroundColor = UIColor.main
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName :
(UIFont(name: "Helvetica", size: 18))!, NSForegroundColorAttributeName:
UIColor.white]
Try this code and make changes accordingly to set image, color and other properties
You should watch Mark Moeykens' youtube series on this. He is IMHO one of the best YouTube presenters for UI Design and implementation in Swift.
The play list is https://www.youtube.com/playlist?list=PLHDMmeIMXj8WyvlX5uFmppVn2Pm0bXVr7
Create an extension for UINavigationItem
extension UINavigationItem {
func backBarButtonItem() -> UINavigationItem {
return UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
}
}

How to change UINavigationController Title & backButton programming?

I have two viewControllers.
vc1 -> presentVC -> vc2
vc2 inherit UINavigationController
I want to set title & backButton in vc2,but it doesn't work.
class vc2: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
// set title!!!
//self.navigationItem.title = "123"
//self.navigationController?.navigationBar.topItem?.title = "123"
//self.title = "123"
//self.navigationBar.topItem?.title = "123"
//self.navigationItem.title = "123"
// set backButton!!!
let navButtonWidth:CGFloat = 44
let backButton:UIButton = UIButton()
backButton.setImage(backImage, for: .normal)
backButton.addTarget(self, action: #selector(back), for: .touchUpInside)
self.navigationItem.leftBarButtonItems = [UIBarButtonItem(customView: backButton)]
}
Place the code below in perform(segue) or viewWillDissappear depended on how do you do your presentation - via Storyboard segue or manually from code.
let backButton = UIBarButtonItem()
backButton.title = "whatever_you_want"
navigationItem.backBarButtonItem = backButton
And in viewDidLoad of your vc2 simply put
navigationItem.title = "Controller title"
I can change it in storyboard Or change it in code.
I usually do this.
First hide navigation bar from default navigation controller.
navigationController?.navigationBar.hidden = true
Create navigation bar in storyboard and outlet it.
#IBOutlet weak var navigationBar: UINavigationBar!
Create custom navigation item.
private lazy var customNavigationItem: UINavigationItem = {
let navigationItem = UINavigationItem()
let backButton = UIBarButtonItem(image: UIImage(named: "cancel_icon"), style: .Plain, target: self, action: #selector(cancelTapped))
navigationItem.leftBarButtonItem = backButton
navigationItem.title = "Your Title"
return navigationItem
}()
Add custom navigation item to navigation bar
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.hidden = true
navigationBar.setItems([customNavigationItem], animated: false)
}
Hope it help.

Swift - Help Converting from Objective C Syntax

I'm trying to replace the UINavigationController BackBarButtonItem with a custom image, and I would like there to be no back icon like it's currently doing here:
I'm taking this picture from another stack post, but something similar is happening with mine
The solution to this was listed here: Remove back arrow in iOS7
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"back-btn"]
style:UIBarButtonItemStylePlain
target:nil
action:nil];
if ([UINavigationBar instancesRespondToSelector:#selector(setBackIndicatorImage:)]) {
[[UINavigationBar appearance] setBackIndicatorImage:[[UIImage alloc] init]];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[[UIImage alloc] init]];
}
The problem is I'm having trouble converting this code to Swift. If anybody could help me that would be greatly appreciated. Thanks in advance!
For getting swipe back feature :
First set delegate in viewDidLoad:
self.navigationController!.interactivePopGestureRecognizer.delegate = self
And then disable gesture when pushing:
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
super.pushViewController(viewController, animated: animated)
self.interactivePopGestureRecognizer.isEnabled = false
}
And enable in viewDidDisappear:
self.navigationController!.interactivePopGestureRecognizer.isEnabled = true
Use below code:
//Hide Default Back Button First
self.navigationItem.setHidesBackButton(true, animated:true);
//Your code to show back button
let backButton = UIButton()
backButton.setImage(UIImage(named: "imagename"), forState: .Normal)
backButton.frame = CGRectMake(0, 0, 30, 30)
backButton.addTarget(self, action: Selector("action"), forControlEvents: .TouchUpInside)
let rightBarButton = UIBarButtonItem()
rightBarButton.customView = backButton
self.navigationItem.leftBarButtonItem = rightBarButton
//Back Button Action
func action()
{
self.navigationController?.popViewControllerAnimated(true)
}
Or you can refer one of the answer
Use following code in swift 2.2 (xocde 7.3) :
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.setBackButton()
}
func setBackButton() {
let backImage = UIImage(named:"back_icon") as UIImage!
let backButton = UIBarButtonItem(image: backImage, style:UIBarButtonItemStyle.Plain, target: self, action: #selector(BaseViewController.viewWillDisappearC) )
self.navigationItem.leftBarButtonItem = backButton
}
func viewWillDisappearC() {
self.navigationController?.popViewControllerAnimated(true)
}
}
Use following code in swift 3 :
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.setBackButton()
}
func setBackButton() {
let backImage = UIImage(named:"back_icon") as UIImage!
let backButton = UIBarButtonItem(image: backImage, style: UIBarButtonItemStyle.plain, target: self, action: #selector(BaseViewController.viewWillDisappearC) )
self.navigationItem.leftBarButtonItem = backButton
}
func viewWillDisappearC() {
_ = self.navigationController?.popViewController(animated: true)
}
}

JSQmessageView Controller: Add "Back" Button & Image on Navigation Bar, Swift

I'm using JSQmessageViewController and trying to programmatically add "back" button & user image on the navigation bar. I'm using the following codes. After running it, there's no "back" button or image. Attached is the screenshot of the simulator. I tested these codes with the normal UIViewController, and they worked.
May I know why they don't work with the JSQmessageViewController? And what should I do to add the "back" button & image on the navigation bar? Thanks a lot!
let navigationBar = UINavigationBar(frame: CGRectMake(0, 0, self.view.frame.size.width, 64))
navigationBar.backgroundColor = UIColor.whiteColor()
navigationBar.delegate = self;
let navigationItem = UINavigationItem()
navigationItem.title = strValue
let leftButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "back:")
self.navigationItem.leftBarButtonItem = leftButton
let imgButton = UIButton()
imgButton.setImage(image, forState: .Normal)
imgButton.addTarget(self, action: "EditProfile:", forControlEvents: UIControlEvents.TouchDown)
imgButton.frame = CGRectMake(self.view.frame.width - 60, 0, 41, self.view.frame.height)
var rightButton = UIBarButtonItem(customView: imgButton)
self.navigationItem.rightBarButtonItem = rightButton
navigationBar.items = [navigationItem]
self.view.addSubview(navigationBar)
}
So if you use a navigation controller to present your instantiation of the JSQMessagesViewController then the navigation bar should actually already be there. You can just provide it with those buttons and they will be in the correct place. It also seems that you may be doing things in an outdated syntax. Here is the latest Syntax.
Create a function to add your back button.
func addCancelButtonLeft() {
let button = UIBarButtonItem(barButtonSystemItem: .back, target: self, action: #selector(dismissView))
navigationItem.leftBarButtonItem = button
}
Create the action for the button.
func dismissView() {
dismiss(animated: true, completion: nil)
}
Then for your Image you are actually trying to put a button in that title view. Which is fine.
func titleView() {
let imgButton = UIButton()
imgButton.setImage(image, forState: .Normal)
imgButton.addTarget(self, action: #selector(EditProfile), forControlEvents: .touchUpInside)
navigationItem.titleView = imgButton
}
func EditProfile() {
navigationController?.present(EditProfileViewController(), animated: true, completion: nil)
}
let me know if you have more questions and I will see what I can do. Good luck 👍🏼

Resources