Adding characters automatically to searchBar textField while user is typing? - swift

I am wondering if there is a way to add characters automatically to a searchBar textField while the user is typing. For example, if the user typed 533 then I want to automatically add " X " to it. I tried to do the following:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty == false {
if searchText.count > 2 {
searchText.insert(" X ", at: 3)
}
}
However, Xcode is displaying an error message next to searchText.insert(" X ", at: 3) stating:
Cannot use mutating member on immutable value: 'searchText' is a let constant

You can't modify the value of searchText. Method parameters are constants. Create a local variable with the new value. Then update the search bar's text.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.count > 2 {
var newText = searchText
newText.insert(" X ", at: 3)
searchBar.text = newText
}
}
But this still has issues that you need to work out. This doesn't account for the user moving the caret or the fact that you will add an X far too many times as the user enters more text into the search field.

Related

issue getting result in search bar in swift

i am working on a searchbar.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredData = patientArray.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()})
searching = true
tableView.reloadData()
}
In this above 2 images, If i am writting the exact same string, then i am getting the result . But if i am searching for any numbers like "20". then the result is empty.
How to fix the issue that if i search for any containing text(it may be numbers or text), i will get the result.
You are searching from the beginning of the string (prefix). If you want to search if a string contains a substring you have to use contains.
But there is a more efficient API, range(of:options:) which also avoids the repetitively calling of lowercased()
filteredData = patientArray.filter{$0.range(of: searchText, options: .caseInsensitive) != nil}

Return autosuggestion search with swift xcode

I have a search bar that filters through an xml array of recipe titles. The problem is that I have to search the entire title, otherwise I don't see suggested results. For example, if I have "Whole Grain Waffles" and "Whole Wheat Bread" typing "Whole" returns nothing. Typing "Whole Grain Waffles" returns successfully. This is the searchBar function
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
isSearching = false
view.endEditing(true)
myTableView.reloadData()
} else {
isSearching = true
filteredData = tableViewDataSource.filter({$0.title == searchBar.text})
myTableView.reloadData()
}
}
I'm pretty sure the solution has to do with case sensitivity, and returning certain characters when setting the filteredData. Thanks for help in advance
You could use contains to filter any item in array that contains the text or you can also use hasPrefix, if you want to search for the strings that start with the search text.
Something like this,
filteredData = tableViewDataSource.filter { $0.title.contains(searchBar.text) ?? "" }
Or,
filteredData = tableViewDataSource.filter { $0.title.hasPrefix(searchBar.text) ?? "" }
Using range worked in my situation
filteredData = tableViewDataSource.filter({$0.title.range(of: searchBar.text!) != nil})

Get word that is being typed

I'm implementing a tagging feature similar to that of Facebook. So when I type # and some character(s) after it, the function should return the word being typed.
So if the textView contains (and the cursor is at c)
Hello #Jac !
The function should return "#Jac"
If it contains (and the cursor is at a)
Hello #Ja !
Then the function should return "#Ja"
The final string of both examples would be,
Hello Jack !
I have attempted multiple solutions but none are working. One particular question was very similar to my question, but the solution has errors. Here is the link.
Update 1
Here is how I've set the delegate on the textView,
postView.textView.delegate = self
This is the code for detecting if the # character was tapped (display the friends list table, if it was)
if let text = self?.characterBeforeCursor() {
if (text == "#" && self?.friends.count != 0) {
self?.friendTableView.isHidden = false
} else {
var word // Need to get the word being typed
self?.displayedFriends = (self?.displayedFriends.filter { ($0["firstName"]?.hasPrefix(word))! })!
}
}
Update 2
The solution below did not solve the problem. It is returning all text in the textfield instead of just the word that is being typed.
There is a delegate function called shouldChangeCharactersInRange. From there you can get the current text after the user tapped the letter.
Make sure you use UITextFieldDelegate in your class declaration and set the textField's delegate to self.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//get the updated text from the text field like this:
let text = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
//note that we *need* to use the text as NSString, because the delegate method gives us an NSRange, rather than a Range which we can't use on String, but NSString, so we need to convert that first
return true //so the text is visually updated in the textfield
}
Edit
I just saw that you posted about UITextView. It's pretty much the same:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let text = (textView.text as NSString?)?.replacingCharacters(in: range, with: text)
return true
}
Edit 2
You need to also assign the text view's text to your variable in the code snippet you provided:
} else {
var word = postView.textView.text
self?.displayedFriends = (self?.displayedFriends.filter { ($0["firstName"]?.hasPrefix(word))! })!
}

Swift add target for paste action UITextField

I have a UITextField that has a target added which performs checks on the field as the user is typing. I currently have an issue however when my code adds text to the textfield in that the text doesn't get checked. Is there a way I can solve this through .editingChanged or is there another UIControlEvent to hook into?
Code is:
NumberOutlet.addTarget(self, action: #selector(handleNumberImage), for: .editingChanged)
The way you can handle this is by implementing the UITextViewDelegate protocol in your viewcontroller. In your viewDidLoad you would want to set the delegate of your UITextField to self.
Then, simply implement the following method, like demonstrated here:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.length > 1 {
// Text was pasted into the text field
// do something with pasted text
} else {
//typed string
}
return true
}
You will want to conform to the UITextFieldDelegate protocol.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let isAddingCharacter: Bool = range.location >= 0 && range.length == 0
let isDeletingCharacter: Bool = range.location >= 0 && range.length == 1
var newCount: Int = textView.text.characters.count
if isAddingCharacter {
newCount += 1
} else if isDeletingCharacter {
newCount -= 1
}
// If the newCount is > 0, the user is entering text
return true
}
Side note, your outlet should be named numberOutlet, not NumberOutlet. It is convention to use camel case syntax for variable names in swift.
The only way I know would be just to call the method you used as selector after you add text via code.
For example you have a method which is executed after you press a button and there you add text to your textfield and the UIControlEvent doesn't get fired here. So just call the method after adding text via code in this example after pressing a button:
#IBAction func buttonPressed(_ sender: UIButton) {
// Note: camel case convention
numberOutlet.text?.append("added text via code")
// perform your check method
handleNumberImage()
}

Force patterns in textfields

(Swift)
So the problem is:
My app (which is a kind of calculator) is crashing when the user puts in the textfield things that can't be calculated.
For example, if he types " -4-.", the app won't be able to do the math.
So, a pattern must be followed.
The following characters are allowed: 1234567890.-
The minus sign can only be typed when it is the first character in the textfield and cannot be typed again.
The point can only be typed after a number, and cannot be typed again.
Well you would have to determine:
When the user clicks on a number/digit/character, you would have to do a:
//Goes at top of one of your classes
var decimalCount:Int = 0
//At location of tap for character
if(decimalCount < 1) {
textField.text += "."
decimalCount += 1
}
This ideology could be applied to "-" as well.
Some how i have understood your question. According to my assumption our task is to validate the input for proper math function.Ok here we go.
First of all declare a bool variable at top of your class
var isNonNumericCharactersAllowes = true
At first we need to make our textfield to respond according to user input.So add delegate to text field and add the following delegate method.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
//1. To make sure that this is applicable to only particular textfield add tag.
if textField.tag == 1 {
let char = string.cStringUsingEncoding(NSUTF8StringEncoding)!
let isBackSpace = strcmp(char, "\\b")
//Helps to react only while typing and not while clearing text
if (isBackSpace != -92) {
let numbersOnly = NSCharacterSet(charactersInString: "1234567890")
let characterSetFromTextField = NSCharacterSet(charactersInString: string)
let Validate:Bool = numbersOnly .isSupersetOfSet(characterSetFromTextField)
if !Validate {
if isNonNumericCharactersAllowes {
isNonNumericCharactersAllowes = false
return true
}
return false
}
isNonNumericCharactersAllowes = true
}
}
return true
}
The above method stops unusual text entry's such as 0..012,--4,4++ etc..
Now while hitting calculate button we need to some validation.Add the following code in IBAction.
#IBAction func calculate(sender: AnyObject) {
let textContent:String!
textContent = textFieldTwo.text
var characterContainer = textContent.characters.map { String($0) }
let numbersOnly = NSCharacterSet(charactersInString: "1234567890")
let lastObjectOfString = NSCharacterSet(charactersInString: characterContainer.last!)
let Validate:Bool = numbersOnly .isSupersetOfSet(lastObjectOfString)
if !Validate {
characterContainer .removeLast()
textFieldTwo.text = characterContainer .joinWithSeparator("")
}
}
This above validation helps in removing things like 30+20+,4+4+, etc.. i.e removes unused operators at the end.