Swift 3 - Reload NavigationController Button Image - swift

I have a navigation controller view, with an embedded collectionview controller. I have a right bar button added programmatically, with an image added to it as follows:
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "greyCircle").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector (handleGrayCircleButton))
I would like a user that clicks on this button, to be able to change the image from grey to silver. I have the button handler set up to switch from grey to silver with each press:
self.navigationItem.rightBarButtonItem?.image = UIImage(named: "greyCircle")
However, the image only changes briefly before switching back. I have tried to save the color of the image as a 'flag' variable, i.e. if it is currently grey, turn to silver, in order to make the change persist, however, the image continually resets to whatever it is loaded with (either with viewDidLoad or viewWillAppear).
Is there anyway to refresh/reload the navigationController tab bar after each click and make it persist, along with the images for the bar buttons?
#
EDIT: Following is full button handler.
func handleFavoritePress(){
print("Favorites pressed")
if(!returnTrueIfInFavorites(objectName: readString(object: self.patientRecord!, key: "name"), objectType: LIST_CoreDataBaseObjects.Patients)){
// Add to favorites
Favorites_Names.append(readString(object: self.patientRecord!, key: "name"))
Favorites_Types.append(LIST_CoreDataBaseObjects.Patients)
// Let user know
let patientName = readString(object: self.patientRecord!, key: "name")
let alertController = UIAlertController(title: "Alert!", message: "\(patientName) has been added to your favorites.", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
var rootViewController = UIApplication.shared.keyWindow?.rootViewController
if let navigationController = rootViewController as? UINavigationController {
rootViewController = navigationController.viewControllers.first
}
if let tabBarController = rootViewController as? UITabBarController {
rootViewController = tabBarController.selectedViewController
}
rootViewController?.present(alertController, animated: true, completion: nil)
self.navigationItem.rightBarButtonItem?.image = UIImage(named: "savedFavorite_1")
}else{
self.navigationItem.rightBarButtonItem?.image = UIImage(named: "favorite_2")
deleteFromFavorites(objectName: readString(object: patientRecord!, key: "name"), objectType: LIST_CoreDataBaseObjects.Patients)
}
for favorite in Favorites_Names{
print("Favorites are currently: \(patient)")
}
}
And other functions:
override func viewWillAppear(_ animated: Bool) {
collectionView?.backgroundView = setBackgroundImage(imageName: "whiteBackground")
navigationItem.title = "Patient"
if(returnTrueIfInFavorites(objectName: readString(object: patientRecord!, key: "name"), objectType: LIST_CoreDataBaseObjects.Patients)){
setupNavBarButtonsIsFavorited()
}else{
setupNavBarButtonsNotFavorited()
}
}
func setupNavBarButtonsNotFavorited(){
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "favorite_2").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector (handleFavoritePress))
}
func setupNavBarButtonsIsFavorited(){
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "savedFavorite_1").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector (handleFavoritePress))
}

The answer to your question lies within this two LIST_CoreDataBaseObjects.Patients, returnTrueIfInFavorites or the patientRecord variable.
Either LIST_CoreDataBaseObjects.Patients did not get updated on your function handleFavoritePress's code
/* this code */
Favorites_Names.append(readString(object: self.patientRecord!, key: "name"))
Favorites_Types.append(LIST_CoreDataBaseObjects.Patients)
/* this code */
Because from what you've explained in the comments section, if I understood it correctly, is that when you tap the button the image changes, and then you navigate to a different view, but then when you navigate back, viewDidAppear(_:) and viewWillAppear(_:) will be triggered.
Which in the code block below I will explain
// this function will be triggered everytime you navigate into this view (because it has appeared)
override func viewWillAppear(_ animated: Bool) {
/* don't forget to call super.viewWillAppear(true) */
super.viewWillAppear(true)
/* don't forget to call super.viewWillAppear(true) */
collectionView?.backgroundView = setBackgroundImage(imageName: "whiteBackground")
navigationItem.title = "Patient"
// the problem lies here
/* the function `(returnTrueIfInFavorites(objectName: readString(object: patientRecord!, key: "name"), objectType: LIST_CoreDataBaseObjects.Patients))` is always returning one thing which is why your image returns back to its previous state */
if (returnTrueIfInFavorites(objectName: readString(object: patientRecord!, key: "name"), objectType: LIST_CoreDataBaseObjects.Patients)) {
setupNavBarButtonsIsFavorited() // only one of these gets called
} else {
setupNavBarButtonsNotFavorited() // only one of these gets called
}
}
func setupNavBarButtonsNotFavorited() {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "favorite_2").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector (handleFavoritePress))
}
func setupNavBarButtonsIsFavorited() {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "savedFavorite_1").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector (handleFavoritePress))
}
All of these or you're just not updating the variable self.patientRecord!'s value (I haven't seen any setting in your code relating to this variable)

Related

Presenting UIAlertController from UINavigationItem extension

I'm trying to add the same right bar button on multiple view controllers so I made an extension to UINavigationItem. My extension:
extension UINavigationItem{
func barButton() {
let barButton = UIBarButtonItem()
barButton.image = UIImage(systemName: "person.fill")
barButton.tintColor = .white
barButton.action = #selector(barButtonClicked)
barButton.target = self
self.rightBarButtonItem = barButton
}
#objc func barButtonClicked() {
let alertController = UIAlertController()
var action = UIAlertAction(title: "Log Out", style: .destructive) { action in
print("logout")
}
alertController.addAction(action)
action = UIAlertAction(title: "History", style: .default, handler: { action in
print("history")
})
alertController.addAction(action)
}
}
I'm calling barButton function in different viewControllers with self.navigationItem.barButton() in viewDidLoad and it works but I don't know how to present this alert without self.present() pointing to specific controller.
Thank you for your help.
You can write a selector function from a view controller
func barButton(func: Selector) {
let barButton = UIBarButtonItem()
barButton.image = UIImage(systemName: "person.fill")
barButton.tintColor = .white
barButton.action = func
barButton.target = self
self.rightBarButtonItem = barButton }
and write a #objc function in a view controller and if you need to write the same function for all. you can create a function in UIViewController Extension

How to change the function of a UIBarButtonItem on event in Swift

I've been following this Youtube tutorial from Let's Build That App to achieve a step by step navigation system using the Google Maps SDK. I want to be able to change the programmatically-generated 'next' UIBarButtonItem to a 'done' button on the last map location and use this to go back to a previous ViewController.
What's the correct method of going about this using Swift?
https://www.youtube.com/watch?v=8wPjCdDn2wo
My code:
navigationItem.rightBarButtonItem = UIBarButtonItem(
title: "Next",
style: .plain,
target: self,
action: #selector(nextLocation)
)
}
func nextLocation() {
if currentDestination == nil {
currentDestination = destinations.first
}
else {
if let index = destinations.index(of: currentDestination!), index < destinations.count - 1{
currentDestination = destinations[index + 1]
}
}
setMapCamera()
}
private func setMapCamera() {
CATransaction.begin()
CATransaction.setValue(2, forKey: kCATransactionAnimationDuration)
mapView?.animate(to: GMSCameraPosition.camera(withTarget: currentDestination!.location, zoom: currentDestination!.zoom))
CATransaction.commit()
let marker = GMSMarker(position: currentDestination!.location)
marker.title = currentDestination?.name
marker.map = mapView
}
/* My incorrect code */
func lastLocation() {
if currentDestination == destinations.last {
navigationItem.rightBarButtonItem = UIBarButtonItem(
title: "Finish",
style: .plain,
target: "myItinerary",
action: #selector(lastLocation)
)
}
}
I Hope this will help you :
First : Detect Last location or bar button click after last location to change bar button label to "Done". Then set action to the button after button "Done" clicked change again to "Next" and set destination counter to first value.
func next() {
if currentDestination == nil {
currentDestination = destinations.first
} else {
if let index = destinations.index(of: currentDestination!), index < destinations.count - 1 {
currentDestination = destinations[index + 1]
}
else {
// After last Location change button title to "Done"
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(lastLocation))
}
}
setMapCamera()
}
func lastLocation() {
currentDestination = destinations.first // Set destinations counter to First value
// Change Title of Bar Button
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(ViewController.next as (ViewController) -> () -> ()))
}
If you want to go backward location (In reverse) then :
func lastLocation() {
currentDestination = destinations.last
destinations = destinations.reversed()
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(ViewController.next as (ViewController) -> () -> ()))
}

t Change UIButton Text Based on User Input

I have a button on my UINavigationBar which I have created using this code:
let inputLabel = UIBarButtonItem(title: userInput, style: .plain, target: self, action: #selector(inputLabelButtonTapped))
I want to make the inputLabel.text based on what the user inputs. Here is what I imagine the inputLabelButtonTapped function to look like, but I cannot code it:
inputLabelButtonTapped() {
// Have a keyboard/numberpad appear on the screen
// Save the what the user types on the screen to a newUserInputVariable
// Make the inputLabel.text = newUserInputVariable
}
Thanks in advance.
You seem to have a mismatch in UIBarButtonItem and UILabel. The former is a button that can be positioned in a UINavigationController or UITabBarController. The latter just displays text.
Instantiate an input field either by adding a UITextField or an UIAlertViewController to the ViewController:
var barButtonItemTitle = String()
// create the alert controller
let ac = UIAlertController(title:"<SomeName>",
message: "<SomeMessage>",
preferredStyle: UIAlertControllerStyle.alert)
Add an UIAlertAction to the UIAlertViewController with a completion handler that will allow you to update the property for barButtonItemTitle
let action = UIAlertAction(title: "OK", style: .default) { _ in
//Add a textfield to the alert controller:
ac.addTextField
when you add a UITextField to a UIAlertController it will appear in an [TextFields] if it exists.
// Unwrap the textfield array and the first item there and access the text property.
guard let inputText = ac.textFields?.first?.text else {return}
Set the barButtonText property to the text that was pulled out
barButtonText = inputText
}
// Add action to the controller & present the view.
ac.addAction(action)
present(ac, animated: true)
Once the barButtonText is updated with new values, you can use that property to create an instance of UIBarButtonItem and place it where ever.
let barButton = UIBarButtonItem(title: barButtonText, style: .plain, target: nil, action: nil)
You can wrap all that into a func that is assigned to your view

How to add action to a programmatically-made button?

Since I was having issues creating a button via storyboard, I went about initiating a right nav bar button through code - found via this question How can I go back to the initial view controller in Swift?. This code is meant to take me back to my root view controller.
So here is the code as it stands at the moment.
let button1 = UIBarButtonItem(image: UIImage(named: "HomeM25.png"), style: .plain, target: self, action: #selector(getter: UIDynamicBehavior.action))
self.navigationItem.rightBarButtonItem = button1
func button() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
I was under the impression that if I were to change the
action: #selector(getter: action)
I would be able to create a function following this button initialization like so
func action() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
However, I am greeted with the "Use of local variable 'action' before its declaration'". I do not understand why this interpretation would not perform/why I would have to establish the action variable when its only use is a function name? Any help would be appreciated.
Update 1: Current Code
override func viewDidLoad() {
super.viewDidLoad()
let button1 = UIBarButtonItem(image: UIImage(named: "HomeM25.png"), style: .plain, target: self, action: #selector(action))
self.navigationItem.rightBarButtonItem = button1
func action() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
Update 1: Also had to use a different function action to go back to the original view controller.
self.navigationController!.popToRootViewController(animated: true)
It should be #selector(action). And you can define function within a function. Move action outside of the viewDidLoad function. Try this.
override func viewDidLoad() {
super.viewDidLoad()
let button1 = UIBarButtonItem(image: UIImage(named: "HomeM25.png"), style: .plain, target: self, action: #selector(action))
self.navigationItem.rightBarButtonItem = button1
}
func action() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}

UITapGestureRecognizer doesn't work

i want to set an UITapGestureRecognizer to permit to user to add an image as profile. I set the code but, when i touch on the image, nothing happens. Here the code:
#IBAction func selezionaFoto(sender: UITapGestureRecognizer) {
fieldNome.resignFirstResponder()
func selezionaLibreria(action : UIAlertAction!) {
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.Default, animated: true)
CameraManager.sharedInstance.newImageFromLibraryForController(self, editing: false)
}
func scattaFoto(action : UIAlertAction!) {
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.Default, animated: true)
var circle = UIImageView(frame: UIScreen.mainScreen().bounds)
circle.image = UIImage(named: "overlay")
CameraManager.sharedInstance.newImageShootForController(self, editing: false, overlay:circle)
}
var myActionSheet = UIAlertController(title: NSLocalizedString("ACTION_IMAGE_TITLE", comment: ""),
message: NSLocalizedString("ACTION_IMAGE_TEXT", comment: ""),
preferredStyle: UIAlertControllerStyle.ActionSheet)
myActionSheet.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_LIBRARY", comment: ""),
style: UIAlertActionStyle.Default,
handler: selezionaLibreria))
myActionSheet.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_SHOOT", comment: ""),
style: UIAlertActionStyle.Default,
handler: scattaFoto))
myActionSheet.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_CANCEL", comment: ""),
style: UIAlertActionStyle.Cancel,
handler: nil))
self.presentViewController(myActionSheet, animated: true, completion: nil)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func incomingImage(image: UIImage) {
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: true)
immagine.image = image
immagineSelezionata = image
}
func cancelImageSelection() {
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: true)
}
The first element in the storyboard is the image and then there is the textfield.
I recently faced the same issue, I had complicated view hierarchy and the reason why gesture didnt respond to the action, was that one superView was set to isUserInteractionEnabled to false, and as you may know, how gestures received by the app and go to the responder in the chain, is that all UIResponders in this chain should be isUserInteractionEnabled = true, to be delivered to the most top view. I assume that you faced the same situation, so just check all the views in the hierarchy.
I'm not 100% sure I understand your question, but it looks like you're just creating a UITapGestureRecognizer on your storyboard and connecting an action to it.
I think you mean to add a UITapGestureRecognizer to a UIImageView, which you can do like this:
var myImageview = UIImageView()
var myTapGestureRecognizer = UITapGestureRecognizer()
myImageview.addGestureRecognizer(myTapGestureRecognizer)
myTapGestureRecognizer.addTarget(self, action: Selector("methodName:"))
myImageview.userInteractionEnabled = true
func methodName(sender: UITapGestureRecognizer) {
// do stuff
}