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
Related
I am creating a custom UIToolBar to add as inputAccessoryView to a UITextField, I would like to add a UIBarButtonItem on the right side of this toolbar to serve as the return key of this textfield, and the text of this barButtonItem should be the same as the keyboard of that textfield.
My approach was:
let buttonDone = UIBarButtonItem(title: myTextField.returnKeyType, style: .done, target: self, action: #selector(pickerDone)
let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
// toolbar
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.items = [space, buttonDone]
toolBar.sizeToFit()
// setup input
myTextField.inputAccessoryView = toolBar
But with this i get this error:
Cannot convert value of type 'UIReturnKeyType?' to expected argument type 'String?'
So, I try something like:
title: myTextField.returnKeyType.text
But returnKeyType doesn't has a .text variable or similar...
Is there any way of doing this?
Should I go another way?
There is no built-in way to convert the UIReturnKeyType enum to a string. You will need to write your own code using a switch on the all the possible values.
Here's one solution using an extension. Add support for the other values as needed.
extension UIReturnKeyType {
var label: String {
switch self {
case .default:
return "Return"
case .go:
return "Go"
case .done:
return "Done"
default:
return "Enter"
}
}
}
Then you can use this as:
let buttonDone = UIBarButtonItem(title: myTextField.returnKeyType.label, style: .done, target: self, action: #selector(pickerDone)
I have an UIAlertController with a TextField and two UIAlertAction.
I need to check the string of the TextField in one of the UIAlertAction and prevent the dismiss of the UIAlertController if the string in the TextField doesn't match requirements
let saveAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: { alert -> Void in
let titleTextField = alertController.textFields![0] as UITextField
//Prevent dismiss if text in textField doesn't match requirements
})
I already tried something like :
(alertController.actions[1] as UIAlertAction).isEnabled = false
But it's already too late for this in the UIAlertAction
So is there any way to prevent dismiss while already in the handle of the UIAlertAction ?
Rather than trying to stop dismiss in UIAlertAction you can set UITextField delegate in,
func addTextField(configurationHandler: ((UITextField) -> Void)? = nil)
method and use the delegate call,
optional func textFieldDidEndEditing(_ textField: UITextField)
to enable and disable the UIAlertAction.
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)
If a textfield is empty, I would like to make an UIAlertController to remind, that something has to be filled out in the textfield. Now I have the following code:
#IBAction func saveDetails(segue: UIStoryboardSegue) {
let dController = segue.source as? EntryTableViewController
if let text = dController?.bezeichnungTextField.text, text.isEmpty {
let alert = UIAlertController(title: "No description", message: "Please fill out description", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "OK", style: .default)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
}else {
guard let menge = dController?.mengeTextField.text, let bezeichnung = dController?.bezeichnungTextField.text, let kategorie = dController?.categoryButtonOutlet.titleLabel?.text else {return}
self.saveItem(menge: menge, bezeichnung: bezeichnung, kategorie: kategorie)
self.tableView.reloadData()
}
}
Now actually it works when I press the button to return to the first controller. But the UIAlertController only appears for a very short moment and then disappears automatically. Is there a mistake in my code or isn't it possible to call the UIAlertController on a unwind segue?
Thank you for your help
You just show the alert controller and then immediately unwind. The solution is to check for the empty textfield before unwinding.
Make IBAction for your button then check the textfield if it is empty and then perforn your segue programmatically.
I had created a normal segment controller which has two category, category 1 and 2 respectively. Now I have add button, which push me to the new view controller to add an item. When clicking on done button for adding an item I have an alert controller which show the category in which I have to save the item. But I don't know how to get that item in particular segment. If anyone can help.
Thanks
#IBAction func done(sender: AnyObject) {
if let item = itemToEdit {
item.text = textField.text!
item.dateTime = dateTime
textField.becomeFirstResponder()
//item.text = textAreaDescription.text!
//textAreaDescription.becomeFirstResponder()
delegate?.itemDetailViewController(self, didFinishEditingItem: item)
} else {
let alertController = UIAlertController(title: "Choose Category", message: "Choose Category To Save Your Item.", preferredStyle: .Alert)
let toDo = UIAlertAction(title: "Category 1", style: .Default) { (action) in
let item = NoToDoItem()
item.text = self.textField.text!
//item.text = textAreaDescription.text!
item.dateTime = self.dateTime
self.delegate?.itemDetailViewController(self, didFinishAddingItem: item)
}
alertController.addAction(toDo)
let notSure = UIAlertAction(title: "Category 2", style: .Default){ (action) in
let notSureItem = NotSureItem()
notSureItem.text = self.textField.text!
//item.text = textAreaDescription.text!
notSureItem.dateTime = self.dateTime
self.delegate?.itemDetailViewController(self, didFinishAddingNotSureItem: notSureItem)
}
alertController.addAction(notSure)
presentViewController(alertController, animated: true, completion: nil)
}
}
use commitEditingstyle, then your cells will still display the Delete button when you swipe. Use editingStyleForRowAtIndexPath: instead. Put an if statement in to test which segment is selected, and return UITableViewCellEditingStyle.None (to disable swipe to delete) or .Delete (to enable swipe to delete) accordingly. If you want to be able to delete the cells by putting the table view into editing mode, then also test tableView.editing to determine whether to use .Delete or .None.