I have a Navigation controller and I'm trying to put a button on the right of navigation bar but I can't handle the tap action. I'm declaring the UIBarButtonItem like this
let navigationButton = UIBarButtonItem.init(title: "Logout", style: .done, target: self, action: #selector(RestaurantsListViewController.logoutAction))
I'm adding the button on the viewDidLoad func
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = navigationButton
}
and the function that I'm trying to use to handle the tap event is this
func logoutAction(sender: AnyObject?){
print("Logout")
}
but when I press the button, the message is not printed in console.
Try this:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "ButtonName", style: .done, target: self, action: #selector(YourViewController.yourAction))
}
let okbtn = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(ViewController.logoutAction))
Try This
otherwise You need to replace func like
func logoutAction()
{
print("logout")
}
The issue here is when you create navigationButton as a class property, it gets initialized before self is initialized. So self doesn't exist yet when you pass it in as the target of the button.
There are a couple ways to fix it, including the answer by #Mannopson where you initialize the button inside the viewDidLoad, which ensures that self has already been created.
Another way to solve this issue is to declare navigationButton to be a lazy var:
lazy var navigationButton = UIBarButtonItem.init(title: "Logout", style: .done, target: self, action: #selector(RestaurantsListViewController.logoutAction))
The lazy var ensures that the property only gets initialized when the property gets accessed (hence it being a lazy initialization). Since the first time it is accessed happens in viewDidLoad, we can also be sure that self has been created. Hope this gives you more context!
Related
When I create a UIBarButton programatically in the viewDidLoad() method it does not show up when I run the program. I am not sure what is happening. Sorry I am new to xcode and do not understand how everything works.
var menuButton: UIBarButtonItem = UIBarButtonItem()
override func viewDidLoad() {
super.viewDidLoad()
// creating the refresh control object
menuButton = UIBarButtonItem.init(title: "Menu", style: .plain, target: self, action: nil)
}
It should create a UIBarButton in the navigation controller, instead there is nothing there.
You need to set navigationItem.rightBarButtonItem = menuButton
I'm unable to detect on-click events on UIBarButtonItems in Swift 3. Can anyone help?. I need to perform a segue to another storyboard and view controller on clicking the UIBarButtonItem.
Swift 3.0:
override func viewDidLoad() {
super.viewDidLoad()
let controllerButton: UIBarButtonItem = UIBarButtonItem.init(title: "Next", style: .plain, target: self, action: #selector(ViewController.changeController))
self.navigationItem.rightBarButtonItem = controllerButton
}
func changeController() {
self.performSegue(withIdentifier: "Controller name segue", sender: AnyObject)
}
Please check this above code to for creating UIBarButtonItem and keep that on right navigation Item along with target and action.
Checkout sample code below:-
var b = UIBarButtonItem(
title: "Continue",
style: .plain,
target: self,
action: #selector(sayHello(sender:))
)
func sayHello(sender: UIBarButtonItem) {
}
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
I'm trying to set in almost each page a custom back button and I'm repeating the same code in each page like this;
let buttonItem = UIBarButtonItem(image: UIImage(named: "arrow_back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack")
buttonItem.tintColor=UIColor.blackColor()
navigationItem.leftBarButtonItem = backButtonItem()
And I think this is the wrong way for it. So please could you tell what is the best way for this?
I've done this by creating a category on UIViewController. In that file I created a method called addBackButton, where you can put your code in once, then expose the method in your .h file. Then in any of your view controller subclasses you can import your category and call [self addBackButton];
You have various options here.
1) UIViewController extension
extension UIViewController {
func brandedBackButton() {
let buttonItem = UIBarButtonItem(image: UIImage(named: "back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack")
buttonItem.tintColor=UIColor.blackColor()
navigationItem.leftBarButtonItem = buttonItem
}
}
and then just call in your view controller
override func viewDidLoad() {
super.viewDidLoad()
brandedBackButton()
}
2) Base View Controller Class
You would basically put a class in between your controllers and the UIViewController.
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let buttonItem = UIBarButtonItem(image: UIImage(named: "back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack")
buttonItem.tintColor=UIColor.blackColor()
navigationItem.leftBarButtonItem = buttonItem
}
}
and then you would inherit from this BaseViewController instead of standard UIViewController.
class YourWhateverViewController: BaseViewController {
//implementation here....
}
Both the category and the base class can also accommodate the popBack custom method so you would end up really with literally 10 characters to get this behaviour anywhere...
I suggest you create a custom UIButton class and reuse it every time you want it.
class backButtonItem: UIBarButtonItem {
convenience init(target: AnyObject?) {
self.init(image: UIImage(named: "arrow_back"), style: .Plain, target: target, action: "popBack")
self.tintColor = UIColor.blackColor()
}}
just type the following script when you use it:
navigationItem.leftBarButtonItem = backButtonItem(target: self)
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.