I have an alertController with a textField. The user enters their data into the textField and hits "set". It should then create the item and save the text entered as my attribute it's set to. However, upon creating the item the textField passes nil. It's not saved until the item is reopened and saved again (prompting the alertController to request data in the textField). Why is it not saving it the first time?
saveButton pressed:
#IBAction func saveButton(sender: AnyObject) {
if (item?.slminqty == nil) {
let alert = UIAlertController(title: "Minimun Qty.", message: "Please set minimun qty. for pantry.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addTextFieldWithConfigurationHandler { (textField: UITextField!) -> Void in
textField.placeholder = "Minimun Qty."
textField.keyboardType = .NumbersAndPunctuation
textField.clearButtonMode = UITextFieldViewMode.WhileEditing
}
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: {saveitem}()))
alert.addAction(UIAlertAction(title: "Set", style: UIAlertActionStyle.Default, handler: {(action) -> Void in
let textField = alert.textFields![0].text!
self.item?.slminqty = textField
self.saveitem(self)}))
self.presentViewController(alert, animated: true, completion: nil)
}else{
if item != nil {
edititems()
} else {
createitems()
}
print(item?.slminqty)
dismissVC()
}
}
Save function:
func saveitem(sender: AnyObject) {
if item != nil {
edititems()
} else {
createitems()
}
print(item?.slminqty)
dismissVC()
}
Create function:
func createitems() {
let entityDescription = NSEntityDescription.entityForName("List", inManagedObjectContext: moc)
let item = List(entity: entityDescription!, insertIntoManagedObjectContext: moc)
item.slitem = slitem.text
item.sldesc = sldesc.text
item.slqty = slqty.text
item.slprice = slprice.text
item.slist = true
item.slcross = false
if slitem.text == nil{
createitems()
}else{
edititems()
}
do {
try moc.save()
} catch _ {
return
}
}
Edit function:
func edititems() {
item?.slitem = slitem.text!
item?.sldesc = sldesc.text!
item?.slqty = slqty.text!
item?.slprice = slprice.text!
do {
try moc.save()
} catch {
return
}
}
If both of the create and edit are the same (with the exception of slcross and slist) why won't it save the data when the item is created?
Edit please see my pull reqest, I have made some changes to your code. along with some comments.
I think the problem in this line :
self.item?.slminqty = textField
self.item might be nil. you should make sure first item is not nil.
you may try to create item if this is nil. like:
if self.item == nil {
//create item.
self.acreateItems()
// after creating the item just test its value.
print("item was nil so we just created it.\nIts value not is \(self.item)")
}
self.item?.slminqty = textField
Related
I am trying to get The Information that is added to display in a label on the main screen. I want it to calculate the profit but i get the error " Value of type 'String' has no member 'text' "
#IBAction func addButtonTapped(_ sender: Any) {
let alert = UIAlertController(title: "Product Information", message: nil, preferredStyle: .alert)
alert.addTextField(configurationHandler: { (itemTextField) -> Void in
itemTextField.placeholder = "Item"
})
alert.addTextField(configurationHandler: { (priceTextField) -> Void in
priceTextField.placeholder = "Price"
})
alert.addTextField(configurationHandler: { (salePriceTextField) -> Void in
salePriceTextField.placeholder = "Sale Price"
})
alert.addAction(UIAlertAction(title: "Add", style: .default) { (action) in
let item = alert.textFields?[0].text ?? ""
let price = alert.textFields?[1].text ?? ""
let salesPrice = alert.textFields?[2].text ?? ""
let itemPriceLabel = Double(price.text!)
let salePriceLabel = Double(salesPrice.text!)
if itemPriceLabel != nil && salePriceLabel != nil {
let profitValue = Double (itemPriceLabel! - salePriceLabel!)
ProfitLabel.text = profitValue
}
let product = Product(item: item, price: price, salesPrice: salesPrice)
self.addProduct(product)
})
self.present(alert, animated: true, completion: nil)
storeData()
}
Your price and salesPrice variables are already string only. But you’ve tried to access text field in those variables which is not present in String class in the next statement.
let itemPriceLabel = Double(price)
let salePriceLabel = Double(salesPrice)
And assign your value as follows.
ProfitLabel.text = String(profitValue)
Basically, both price and salesPrice are already Strings (because they took the text from the labels), not labels.
All you need to do is convert them to straight to doubles, no need for .text.
So instead of:
let itemPriceLabel = Double(price.text!)
let salePriceLabel = Double(salesPrice.text!)
Do:
let itemPriceLabel = Double(price)
let salePriceLabel = Double(salesPrice)
There are a few mistakes in the alert action and the naming of the variables is confusing, replace it with
alert.addAction(UIAlertAction(title: "Add", style: .default) { _ in
let itemText = alert.textFields?[0].text ?? ""
let priceText = alert.textFields?[1].text ?? ""
let salesPriceText = alert.textFields?[2].text ?? ""
if let price = Double(priceText),
let salesPrice = Double(salesPriceText),
!itemText.isEmpty {
let profitValue = price - salesPrice
ProfitLabel.text = String(profitValue)
let product = Product(item: itemText, price: price, salesPrice: salesPrice)
self.addProduct(product)
} else {
print("Enter something in 'item' and enter numeric values in 'price' and 'sales price'")
// add error handling
}
}
Im doing an IOS contact app and I want the user to able to change there quick information from an alert my problem is that I cannot get my alertAction to read anything beside the first and last element
let add = UIAlertAction(title: "Add", style: .default) { _ in
guard
let name = alert.textFields?.first?.text,
let address = alert.textFields?.first?.text,
let state = alert.textFields?.first?.text,
let city = alert.textFields?.first?.text,
let zip = alert.textFields?.first?.text,
let phoneString=alert.textFields?.last?.text,
let phone = Int(phoneString)
else { return }
I know that alert.textField?.first.text returns the first element and alert.textField?.last.text returns my last element ,so what ever goes in my name and phone textField is good , but how can I return the elements that go in to my address , state , city and zip text fields?
You can add tag to each of your textFields. And then utilize it, you can enumerate your textFields in your alert object. For example:
let alert = UIAlertController(title: "Add New Name", message: "", preferredStyle: .alert)
alert.addTextField { (textField : UITextField!) -> Void in
textField.placeholder = "Enter Name"
textField.tag = 0
}
alert.addTextField { (textField : UITextField!) -> Void in
textField.placeholder = "Enter Address"
textField.tag = 1
}
alert.addTextField { (textField : UITextField!) -> Void in
textField.placeholder = "Enter State"
textField.tag = 2
}
let saveAction = UIAlertAction(title: "Save", style: .default, handler: { _ -> Void in
guard let name = alert.textFields?.filter({ $0.tag == 0 }).first?.text,
let address = alert.textFields?.filter({ $0.tag == 1 }).first?.text,
let state = alert.textFields?.filter({ $0.tag == 2 }).first?.text else { return }
/////
})
alert.addAction(saveAction)
self.present(alert, animated: true, completion: nil)
OR
let name = alert.textFields?[0].text ?? ""
let address = alert.textFields?[1].text ?? ""
let state = alert.textFields?[2].text ?? ""
You can add multiple textfields inside a UIAlertController. UIAlertController has a property "textFields" which is basically a array of all added TextFields.
You can add a textfield using this code -
let alert = UIAlertController(title: "Alert", message: "Hi", preferredStyle: .alert)
alert.addTextField { (textFieldName) in
// Configure it here
}
alert.addTextField { (textFieldAddress) in
}
alert.addTextField { (textfieldCity) in
}
The order in which you add the textFields ,will be same when you will access the textfields using "textField" property.
I have an alert box that takes user input and starts a download task. After the user clicks "Ok," I want the screen to show a UIView that I added an ActivityIndicator to while the download occurs. The download occurs successfully and the function correctly opens up the next controller, however, the custom view nor activity indicator are ever displayed. Here's my code:
private func getKeyFromAlert() {
let alert = UIAlertController(title: "Enter Key", message: "Enter your text key below. If you want to scan it instead, click \"Scan Key.\" ", preferredStyle: .alert)
alert.addTextField { (textField) in
let attribString = NSAttributedString(string: "Enter your app code here")
textField.attributedPlaceholder = attribString
}
let scanAction = UIAlertAction(title: "Scan Key", style: .default) { _ in
self.openSettingsForApp()
}
let okAction = UIAlertAction(title: "OK", style: .default) { _ in
let textField = alert.textFields![0]
if let text = textField.text {
let encoded = text.toBase64()
let status = APIKeychain.storeToken(encoded)
if !status {
self.displayAlertForBadKeychain(useCamera: false)
} else {
self.addLoader()
self.dismissSetup()
}
} else {
let _ = APIKeychain.storeToken("")
self.addLoader()
self.dismissSetup()
}
}
alert.addAction(scanAction)
alert.addAction(okAction)
show(alert, sender: nil)
}
The function in question that I use to display the UIView is addLoader()
The code for addLoader is:
private func addLoader() {
let frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)
let loaderView = UIView(frame: frame)
loaderView.alpha = 1.0
loaderView.backgroundColor = UIColor.white
let activitySpinner: UIActivityIndicatorView = UIActivityIndicatorView(frame: loaderView.frame)
activitySpinner.center = loaderView.center
activitySpinner.hidesWhenStopped = false
activitySpinner.style = .whiteLarge
activitySpinner.startAnimating()
loaderView.addSubview(activitySpinner)
self.view.addSubview(loaderView)
self.view.layoutIfNeeded()
}
I've tried several iterations of setNeedsDisplay and setNeedsLayout without luck. I've also tried explicitly declaring this in a DispatchQueue.main.async without any affect as well.
EDIT
I added the code below for dismissSetup()
private func dismissSetup() {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
DispatchQueue.global(qos: .background).async {
if self.updateDatabase {
//This will start the download process
let _ = WebDataFetcher(dataStack: self.dataStack)
}
dispatchGroup.leave()
}
dispatchGroup.wait()
let mainSB = UIStoryboard(name: "UserInputs", bundle: nil)
let mainVC = mainSB.instantiateViewController(withIdentifier: "userInputs")
appDelegate.window?.rootViewController = mainVC
}
this is my code in xcode with swift 2. please see it first.
import UIKit
class sendComplaintViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var subjectTextField: UITextField!
#IBOutlet weak var typeDropDown: IQDropDownTextField!
#IBOutlet weak var messageTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
typeDropDown.isOptionalDropDown = false
typeDropDown.itemList = ["Choose Category","Complaint", "Suggestion"]
// Do any additional setup after loading the view.
messageTextView.text = "Placeholder"
messageTextView.textColor = UIColor.lightGrayColor()
messageTextView.becomeFirstResponder()
messageTextView.selectedTextRange = messageTextView.textRangeFromPosition(messageTextView.beginningOfDocument, toPosition: messageTextView.beginningOfDocument)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//placeholder textview
func messageTextView(messageTextView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
// Combine the textView text and the replacement text to
// create the updated text string
let currentText:NSString = messageTextView.text
let updatedText = currentText.stringByReplacingCharactersInRange(range, withString:text)
// If updated text view will be empty, add the placeholder
// and set the cursor to the beginning of the text view
if updatedText.isEmpty {
messageTextView.text = "Placeholder"
messageTextView.textColor = UIColor.lightGrayColor()
messageTextView.selectedTextRange = messageTextView.textRangeFromPosition(messageTextView.beginningOfDocument, toPosition: messageTextView.beginningOfDocument)
return false
}
// Else if the text view's placeholder is showing and the
// length of the replacement string is greater than 0, clear
// the text view and set its color to black to prepare for
// the user's entry
else if messageTextView.textColor == UIColor.lightGrayColor() && !text.isEmpty {
messageTextView.text = nil
messageTextView.textColor = UIColor.blackColor()
}
return true
}
func textViewDidChangeSelection(messageTextView: UITextView) {
if self.view.window != nil {
if messageTextView.textColor == UIColor.lightGrayColor() {
messageTextView.selectedTextRange = messageTextView.textRangeFromPosition(messageTextView.beginningOfDocument, toPosition: messageTextView.beginningOfDocument)
}
}
}
//border textview
override func viewDidLayoutSubviews() {
// Creates the bottom border
let borderBottom = CALayer()
let borderWidth = CGFloat(2.0)
borderBottom.borderColor = UIColor.grayColor().CGColor
borderBottom.frame = CGRect(x: 0, y: messageTextView.frame.height - 1.0, width: messageTextView.frame.width , height: messageTextView.frame.height - 1.0)
borderBottom.borderWidth = borderWidth
messageTextView.layer.addSublayer(borderBottom)
messageTextView.layer.masksToBounds = true
// Creates the Top border
let borderTop = CALayer()
borderTop.borderColor = UIColor.grayColor().CGColor
borderTop.frame = CGRect(x: 0, y: 0, width: messageTextView.frame.width, height: 1)
borderTop.borderWidth = borderWidth
messageTextView.layer.addSublayer(borderTop)
messageTextView.layer.masksToBounds = true
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
#IBAction func sendButtonTapped(sender: AnyObject){
//let controltype = controlTypeTextField.selectedItem
let subject = subjectTextField.text
let type = typeDropDown.selectedItem
let complaintMessage = messageTextView.text
let userId = NSUserDefaults.standardUserDefaults().stringForKey("userId")
if(type == nil || type! == "Choose Category"){
displayAlertMessage("Please Choose Category")
return
}
if(subject!.isEmpty || type!.isEmpty || complaintMessage!.isEmpty){
//display an alert message
displayAlertMessage("All fields are requiered to fill in")
return
}
//input fungsi mbprog
let spinningActivity = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
spinningActivity.labelText = "Loading"
spinningActivity.detailsLabelText = "Please wait"
//Send HTTP POST
let myUrl = NSURL(string: "");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
dispatch_async(dispatch_get_main_queue()){
spinningActivity.hide(true) //waiting send data to server (signup)
if error != nil{
self.displayAlertMessage(error!.localizedDescription)
return
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
if let parseJSON = json {
let complaintId = parseJSON["complaintId"] as? String
if( complaintId != nil)
{
let myAlert = UIAlertController(title: "Alert", message: "Success!", preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default){(action) in
self.dismissViewControllerAnimated(true, completion: nil)
}
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated: true, completion: nil)
} else {
let errorMessage = parseJSON["message"] as? String
if(errorMessage != nil)
{
self.displayAlertMessage(errorMessage!)
}
}
}
} catch{
//print(error)
print(error)
if data != nil {
let string = String(data: data!, encoding: NSUTF8StringEncoding)
print(string)
}
print(response)
}
}
}).resume()
}
#IBAction func cancelButtonTapped(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
func displayAlertMessage(userMessage:String){
let myAlert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title: "ok", style: UIAlertActionStyle.Default, handler: nil)
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated: true, completion: nil)
}
}
Is there any error in my code that makes placeholder didn't working right?
Placeholder is already showing but it didn't disappear after clicked.
thank you.
You are setting the text of your TextView, not the placeholder.
This piece of code messageTextView.text = "Placeholder" sets the text not the placeholder
If your view is a UITextView, then check this question Text View Placeholder Swift
You can use HCExtentionSwift for that it easy. You can check GitHub link for that pod here
Step 1 Install pod 'HCExtentionSwift'
Step 2 Go to Identity Inspector
Step 3 Insert class name TextViewDesign
Now after doing 3 steps you will see changes in Attribute Inspector
From there you can insert PlaceHolder Value
#IBAction func addToCart(sender: AnyObject) {
let itemObjectTitle = itemObject.valueForKey("itemDescription") as! String
let alertController = UIAlertController(title: "Add \(itemObjectTitle) to cart?", message: "", preferredStyle: .Alert)
let yesAction = UIAlertAction(title: "Yes", style: UIAlertActionStyle.Default) { (action) in
var tabArray = self.tabBarController?.tabBar.items as NSArray!
var tabItem = tabArray.objectAtIndex(1) as! UITabBarItem
let badgeValue = "1"
if let x = badgeValue.toInt() {
tabItem.badgeValue = "\(x)"
}
}
I don't know why I can't just do += "(x)"
Error:
binary operator '+=' cannot be applied to operands of type 'String?' and 'String'
I want it to increment by 1 each time the user selects "Yes". Right now obviously it just stays at 1.
You can try to access the badgeValue and convert it to Integer as follow:
Swift 2
if let badgeValue = tabBarController?.tabBar.items?[1].badgeValue,
nextValue = Int(badgeValue)?.successor() {
tabBarController?.tabBar.items?[1].badgeValue = String(nextValue)
} else {
tabBarController?.tabBar.items?[1].badgeValue = "1"
}
Swift 3 or later
if let badgeValue = tabBarController?.tabBar.items?[1].badgeValue,
let value = Int(badgeValue) {
tabBarController?.tabBar.items?[1].badgeValue = String(value + 1)
} else {
tabBarController?.tabBar.items?[1].badgeValue = "1"
}
To delete the badge just assign nil to the badgeValue overriding viewDidAppear method:
override func viewDidAppear(animated: Bool) {
tabBarController?.tabBar.items?[1].badgeValue = nil
}
Works with Swift 2:
let tabController = UIApplication.sharedApplication().windows.first?.rootViewController as? UITabBarController
let tabArray = tabController!.tabBar.items as NSArray!
let alertTabItem = tabArray.objectAtIndex(2) as! UITabBarItem
if let badgeValue = (alertTabItem.badgeValue) {
let intValue = Int(badgeValue)
alertTabItem.badgeValue = (intValue! + 1).description
print(intValue)
} else {
alertTabItem.badgeValue = "1"
}