I have UIBarButtonItem and the selector method is not being called, been driving me nuts for days.
These are in my viewDidLoad method
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: nil, action: #selector(self.doneButtonPressed))
self.navigationItem.rightBarButtonItem = doneButton
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: nil, action: #selector(self.cancelButtonPressed))
self.navigationItem.leftBarButtonItem = cancelButton
and here are the methods:
#objc func doneButtonPressed()
{
dismiss(animated: true, completion: nil)
}
#objc func cancelButtonPressed()
{
dismiss(animated: true, completion: nil)
}
How come they are not being called?
You should set target to self instead of nil
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(self.doneButtonPressed))
You need to set a valid target in UIBarButtonItem(title:style:target:action:).
This is how it works behind the scenes: #selector transforms your selector into a Selector type: something that is almost – but not quite – a string. The Objective C runtime later uses that Selector to send a message with the Selector as its content to target; i.e. it calls the selector on the target.
Therefore, you must add the #objc annotation to your target and you must add the target to the constructor.
Related
I am trying to tidy up a ViewController and would like to move the setup of a navigation item to an extension.
This is the code I am looking to move out of the ViewController:
private func setupNavigationItem() {
navigationItem.leftBarButtonItem = UIBarButtonItem.stashWhite(barButtonSystemItem: .cancel, target: self, selector: #selector(cancelBarButtonHandler))
navigationItem.rightBarButtonItem = UIBarButtonItem.stashRed(barButtonSystemItem: .save, target: self, selector: #selector(saveBarButtonItemHandler))
navigationItem.rightBarButtonItem?.isEnabled = false
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
I have then created this extension.
extension UINavigationItem {
func addSavingSetup(_ cancelBarButtonHandler: Selector, _ saveBarButtonItemHandler: Selector) {
leftBarButtonItem = UIBarButtonItem.stashWhite(barButtonSystemItem: .cancel, target: self, selector: cancelBarButtonHandler)
rightBarButtonItem = UIBarButtonItem.stashRed(barButtonSystemItem: .save, target: self, selector: saveBarButtonItemHandler)
rightBarButtonItem?.isEnabled = false
backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
}
I then call it like so:
navigationItem.addSavingSetup(#selector(cancelBarButtonHandler(_:)), #selector(saveBarButtonItemHandler))
However I then get this error when tapping either the cancel or save bar button:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationItem cancelBarButtonHandler:]: unrecognized selector sent to instance 0x105e01e80
Any idea how I can solve this? Not sure how else to pass in the selector.
Thanks
You are setting different target. you need to pass target, like below
extension UINavigationItem {
func addSavingSetup(target: Any,_ cancelBarButtonHandler: Selector, _ saveBarButtonItemHandler: Selector) {
leftBarButtonItem = UIBarButtonItem.stashWhite(barButtonSystemItem: .cancel, target: target, selector: cancelBarButtonHandler)
rightBarButtonItem = UIBarButtonItem.stashRed(barButtonSystemItem: .save, target: target, selector: saveBarButtonItemHandler)
rightBarButtonItem?.isEnabled = false
backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
}
I am really confused as to why the follow only works when I create the UIButton in view did load, Im hoping to get an answer to this because im trying to clean my the viewDidLoad function.
let logoutButton: UIBarButtonItem = {
let button = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(handleLogout))
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = logoutButton // Does not work
// navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(handleLogout))
}
I have a UIViewController, and I embed it into a UINavigationController.
I want to show one item in the toolbar (and by toolbar, I mean this:
This is my code in viewDidLoad method
self.navigationController?.toolbarHidden = false
self.navigationController?.toolbar.items?.append(UIBarButtonItem(title: "Buy Potato", style: .Plain, target: self, action: #selector(ViewController.buyPotato)))
self.navigationController?.toolbarItems?.append(UIBarButtonItem(title: "Buy Potato", style: .Plain, target: self, action: #selector(ViewController.buyPotato)))
self.toolbarItems?.append(UIBarButtonItem(title: "Buy Potato", style: .Plain, target: self, action: #selector(ViewController.buyPotato)))
and I already have the method buyPotato
func buyPotato() {
}
as you see, I tried to do that using either the viewController or the navigationController, but it doesn't work.
All I can see is the toolbar at the bottom of my screen but without any button.
self.navigationController?.toolbarHidden = false
var items = [UIBarButtonItem]()
items.append(
UIBarButtonItem(barButtonSystemItem: .Plain, target: self, action: nil))
items.append(
UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "onClickedToolbeltButton:"))
self.setToolbarItems(barButtonItems, animated: true)
This has to work for you as per the answer written here.
Delete
self.setToolbarItems(barButtonItems, animated: true)
Add
self.toolbarItems = barButtonItems
I have a following helper class
class PDFPreviewHelper {
var pdfNavigationController: UINavigationController!
func previewButtonPressed(rootViewController: UIViewController) {
let pdfViewController = PDFViewController(resource: "final.pdf")
pdfNavigationController = UINavigationController(rootViewController: pdfViewController)
let backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "backButtonPressedInPDF")
pdfViewController.navigationItem.setLeftBarButtonItem(backButton, animated: false)
rootViewController.presentViewController(pdfNavigationController, animated: true, completion: nil)
}
func backButtonPressedInPDF() {
pdfNavigationController.dismissViewControllerAnimated(true, completion: nil)
}
}
I call a function in above helper class in my rootviewcontroller like following:
func previewInPdfButtonPressed() {
let a = PDFPreviewHelper()
a.viewI129InPDF(self)
}
I successfully modally present pdfNavigationController on top of my rootViewController, but whenever i press back button, nothing gets called. Why is this so? I set a break point in backButtonPressedInPDF function and it doesn't even hit the break point.
You need to assign leftBarButtonItem to UINavigationController via below way.
pdfViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "backButtonPressedInPDF")
I think the problem lies here.
let backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "backButtonPressedInPDF")
To:
let backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "backButtonPressedInPDF:")
I believe the problem as at action: "backButtonPressedInPDF" where there is a missing :
In addition, change your method by adding (sender:AnyObject) also?
func backButtonPressedInPDF(sender:AnyObject) {
pdfNavigationController.dismissViewControllerAnimated(true, completion: nil)
}
The issue is you are creating a temporary instance of PDFPreviewHelper class and using it to present the view controller. Not keeping the reference.
This can be fixed in two ways:
Method 1: Add previewInPdfButtonPressed method to your PDFViewController class. Then change implementation like following.
let backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: pdfViewController, action: "backButtonPressedInPDF")
Method 2:
You should keep the object a until you dismiss your PDFViewController-navigationcontroller.
Even if you do like this you will get an exception like
2016-03-16 15:43:03.140 XXXX[3757:585277] *** NSForwarding: warning: object 0x7ff5eb72be70 of class 'XXXX.PDFPreviewHelper' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector -[XXXX.PDFPreviewHelper backButtonPressedInPDF]
This is because you are not subclassing NSObject for your PDFPreviewHelper.
You can fix it in two ways,
Make PDFPreviewHelper as NSObject subclass.
Add dynamic
keyword to the function like dynamic func previewInPdfButtonPressed() {....}.
Resolution to this issue is well described here
func previewInPdfButtonPressed()
{
let a = PDFPreviewHelper()
a.viewI129InPDF(self)
}
The problem is a is released when the program goes out of the scope of this function. The action you target on a of course can't be executed anymore
solution add a as a property of the viewController
func showFilterPanel() {
println("Showing")
}
let backButton = UIBarButtonItem(title: "Horse", style: UIBarButtonItemStyle.Plain, target: nil, action: "showFilterPanel")
#IBAction func tapGesture(sender: AnyObject) {
self.navigationItem.leftBarButtonItem = self.backButton
}
The above code is embedded in a Map Navigation Controller. The action has no effect, any ideas why (I don't see "Showing" in the console)?
You passed nil as the target for the selector. It should be self in this case:
let backButton = UIBarButtonItem(title: "Horse", style: UIBarButtonItemStyle.Plain, target: nil, action: "showFilterPanel")
You'll also need to rearrange this to move the instantiation of the UIBarButtonItem down to method scope in order to be able to reference self in its instantiation. When you create a property in Swift you can't reference other properties or self in its creation, unless it's a computed or lazily instantiated property.