I have a screen with a collection view, and a plus sign bar button item. When the plus sign is pressed, an alert window pops up, where the user can add information to the list. Upon hitting OK, I am trying to refresh the collection view, but I'm doing something wrong.
The print statement "passed guard" is achieved, and I can get the information they entered. Just can't refresh the view to reflect this without leaving and coming back. Any guidance? I've run into this a few times actually, so I'm clearly missing something. Thanks very much in advance.
#objc func newButtonPressed() {
let alert = UIAlertController(title: "Add", message: "", preferredStyle: .alert)
alert.addTextField { (textField) in
textField.placeholder = "Name"
}
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in
var name = ""
guard let textFields = alert.textFields else { return }
guard let navController = self.parent as? UINavigationController else { return }
guard let settingsVC = navController.topViewController as? SettingsVC else { return }
print("passed guard") // success
DispatchQueue.main.async {
settingsVC.collectionView.reloadData()
settingsVC.view.backgroundColor = .red
// For testing purposes, explicitly using main thread and setting to red
}
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
May be you need to alter the collection dataSource
guard let textFields = alert.textFields else { return }
settingsVC.arr.append(textFields.first!.text!) // arr is collection dataSource
settingsVC.collectionView.reloadData()
Related
So my goal is to have the label text always revert back to the original price if the alert view controller is dismissed.
Here is a clip of the issue so you guys can understand it more, it would be too long to explain over text...
So as you can see, when the user cancels the alert prompt, I want everything to revert back as if they never pressed it in the first place. The stepper value reverts, the number label text
reverts, but the event cost does not and I can't figure out why.
Here is the #IBAction function I use with the stepper:
#IBAction func guestsCount(_ sender: UIStepper) {
guestNumberCount.text = String(Int(sender.value))
let totalCost = Decimal(sender.value) * cost
let formatted = totalCost.formattedAmount
var textfield = UITextField()
var textfield2 = UITextField()
actualCostOfEvent.text = "$\(formatted ?? "")"
if(Int(sender.value) > sampleStepperValueForIncrement){
print("increasing")
sampleStepperValueForIncrement += 1
let alert = UIAlertController(title: "Add A Guest", message: "Type full name the of guest you want to add below", preferredStyle: .alert)
let addGuest = UIAlertAction(title: "Add", style: .default) { (add) in
guard let user = Auth.auth().currentUser else { return }
db.document("student_users/\(user.uid)/guestTickets/guestDetails").setData(["guests": FieldValue.arrayUnion([textfield.text ?? "Nil"])], merge: true) { (error) in
if let error = error {
print("There was an error adding the name: \(error)")
} else {
print("name added successfully")
}
}
}
let dismiss = UIAlertAction(title: "Dismiss", style: .cancel) { (cancel) in
self.dismiss(animated: true, completion: nil)
self.guestNumberCount.text = String(Int(sender.value) - 1)
self.stepperValue.value -= 1
}
alert.addTextField { (alerttextfield) in
textfield = alerttextfield
alerttextfield.placeholder = "Guest Name"
alerttextfield.clearButtonMode = .whileEditing
}
alert.addAction(dismiss)
alert.addAction(addGuest)
present(alert, animated: true, completion: nil)
}
else {
print("decreasing")
sampleStepperValueForIncrement = sampleStepperValueForIncrement - 1
let alert = UIAlertController(title: "Remove A Guest", message: "Type the full name of the guest you want to remove. The guest name is case-sensitive and must equal the name you added.", preferredStyle: .alert)
let dismiss = UIAlertAction(title: "Dismiss", style: .cancel) { (cancel) in
self.dismiss(animated: true, completion: nil)
self.guestNumberCount.text = String(Int(sender.value) + 1)
self.stepperValue.value += 1
}
let removeGuest = UIAlertAction(title: "Remove", style: .destructive) { (remove) in
guard let user = Auth.auth().currentUser else { return }
db.document("student_users/\(user.uid)/guestTickets/guestDetails").updateData(["guests": FieldValue.arrayRemove([textfield2.text ?? "Nil"])]) { (error) in
if let error = error {
print("There was an error removing the name: \(error)")
} else {
print("name removed successfully")
}
}
}
alert.addTextField { (alerttextfield2) in
textfield2 = alerttextfield2
alerttextfield2.placeholder = "Guest Name"
alerttextfield2.clearButtonMode = .whileEditing
}
alert.addAction(dismiss)
alert.addAction(removeGuest)
present(alert, animated: true, completion: nil)
}
}
The problems both stem from the dismiss actions in both the if statement and the else statement. I tried possibly adding Decimal(sender.value) - 1 in one dismiss action and then Decimal(sender.value) + 1 in the other, but those didn't make a difference. The exact thing happens when you already have a guest added and you decide to remove, but end up dismissing the alert vc, the price also doesn't revert as well. The odd thing is that if I add all the way to the max value, which is 6 and then remove guests all the way back down to 1 again, the price will revert back to the original price.
Any help would be appreciated, thanks.
You only set actualCostOfEvent.text before the alert controller is displayed.
In the event that it is cancelled, you'll need to set it again, with the recalculated value once you've decremented the stepper again.
I'd suggest moving that code into a helper function so that you aren't repeating it:
func setLabelToFormattedTotalCost(multiplier: Int) {
let totalCost = Decimal(multiplier) * cost
let formatted = totalCost.formattedAmount
actualCostOfEvent.text = "$\(formatted ?? "")"
}
Then, call that with the stepper value (that would be the multiplier parameter) at the beginning of guestsCount and then again once you've decremented the value if the alert is dismissed.
Its because in dismiss you reset stepper value and guest number count but dont update the cost value, so in both dismiss action you can have
let dismiss = UIAlertAction(title: "Dismiss", style: .cancel) { (cancel) in
//all other code of yours and finally
let totalCost = Decimal(self.stepperValue.value) * cost
let formatted = totalCost.formattedAmount
actualCostOfEvent.text = "$\(formatted ?? "")"
}
You can obviously move this to a function and call the function from both dismiss action as
func updateCostValue() {
let totalCost = Decimal(self.stepperValue.value) * cost
let formatted = totalCost.formattedAmount
actualCostOfEvent.text = "$\(formatted ?? "")"
}
let dismiss = UIAlertAction(title: "Dismiss", style: .cancel) { (cancel) in
self.dismiss(animated: true, completion: nil)
self.guestNumberCount.text = String(Int(sender.value) - 1)
self.stepperValue.value -= 1
self.updateCostValue()
}
let dismiss = UIAlertAction(title: "Dismiss", style: .cancel) { (cancel) in
self.dismiss(animated: true, completion: nil)
self.guestNumberCount.text = String(Int(sender.value) + 1)
self.stepperValue.value += 1
self.updateCostValue()
}
I have an app that allows users to save their profile. In order for them to be able to sign up, I want to check and see if they have agreed to the apps terms and conditions. The issue I am having is if the user doesn't agree to them, they will see an alertController telling them to agree. However, the app still continues to execute the remainder of the code.
func checkIfChecked() {
if self.checkbox.imageView.isHidden == true {
let alert = UIAlertController(title: "Hold up!",message:" You must agree to our Community Guidelines before you can sign up.", preferredStyle: UIAlertController.Style.alert)
let continueButton = UIAlertAction(title: "Got it!", style: .default, handler: {(_ action: UIAlertAction) -> Void in
})
continueButton.setValue(GREEN_Theme, forKey: "titleTextColor")
alert.addAction(continueButton)
self.present(alert, animated: true, completion: nil)
}
if self.checkbox2.imageView.isHidden == true {
let alert = UIAlertController(title: "Hold up!",message:" You must agree to our Terms & Conditions before you can sign up.", preferredStyle: UIAlertController.Style.alert)
let continueButton = UIAlertAction(title: "Got it!", style: .default, handler: {(_ action: UIAlertAction) -> Void in
})
continueButton.setValue(GREEN_Theme, forKey: "titleTextColor")
alert.addAction(continueButton)
self.present(alert, animated: true, completion: nil)
}
}
#objc func handleRegister() {
checkIfChecked()
let hud = JGProgressHUD(style: .dark)
hud.textLabel.text = "Registering!"
hud.show(in: view)
guard let email = emailTextField.text, let password = passwordTextField.text, let name = nameTextField.text, let phonenumber = phonenumberTextField.text else {
print("Error")
return
the remainder of code....
}
}
if the checkBoxs are checked, there is no issue. But if they are not checked, then the users information will still be saved to the data base without them logging in. So I am trying to stop the execution of handleRegister after checkIfChecked is called only if the boxs were not checked.
Not sure if this is the safest way to fix the issue I am having, but what I did to fix the problem is inside of of the handleRegister, I added
checkIfChecked()
this has to be after the checkIfChecked that way the alertControllers can show.
if self.checkbox.imageView.isHidden == true {
return
} else if self.checkbox2.imageView.isHidden == true {
return
}
it does stop the execution of code if these lines are true.
The following function below is displaying an alert if no selections are made in UITableView.
But I do not want the alert to come up when the TableView has no records. How can this be done?
Work so far:
#IBAction func onTapNextButton(_ sender: UIBarButtonItem) {
guard let selectedIndexPaths = tableView.indexPathsForSelectedRows, !selectedIndexPaths.isEmpty else {
//Show Alert here...
let alert = UIAlertController(title: "Alert..!!", message: "You must select atleast 1 row before proceeding.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
}
First check if the data source array is not empty, replace dataSourceArray with the real name
guard !dataSourceArray.isEmpty else { return }
guard let selectedIndexPaths = tableView.indexPathsForSelectedRows,
!selectedIndexPaths.isEmpty else {
let alert ...
Whatever the object is that you're using as your data source (I'm assuming it's an array), check to see if that object (array) has zero elements. So, for example, something like:
//tableViewData should be the name of the array you're using as your data source
if tableViewData.isEmpty {
return
}
That way if there are no rows, you exit the function, and it won't display the alert.
// this is one way of checking if the tableView is empty
#IBAction func onTapNextButton(_ sender: UIBarButtonItem) {
if tableView.visibleCells.isEmpty {
//table view is empty: do something
} else {
guard let selectedIndexPaths = tableView.indexPathsForSelectedRows, !selectedIndexPaths.isEmpty else {
//Show Alert here...
let alert = UIAlertController(title: "Alert..!!", message: "You must select atleast 1 row before proceeding.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
}
}
***** Hope you like it *****
I've got a Problem. I'm new to iOS programming, and i'm struggling to understand how Swift code is excecuted. For example, in the piece of code below, I would think that every line is executed right after the one above. But when it reaches the passData() function, it does not ejecute that fuction. it keeps going, and some time later (or erlier) it excecutes it (the passData function).
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if numLaunches == 0{
nombre = usuario()
numLaunches += 1
}
}
func usuario() -> String {
var tField: UITextField!
func configurationTextField(textField: UITextField!)
{
print("generating the TextField")
textField.placeholder = "Enter an item"
textField.textAlignment = .center
tField = textField
}
func handleCancel(alertView: UIAlertAction!)
{
print("Cancelled !!")
}
let alert = UIAlertController(title: "Please Enter Your Name", message: "", preferredStyle: .alert)
alert.addTextField(configurationHandler: configurationTextField)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler:handleCancel))
alert.addAction(UIAlertAction(title: "Done", style: .default, handler:{ (UIAlertAction) in
print("Done !!")
print("Item : \(String(describing: tField.text))")
self.nombre = tField.text!
}))
self.present(alert, animated: true, completion: {
print("completion block")
})
print(self.nombre)
passData()
if tField.text == nil {
return "No value"
}else{
return (tField.text!)
}
}
func passData() {
let myVC = storyboard?.instantiateViewController(withIdentifier: "SecondVC") as! AboutViewController
if myVC.playerName != nil {
myVC.playerName.text = nombre
}else{
}
navigationController?.pushViewController(myVC, animated: true)
print("pasa el dato")
}
So, the Problem is, that i need to pass the content of the variable "nombre" to another VC. But when the passData function is excecuted that variable is empty. I thought if i called that function after the variable was updated, it Will pass the right content. But im clearly mistaken.
I would appreciate the help!
In general, unless your functions are asynchronous, you are correct in your understanding that code is executed from top down. In this case I think you are just confused about your UIAlertAction code. It appears you are popping an alert to the user, asking them to write their name, and when they hit "Done" you want to call your passData() function. In that case, you should put that passData() call inside your UIAlertAction, like so:
alert.addAction(UIAlertAction(title: "Done", style: .default, handler:{ (UIAlertAction) in
print("Done !!")
print("Item : \(String(describing: tField.text))")
self.nombre = tField.text!
self.passData()
}))
The code inside the handler for your UIAlertActions will not be executed until the user presses that button, which is why you are finding that passData is getting called too early.
In my app I display tow alert views. The second alert view should pop up if the first has been closed. Now I check if an alert view is displayed like this:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let viewController = appDelegate.window!.rootViewController as! ViewController
if viewController.view.window != nil {
}
else {
let alertView = UIAlertController(title: NSLocalizedString("IAP", comment: "comment"), message: NSLocalizedString("IAP1", comment: "comment"), preferredStyle: .Alert)
alertView.addAction(UIAlertAction(title: "Ok", style: .Cancel, handler: nil))
viewController.presentViewController(alertView, animated: true, completion: nil)
}
I send the 2nd alert view if the first isn't displayed anymore. But if the first view is still displayed the 2nd alert view doesn't pops up anymore. So my question is if there's a waiting line for alert views and how can I solve this problem?
You should define a handler for the first action and present the 2nd alertView within the handler.
So instead of
UIAlertAction(title: "Ok", style: .Cancel, handler: nil)
you should do
UIAlertAction(title: "Ok", style: .Cancel) { (action) -> Void in
// Present the other alertView
}
If you are using a navigation controller, the general way to see if an alert is already displayed is to check the presentedViewController property.
if let _ = navigationController.presentedViewController {
print("is already presenting \(navigationController.presentedViewController)")
} else {
navigationController.presentViewController(alert, animated:true, completion:nil)
}
For a fast patch in any UIViewController : (Xcode 8.3.1 & Swift 3.1)
func blabla() {
if presentedViewController != nil {
delay(0.5, closure: {
self.blabla()
})
return
}
// other alert code here
}
Simple way to check in Swift
var isAlertViewPresenting: Bool {
get {
if self.presentedViewController is UIAlertController {
return true
}
return false
}
}