Swift 2: Call a method after pressing Return in UITextField - ios

I have a Text Field in a Tip Calculator. I want the Tip Calculator to update all the fields of my calculator.
With the current code that I have now, for whatever reason. It doesn't call calcTip() (or atleast it doesn't seem to) until I press return once, enter another (or the same) value and press return again.
I'm really close to having this app work exactly how I want it to, and I feel like I'm just 1 or 2 lines away.
My ViewController conforms to UITextFieldDelegate, and I declared amountBeforeTaxTextField.delegate = self in viewDidLoad.
TextShouldReturn:
func textFieldShouldReturn(textField: UITextField) -> Bool {
amountBeforeTaxTextField.clearsOnBeginEditing = true
amountBeforeTaxTextField.becomeFirstResponder()
calcTip()
amountBeforeTaxTextField.resignFirstResponder()
return true
}
calcTip():
func calcTip() {
let tip = tipCalc.calculateTip()
let total = tipCalc.calculateTotal()
let tax = tipCalc.calculateTax()
let perPerson = total / Float(Int(partySizeSlider.value))
tipCalc.tipPercentage = (tipPercentageSlider.value)
tipCalc.amountBeforeTax = (amountBeforeTaxTextField.text! as
NSString).floatValue
tipCalc.taxPercentage = (taxPercentageSlider.value)
resultLabel.text = String(format: "Tip $%.2f, Tax: $%.2f Total: $%.2f", tip, tax, total)
perPersonLabel.text = String(format: "Per-Person Total: $%.2f", perPerson)
}
Note: I have the same outcome with & without .becomeFirstResponder()
Thanks for reading.

So your textFieldShouldReturn function is called when you press the bottom right button on the keyboard, which can have various titles; such as "Go", "Return", "Done", or "Send".
Your first line, only accomplishes a UI effect; meaning when the user taps on the textfield to input text, there will be a little box that appears on the far right handside of the textfield that allows the user to clear the inputted text, if there is any. Sort of like a reset.
When you use becomeFirstResponder, you are pretty much calling another keyboard to pop up, so you aren't accomplishing anything by doing that.
When you use resignFirstResponder, you are hiding the keyboard.
What you want to do is remove "becomeFirstResponder" as well as the first line, because those don't do anything to help your problem. That should be the solution to the problem you are facing.

Related

UITapGestureRecognizer on a text field not as expected

In my class I have 11 UITapGestureRecognizers in an array textViewRecognizer attached to 11 out of 100 UITextFields in an array boxArray. When a Textfield is tapped containing a UIGestureRecognizer it runs tappedTextView where I try to get the index of the first responder.
However, due to some weird ordering in how things are executed, the action function only gives me the first responder of the previous first responder to the one that was just tapped.
Also, I have to double tap to even select the text field I was going for! I need to use the tap function and not the text delegates so this has been a real headache.
I have...
#objc func tappedTextField(_ sender: UITapGestureRecognizer) {
for i in 0...99 {
if (boxArray[i]?.isFirstResponder)! {
if let index = boxArray.index(of: boxArray[i]) {
print(index)
break
}
}
}
}
in my viewDidLoad I have
for i in 0...10 {
textFieldTapRecognizer[i].addTarget(self, action: #selector(self.tappedTextField(_:)))
}
In my class I have
I want to set 11 out of 100 textFields to have this a tap recognizer depending on some conditions (I'm just going to use a regular for loop here)
for i in 0...10 {
boxArray[i]?.addGestureRecognizer(textFieldTapRecognizer[i])
}
Is there anyway I can get it to give me the actual first responder, after the tap was made?
Is there anyway to go around the double tap to select the text field that has a UITapGesture?
Any help is greatly appreciated.
Edited: properly named functions
It sounds like you want to remove the automatic editing behavior on a UITextView. You can grab more control over that with the textViewShouldBeginEditing(_ textView: UITextView) -> Bool UITextViewDelegate method, documented here.
If you return false for that method, this should avoid needing a double tap to get to your gesture recognizer. Depending on your use case, you can then "allow" the tap to go to the text view by returning true for the textView you want to be actually edited.
While I'm not 100% clear on the first responder part of your question, since the textView won't be grabbing first responder if it's not starting it's editing mode, this should address that concern I believe. Good luck!
I would add a Tag to my UITextView and set the UITextViewDelegate to my ViewController.
Then I would add the following Delegate method:
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
print("Textview tag: ", textView.tag)
return false
}

How to disable UIControlEventEditingChanged

I have a UIViewController with several UITextFields. When tap one text field, it should present the barcode scanning view controller. Once the scanning is completed, my barcode scanning viewcontroller is disappearing (used "dismissViewcontroller") and the scanned value should entered into the text field I tapped. This is working fine. I have set the delegate for each text field like this.
[field addTarget:metrixUIViewControllerIn action:#selector(executeScriptOnTextFieldChange:) forControlEvents:UIControlEventEditingChanged];
The problem is this :
Lets say I have set an alert to display inside this executeScriptOnTextFieldChange method. Once I tapped on the 1st text field, then the barcode scanner comes. Once I scanned barcode scanner closes and set the value for the first text field and fire the alert.Thats ok. But then if scanned by tapping the 2nd textfield and the string will set to that textfield and fire the alert related to 2nd textfield also fire the alert related to first textfield as well. I want to stop happening this. Is there any way to disable the delegate for one textfield? This happens because I am refreshing the view in the viewDidAppear. But I have to do that as well. Please help me.
UIControlEventEditingChanged for a textField can fire at many different events that are not even directly related to that textField, but related inderectly.
For instance, when your ViewController is presenting the barcodeScanner it may trigger a "resignFirstResponder" event on the textField. Also when the 2nd textField is tapped, cause the 2nd becomes first responder and the 1st suffers a "resignFirstResponder".
I suggest trying to use a UITapGestureRecognizer in your textField instead. Example:
Swift 4
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.textField.tag = 1
self.textField.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(fireTextField(_:))))
}
#objc func fireTextField(_ sender: UIGestureRecognizer){
let view = sender.view
guard view != nil else{
//Do nothing
return
}
let condition = view!.tag == 1
if condition{
//or do whatever other stuff you need
self.textField.becomeFirstResponder()
}else{
//Whatever for other textFields
}
}
This way, you could use the "tag" attribute to determine which textField is firing and so adjust "condition". You could also filter the flow with a switch using the "tag".
Not sure if any of this will really help as I would need more info about the flow you need to accomplish. Hope it does help!

My CustomTableViewCell's read more/less function crashes

Thanks for your attention.
I have an app that uses a UITableView as a timeline that shows certain events, The Cell prototype was a little complex because i use multiple labels, a button and some imageViews that automatically change in function of the content of other fields.
In that cell, I have a UILabel, this UILabel can have 140 characters or 4 line jumps, if the text inside the label have more line jumps (\n) or are longer that 140 chars, I take a fragment and only display that fragment and add the text "... READ MORE"; when the user taps on the text, the label change and shows all the text, and at the end, appends the label "READ LESS", If the user taps again the label, it return to the initial state showing the fragment and the label "READ MORE" and so.
When I test this, it works on a device with iOS9, but in devices with iOS 10 (including simulators) It stops to work; it appears that when I Tap the label, the label changes as usual, but immediately returns to their original form. I register only one tap.
Have an idea?, Here is my code that is called to update the cell when the user taps the text label:
func cellTextPressed(gesture: UITapGestureRecognizer){
let cell: TimeLineViewCell = gesture.view?.superview?.superview as! TimeLineViewCell
let tappedIndexPath: NSIndexPath = self.timelineTableView.indexPathForCell(cell)!
NSLog ("Text Tapped at: \(tappedIndexPath)")
if ((cell.isReasonExpanded) == true)
{
cell.isReasonExpanded = false
let attrs = [NSForegroundColorAttributeName: UIColor.magentaColor()]
let attributedReducedText = NSMutableAttributedString(string: cell.reducedReason)
attributedReducedText.appendAttributedString(NSAttributedString(string: "... "))
attributedReducedText.appendAttributedString(NSAttributedString(string: "READ MORE", attributes: attrs))
cell.labelReason.attributedText = attributedReducedText
}
else
{
cell.isReasonExpanded = true
let attrs = [NSForegroundColorAttributeName: UIColor.magentaColor()]
let attributedRealText = NSMutableAttributedString(string: cell.realReason)
attributedRealText.appendAttributedString(NSAttributedString(string: " "))
attributedRealText.appendAttributedString(NSAttributedString(string: "READ LESS", attributes: attrs))
cell.labelReason.attributedText = attributedRealText
}
let lastScrollOffset = self.timelineTableView.contentOffset
UIView.performWithoutAnimation
{
self.timelineTableView.beginUpdates()
self.timelineTableView.endUpdates()
self.timelineTableView.layer.removeAllAnimations()
self.timelineTableView.setContentOffset(lastScrollOffset, animated: false)
}
}
I already have a method to avoid this.In IOS 9 and older versions, there are no problem with that code; but in iOS 10 there is different.
In documentation, apple says that the delegate method tableView cellForRowAtIndexPath is called everytime when a cell is displayed on screen, for that cell. In the older versions of IOS, it appears to be a bug that make this situation don't work, let us modify the view outside of this method whitout callback cellForRowAtIndexPath after refresh the cell.
I Added an array of Bool values that represents the status of the labels (If it have extended or shrinked value of the label) and initialize it on the ItemsDownload Method of my JSON receiver method
I add a condition that evaluates the value in that stateArray in cellForRowAtIndexPath() and put the full or the fragment of the text as corresponds.
finally, in the method posted above, added the change of the value at index path to the status Array instead to change all in that method

Voting buttons with Parse in Swift

A quick overview of what I'm trying to make. I'm making an app to show my film reviews. I have a PFQueryTableviewController of which I use a custom cell. This table shows films which come from my Parse database. In each cell, there a 3 UIButtons, of which I want the user to use to vote for the film (happyVote, OkVote, sadVote). Over the top of each button is a UILabel that simply displays a count.
How I want it to work
When a user presses one of the buttons, the vote increases by 1.
When a user presses the same button again, the vote decreases by 1. Or,
If the user had pressed a different button, the vote decreases on the first button and increases on the button just pressed.
The user can only ever vote on one of the buttons.
The vote is shown by the UILabel showing the count, and by the button image changing.
See below for a visual:
This is what I've added in Parse:
So far, I've added the code to increase the vote count in Parse, in my TableViewController.swift:
#IBAction func upVoteButton(sender: AnyObject) {
let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let hitIndex = self.tableView.indexPathForRowAtPoint(hitPoint)
let object = objectAtIndexPath(hitIndex)
object!.incrementKey("UpVoteCount")
object!.saveInBackground()
self.tableView.reloadData()
}
This kind of works, except, the user can keep increasing the count, and they can vote on all 3 buttons. And it doesn't change the Button Image when pressed.
In my cellForRowAtIndexPath method, I've put:
let downVote = object!.valueForKey("DownVoteCount") as! Int
cell.downVoteLabel.text = "\(downVote)"
let middleVote = object!.valueForKey("MiddleVoteCount") as! Int
cell.middleVoteLabel.text = "\(middleVote)"
let upVote = object!.valueForKey("UpVoteCount") as! Int
cell.upVoteLabel.text = "\(upVote)"
I have searched for a while for some examples of how to figure the rest out, but can't find anything, and I'm really struggling to figure the next steps out.
Any help will be greatly appreciated, and please let me know if you need/want to see anymore of my code. Thanks
In your upVoteButton() method, you can add the other two buttons and set them button.enabled = false inside an if statement like:
if downVoteButton.enabled == true {
downVoteButton.enabled = false
} else if downVoteButton.enabled == false {
downVoteButton.enabled = True
}
And do the same with the middleVoteButton in antoher if statement or the same, whatever floats your boat, also within the other button methods with the appropriate buttons. This helps disables the buttons when pressed and then enables it when it's pressed again.
And for the image changing you can follow this.

How can I check if a button is pressed during a specific time interval in iOS?

I'm creating a game for iOS and need some help.
Simply put, random letters will be shown in a label(changing at regular interval), and the user must press the button when they recognise a pattern. If they press the button they earn points, and if they miss it, they lose points.
For example:
let's say they are looking for a letter repeating over one gap. They see the letters "R K W K". On the second K they would press the button. I need to be able to check if the button was pressed while that second K was displayed. I have searched here on stack overflow, but I can't really find anything specific to my situation. Thanks in advance for your responses. I'm new here, so I tried to be as specific as possible.
You can do something like this:
Make a global variable of type bool lets call it isMatch.
While displaying other letters make isMatch = false;
When you display the second 'K' (as your example) make isMatch = true
In you button click method check:
if(isMatch){
point++;
}
else point--;
Make sure you change isMatch = false on displaying other letters.
Hope this helps.. :)
As I understood you are displaying K (say) in UILabel and you might be getting input in UITextField, what you should do is implement ValueChanged IBAction for UITextField and add all letters (K, M, X, H) etc in an array what you have on screen for now. When the text changes it will come in above IBAction, match the UITextField's text with the elements of an array. If there is a match, you are done!
Hope this helps!
Just check in the selector for the button ... (If you're using Interface builder replace void with IBAction)
- (void) buttonWasPressed:(UIButton *) sender {
if ([[[sender titleLabel] text] isEqualToString:#"K"])
point ++;
else
point --;
}
try like this
- (void)buttonTapped:(UIButton *)yourbutton;
{
yourbutton.selected = ![yourbutton isSelected];
}
then this
[self.yourbutton isSelected];

Resources