Swift custom back button in navigation bar - swift

I am trying to use a custom image for my back button in the navigation bar. I am using the below code, which adds the image, but also keeps the text "Back" in the button. I want to also remove the text. Can I do that?
self.navigationController?.navigationBar.backIndicatorImage = UIImage(named: "icon-back")
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage(named: "icon-back")
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)

Try code below :-)
func SetBackBarButtonCustom()
{
//Back buttion
let btnLeftMenu: UIButton = UIButton()
btnLeftMenu.setImage(UIImage(named: "back_arrow"), for: UIControlState())
btnLeftMenu.addTarget(self, action: #selector(UIViewController.onClcikBack), for: UIControlEvents.touchUpInside)
btnLeftMenu.frame = CGRect(x: 0, y: 0, width: 33/2, height: 27/2)
let barButton = UIBarButtonItem(customView: btnLeftMenu)
self.navigationItem.leftBarButtonItem = barButton
}
func onClcikBack()
{
_ = self.navigationController?.popViewController(animated: true)
}

If you want to add Back button in every UIViewController then you can add the code in UIViewController extension else you can use addBackButton() directly as follows.
extension UIViewController {
func addBackButton() {
let btnLeftMenu: UIButton = UIButton()
let image = UIImage(named: "backButtonImage");
btnLeftMenu.setImage(image, for: .normal)
btnLeftMenu.setTitle("Back".localized, for: .normal);
btnLeftMenu.sizeToFit()
btnLeftMenu.addTarget(self, action: #selector (backButtonClick(sender:)), for: .touchUpInside)
let barButton = UIBarButtonItem(customView: btnLeftMenu)
self.navigationItem.leftBarButtonItem = barButton
}
func backButtonClick(sender : UIButton) {
self.navigationController?.popViewController(animated: true);
}
}
Make sure you should add the following file "backButtonImage.png" in your app bundle.
Call this method self.addBackButton()in your viewDidLoad method of your custom UIViewController class like this
override func viewDidLoad() {
super.viewDidLoad()
self.addBackButton()
}
Note : if you don't add addBackButton Method in extension then you will need to add this method directly in the class and set target and Selector accordingly.

Related

InputAccessoryView and my problem (Swift)

I have such screen:
When you click on a textfield, the top of the keyboard should have "two arrows" on the left and the button "Done" on the right.
Like this:
I must say right away that I used Iqkeyboardmanager and because of this library there were problems therefore I had to remove it from the project. So help do it manually.
In the project, I already implemented the Done button, my code:
extension UITextField {
func addInputAccessoryView(title: String, target: Any, selector: Selector) {
let toolBar = UIToolbar(frame: CGRect(x: 0.0,
y: 0.0,
width: UIScreen.main.bounds.size.width,
height: 44.0))
let flexible = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let barButton = UIBarButtonItem(title: title, style: .plain, target: target, action: selector)
toolBar.setItems([flexible, barButton], animated: false)
self.inputAccessoryView = toolBar
}
}
final class ProfileSettingsViewController: UIViewController,UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate {
#objc func tapDone() {
self.view.endEditing(true)
}
override func viewDidLoad() {
super.viewDidLoad()
self.nameTF.addInputAccessoryView(title: "Done", target: self, selector: #selector(tapDone))
self.companyTF.addInputAccessoryView(title: "Done", target: self, selector: #selector(tapDone))
self.nameTF.delegate = self
self.companyTF.delegate = self
.....
The rest of the code
}
}
What i have at the moment:
I need to add two black arrows on the left and make the Done buttons black. Help me please.
If you think my code is bad, you can provide your correct example. I am new in ios development.
1.Create a custom delegate which will notify the ViewController when some button is pressed inside the Input Accessory View.
protocol MyCustomTextFieldDelegate: class {
func doneButtonPressed()
func arrowDownPressed()
func arrowUpPressed()
}
2.Create a custom subclass of the the UITextField and make a function that will create the Input Accessory View. Define the newly created MyCustomTextFieldDelegate as a weak class property. (Note: we are making this subclass, as we are unable to add the MyCustomTextFieldDelegate as a property to the UITextField directly)
class MyCustomTextField: UITextField {
weak var customTextFieldDelegate: MyCustomTextFieldDelegate?
func enableInputAccessoryView() {
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
let space = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self,
action: #selector(textFieldDonePressed))
doneButton.tintColor = .black
let arrowDown = UIBarButtonItem(image: UIImage(named: "arrow-right"), style: .done, target: self, action: #selector(arrowUpPressed))
arrowDown.tintColor = .black
let arrowUp = UIBarButtonItem(image: UIImage(named: "arrow-right"), style: .done, target: self, action: #selector(arrowDownPressed))
arrowUp.tintColor = .black
toolBar.setItems([arrowUp, arrowDown, space, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
toolBar.sizeToFit()
inputAccessoryView = toolBar
}
#objc private func textFieldDonePressed() {
endEditing(true)
customTextFieldDelegate?.doneButtonPressed()
}
#objc private func arrowDownPressed() {
customTextFieldDelegate?.arrowDownPressed()
}
#objc private func arrowUpPressed() {
customTextFieldDelegate?.arrowUpPressed()
}
}
3.In your ViewController change your current UITextField to your new MyCustomTextField and apply the Input Accessory View.
yourTextField.enableInputAccessoryView()
4.Set your ViewController to be the MyCustomTextField delegate.
yourTextField.customTextFieldDelegate = self
5.Confrom your ViewController to the newly created MyCustomTextFieldDelegate, where you will handle the user interaction.
extension YourViewController: MyCustomTextFieldDelegate {
func doneButtonPressed() {
// Handle done Pressed
}
func arrowDownPressed() {
// Handle down pressed
}
func arrowUpPressed() {
// Handle up pressed
}
}

Why Doesn't Swift 3 recognize me button tap?

let playButton: UIButton = {
let button = UIButton()
let image = UIImage(named: "VideoIcon.png") as UIImage?
button.backgroundImage(for: .normal)
button.addTarget(self, action: #selector(pressBackButton(button:)), for: .touchUpInside)
button.setImage(image, for: .normal)
return button
}()
func pressBackButton(button: UIButton) {
print("test")
if let playVideoButtonURL = post?.videourl {
let player = AVPlayer(url: playVideoButtonURL as URL)
let playerLayer = AVPlayerLayer(player:player)
playerLayer.frame = CGRect(x: 100, y: 200, width: 100, height: 100)
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
self.layer.addSublayer(playerLayer)
player.play()
}
}
even if the video code is wrong it should still print test but it doesn't which is strange. I feel like something may be wrong with the selector but i currently have no idea what could be wrong with it.
access function or property in self outside of initializer.
class TestViewController: UIViewController {
let playButton: UIButton = {
let button = UIButton()
let image = UIImage(named: "VideoIcon.png") as UIImage?
button.backgroundImage(for: .normal)
button.setImage(image, for: .normal)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self, action: #selector(pressBackButton(button:)), for: .touchUpInside)
}
}
Instead of doing:
let playButton: UIButton = {
...
...
return button
}()
which won't work (because you're attempting to create playButton at the wrong time -- which is before the view is loaded or before it's about to appear)
In your viewDidLoad or viewWillAppear, create the button, define the target and simply add it as a subview.
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
if let image = UIImage(named: "VideoIcon.png") as UIImage?
{
button.backgroundImage(for: .normal)
button.addTarget(self, action: #selector(pressBackButton(button:)), for: .touchUpInside)
button.setImage(image, for: .normal)
}
self.addSubview(button)
}
Adding the button as a subview increments the retain count (i.e. it won't go away), but if you want to keep it around as a property simply declare it as:
var playButton : UIButton?
and then set playButton = button at the end of viewDidLoad.

navigation bar button text disappears

I have 2 nav bar buttons at the top of the screen, one with an image an another with text like so:
When I present a new view controller with a new nav controller/root controller and then dismiss it, going back to the same view controller, the text disappears like so:
Here's the code for the view controller
override func viewDidLoad() {
setupNavBarButtons()
}
func setupNavBarButtons() {
let searchImage = UIImage(named: "search_icon")?.imageWithRenderingMode(.AlwaysOriginal)
let searchBarButtonItem = UIBarButtonItem(image: searchImage, style: .Plain, target: self, action: #selector(handleSearch))
navigationItem.rightBarButtonItem = searchBarButtonItem
let filterBarButtonItem = UIBarButtonItem(title: "Filter", style: .Plain , target: self, action: #selector(displayFilter))
navigationItem.leftBarButtonItem = filterBarButtonItem
}
func presentAccountController() {
let accountController = AccountController()
accountController.listController = self
let vc = UINavigationController(rootViewController: accountController
presentViewController(vc, animated: true, completion: nil)
}
The controller I'm dismissing to return to the original view controller:
class AccountController: UIViewController, UITableViewDelegate, UITableViewDataSource, MFMailComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
view.backgroundColor = UIColor.rgb(245, green: 245, blue: 245)
let closeButtonItem = UIBarButtonItem(title: "Close", style: .Plain, target: self, action: #selector(dismissController))
navigationItem.leftBarButtonItem = closeButtonItem
}
func dismissController() {
self.dismissViewControllerAnimated(true, completion: nil)
}
I've tried calling it in viewWillLoad, viewDidLoad, viewDidAppear. Still no change and the bug still occurs.
You can do this and create a frame with a wide width when you create the button
lazy var filterButton: UIButton = {
let button = UIButton(type: .system)
button.frame = CGRect(x: 0, y: 0, width: 60, height: 30) // Set the width here to make it wider
button.setTitle("Filter", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 18)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(filterButtonTapped), for: .touchUpInside)
return button
}()
var leftBarButtonItem: UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
leftBarButtonItem = UIBarButtonItem(customView: filterButton)
navigationItem.leftBarButtonItem = leftBarButtonItem
}
#objc func filterButtonTapped() {
print("Filter button tapped")
}

Swift Custom Navigation Bar Item With Observer

So I have this custom navigation bar item that I would like to subclass out and use in other ViewControllers. The issue is that the Bar Item also has an observer that I want to add on ViewDidAppear and remove the observer on ViewDidDisappear. How can I subclass the bar item along with the observer out. And include it in each of my ViewControllers instead of duplicating code?
Current Code:
In each ViewController:
override func viewWillAppear(animated: Bool) {
if (!observingDefaults) {
defaults.addObserver(self, forKeyPath: Constants.General.kConnectedToPC, options: NSKeyValueObservingOptions(), context: nil)
observingDefaults = true
}
self.updateConnectionIcon()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if (observingDefaults) {
defaults.removeObserver(self, forKeyPath: Constants.General.kConnectedToPC)
observingDefaults = false
}
}
My Extension For The Nav Bar Item:
extension UIViewController {
func updateConnectionIcon() {
self.navigationItem.rightBarButtonItem = nil
let isConnected = NSUserDefaults.standardUserDefaults().boolForKey(Constants.General.kConnectedToPC)
let button = UIButton()
if isConnected {
button.setImage(UIImage(named: "BluetoothEnabled")?.imageWithRenderingMode(.AlwaysTemplate), forState: UIControlState.Normal)
} else {
button.setImage(UIImage(named: "BluetoothDisabled")?.imageWithRenderingMode(.AlwaysTemplate), forState: UIControlState.Normal)
}
button.frame = CGRectMake(0, 0, 23, 31)
button.tintColor = UIColor.whiteColor()
button.userInteractionEnabled = false
let barButton = UIBarButtonItem(customView: button)
self.navigationItem.rightBarButtonItem = barButton
}
}
Anyway I can add the top half to the bottom code so I can just call updateConnectionIcon() in each ViewController?
Rather than putting observer in each viewController, create one BaseClase and keep it there and inherit all class which needs these observer. is that make sense?

set navigation backBarButtonItem to go to specific UIViewController in Swift

I need the navigation's back button always pops a specific UIViewController.
override func viewDidLoad() {
super.viewDidLoad()
//set title image
var logoImage:UIImage = UIImage(named: "barra")!
var logoImageView : UIImageView = UIImageView(image: logoImage)
logoImageView.frame = CGRectMake(0, 0, 320, 44)
logoImageView.contentMode = .ScaleAspectFit
logoImageView.contentMode = UIViewContentMode.Center
logoImageView.clipsToBounds = true
self.navigationItem.titleView = logoImageView
//set back image
var backImage:UIImage = UIImage(named: "freccia")!
backImage = backImage.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
self.navigationItem.backBarButtonItem = UIBarButtonItem(title:"", style: .Plain,target: self, action: "goToServizi:")
self.navigationController!.navigationBar.backIndicatorImage = backImage
self.navigationController!.navigationBar.backIndicatorTransitionMaskImage = backImage
//set menu image
var menuImage:UIImage = UIImage(named: "menu")!
menuImage = menuImage.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
self.navigationItem.rightBarButtonItem?.image = menuImage
}
func goToServizi(sender: UIBarButtonItem)
{
self.navigationController?.popToViewController(ServiziVC(), animated: true)// here I add a breakpoint, but it is never executed.
}
ServiziVC is the UIViewController that I need to show every time I click on the Back button.
I can't understand why goToServizi func is not called. Please help.
Thank you.
Here is the example for you.
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Customise your barButton Like this
var backImage:UIImage = UIImage(named: "freccia")!
backImage = backImage.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
var backButton = UIBarButtonItem(image: backImage, style: UIBarButtonItemStyle.Bordered, target: self, action: "goToThird")
self.navigationItem.leftBarButtonItem = backButton
}
func goToThird(){
//Initiate newViewController this way
let ThirdView = self.storyboard?.instantiateViewControllerWithIdentifier("ThirdViewController") as ThirdViewController
self.navigationController?.pushViewController(ThirdView, animated: true)
}
}
EDIT
Click on the ViewController which you want to initiate with identifier and you can find StoryBoard ID in Identity Inspector like shown in below Image.
HERE is the example for you for more reference.
Modify code as per your need.may be this will help you.
self.navigationController?.popToViewController(ServiziVC(), animated: true)You can't customize the back button action like that. This works:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.hidesBackButton = true;
var backImage:UIImage = UIImage(named: "freccia")!
backImage = backImage.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
var fakeBackButton = UIBarButtonItem(image: backImage, style: UIBarButtonItemStyle.Bordered, target: self, action: "goToServizi:")
self.navigationItem.leftBarButtonItem = fakeBackButton;
}
func goToServizi(sender: UIBarButtonItem)
{
self.navigationController?.popToViewController(ServiziVC(), animated: true)
}
Alternatively, you could override the UINavigationController's delegate method as described here : UINavigationController and back button action