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
Related
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.
I'm trying to remove the text "Back" from the navigation back button, leaving just the back chevron, but everything I'm trying is not working. For example if I add something like the following, obtained from previous answers to the same question, to viewDidLoad:
navigationItem.backBarButtonItem = UIBarButtonItem(title: "go away", style: .plain, target: nil, action: nil)
or
navigationController?.navigationBar.backBarButtonItem = UIBarButtonItem(title: "go away", style: .plain, target: nil, action: nil)
Then when the view appears it's still showing "< Back" in the navigation bar.
Here's what the views look like within captured within viewDidAppear.
Image:1
Try this code snippet hope it will help you
happy coding =)
override func viewDidLoad() {
DispatchQueue.main.async {
if let navBar = self.navigationController?.navigationBar {
navBar.backItem?.title = ""
}
}
}
You should create a left button and set the action to return to the rootViewController.
In viewDidLoad:
let leftButton = UIBarButtonItem(title: "<", style: .plain, target: self, action: #selector(back(_ :)))
self.navigationItem.leftBarButtonItem = leftButton
You are changing the wrong thing. You use this code here to change the title for the back button.
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Back", style: .done, target: self, action: #selector(handleBack))
By doing this you need to add a selector for the button as well. Cause if you click the back button nothing will happen. This is how you would do that.
#objc private func handleBack() {
navigationController?.popViewController(animated: true)
}
Hope this helps.
Alternatively, from Interface Builder, you can set previous UIViewController's Back Button on Navigation Item to " " (not empty string, space):
I added an extension to UIViewController to add a close button
extension UIViewController {
func addCloseButton() {
let button = UIBarButtonItem(image: #imageLiteral(resourceName: "bar_close"),
landscapeImagePhone: nil,
style: .done,
target: self,
action: #selector(UIViewController.dismiss(animated:completion:)))
navigationItem.leftBarButtonItem = button
}
}
When i tap the barbutton i get a crash directly to AppDelegate.
Any hints? Seems related to the selector.
You can't use dismiss(animated:completion:) as selector here because it takes two arguments bool and closure and bar button action pass args as UIBarButtonItem which cause app crash.
so change your code like this.
extension UIViewController {
func addCloseButton() {
let button = UIBarButtonItem(image: #imageLiteral(resourceName: "rightgreen"),
landscapeImagePhone: nil,
style: .done,
target: self,
action: #selector(onClose))
navigationItem.leftBarButtonItem = button
}
#objc func onClose(){
self.dismiss(animated: true, completion: nil)
}
}
However this question has accepted answer which load extra one method addCloseButton in each and every viewcontroller still posting a answer will going to help someone
NOTE : This example for adding barbutton item automatically and also handle action for pop view controller.
As Protocol extension doesn't provide a to implement selector methods so to get the rid of it I have created this solution.
First thing you need is BaseVC which is subclass of UIViewController and all of your view controller going to be inherited by BaseVC like your class LoginVC:BaseVC ...
now declare protocol
protocol PopableClass {
func popSelf (animated:Bool)
}
extension PopableClass where Self : UIViewController {
func popSelf (animated:Bool) {
self.navigationController?.popViewController(animated: animated)
}
}
In your Base VC add two methods and call setupNavigationBar from viewDidLoad
func setupNavigationBar () {
if self is PopableClass {
let barbuttonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "back"), landscapeImagePhone: #imageLiteral(resourceName: "back"), style: .plain, target: self, action: #selector(popViewController))
self.navigationItem.leftBarButtonItem = barbuttonItem
}
}
//--------------------------------------------------------------------------------
#objc func popViewController () {
if self is PopableClass {
(self as! PopableClass).popSelf(animated: true)
}
}
You did it !!
Now in whatever class you need back button to pop view controller just use like this
class PushedClass: BaseVC,PopableClass
Hope it is helpful
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!
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.