Inline Autocomplete TextFields — Swift 3 Tutorial For iOS

The Problem: Inline Text Autocomplete

I was surprised at how easy it was to find resources to make an autocomplete text field with a tableView below to show results. I had initially thought that the matching of a users string to an array of results would be the difficult part but I couldn’t have been more wrong. With the help of Stack Overflow I had that up and running in minutes. The challenge turned out to be returning the results in the textField. I hope this will help you implement your own solutions without having to deal with all the little problems I had to.

Breaking Down The Problem Into What We Are Trying To Do

  • We need to grab the users query
  • Check if their query matches anything in our array of strings
  • If it is we want the rest of the string to be displayed after the users query in a different colour

Things To Consider

  • We need to keep the cursor in the correct position for the user
  • We also need to have seamless support for a user misspelling the word or using backspace
  • We need to be able to tell what in the text box is the user text and what’s our autocompletion text
  • We need to deal with users using weird capitalization
  • What if the user clears the textfield

Step 1: Set Up Our ViewController

class GuidedSearchViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var textField: UITextField!
var autoCompletionPossibilities = [“Apple”, “Pineapple”, “Organge”]
var autoCompleteCharacterCount = 0
var timer = Timer()
}

Note: you need to control drag from your textField to the little yellow circle at the top of your storyboard and select ‘delegate’.

Step 2: Send The Text From The TextField To Our SearchAutocompleteEntries Function

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { //1
var subString = (textField.text!.capitalized as NSString).replacingCharacters(in: range, with: string) // 2
subString = formatSubstring(subString: subString)

if subString.characters.count == 0 { // 3 when a user clears the textField
resetValues()
} else {
searchAutocompleteEntriesWIthSubstring(substring: subString) //4
}
return true
}
func formatSubstring(subString: String) -> String {
let formatted = String(subString.characters.dropLast(autoCompleteCharacterCount)).lowercased().capitalized //5
return formatted
}

func resetValues() {
autoCompleteCharacterCount = 0
textField.text = “”
}
  1. We made our VC conform to UITextFieldDelegate so start typing “shouldChange” and it will autocomplete our function
  2. Inside this function we grab the text and format it.
  3. This check is to see if the user clears the textField.
  4. Send the users query to the function we are about to create
  5. I formatted it to be lowercased and then capitalized because of how my autocompletePossibilities array is formatted. We need the .lowercased incase they have caps lock on. The important part of this line is the .dropLast function. We need to do this to drop off the previous autocomplete text.

Step 3: Search Our Autocomplete Possibilities And Add The Autocomplete Suggestion To The TextField

func searchAutocompleteEntriesWIthSubstring(substring: String) {
let userQuery = substring
let suggestions = getAutocompleteSuggestions(userText: substring) //1

if suggestions.count > 0 {
timer = .scheduledTimer(withTimeInterval: 0.01, repeats: false, block: { (timer) in //2
let autocompleteResult = self.formatAutocompleteResult(substring: substring, possibleMatches: suggestions) // 3
self.putColourFormattedTextInTextField(autocompleteResult: autocompleteResult, userQuery : userQuery) //4
self.moveCaretToEndOfUserQueryPosition(userQuery: userQuery) //5
})
} else {
timer = .scheduledTimer(withTimeInterval: 0.01, repeats: false, block: { (timer) in //7
self.textField.text = substring
})
autoCompleteCharacterCount = 0
}
}
  1. ‘suggestions’ is an array of possible autocomplete matches
  2. We need to use timers in order to replace the text in the textField with no weird behaviour. It’s not ideal as there is a slight delay but it works almost perfectly.
  3. This takes the first result in ‘suggestions’ and cuts off the characters the user has typed.
  4. This puts together the users query and our formatted suggestion, colours the suggestion text and puts it in the textField
  5. Moves the caret to after the users query and before the autocomplete suggestion

Step 4 The Functions We Need To Make Step 3 Work

func getAutocompleteSuggestions(userText: String) -> [String]{
var possibleMatches: [String] = []
for item in autoCompletionPossibilities { //2
let myString:NSString! = item as NSString
let substringRange :NSRange! = myString.range(of: userText)

if (substringRange.location == 0)
{
possibleMatches.append(item)
}
}
return possibleMatches
}

func putColourFormattedTextInTextField(autocompleteResult: String, userQuery : String) {
let colouredString: NSMutableAttributedString = NSMutableAttributedString(string: userQuery + autocompleteResult)
colouredString.addAttribute(NSForegroundColorAttributeName, value: UIColor.white, range: NSRange(location: userQuery.characters.count,length:autocompleteResult.characters.count))
self.textField.attributedText = colouredString
}
func moveCaretToEndOfUserQueryPosition(userQuery : String) {
if let newPosition = self.textField.position(from: self.textField.beginningOfDocument, offset: userQuery.characters.count) {
self.textField.selectedTextRange = self.textField.textRange(from: newPosition, to: newPosition)
}
let selectedRange: UITextRange? = textField.selectedTextRange
textField.offset(from: textField.beginningOfDocument, to: (selectedRange?.start)!)
}
func formatAutocompleteResult(substring: String, possibleMatches: [String]) -> String {
var autoCompleteResult = possibleMatches[0]
autoCompleteResult.removeSubrange(autoCompleteResult.startIndex..<autoCompleteResult.index(autoCompleteResult.startIndex, offsetBy: substring.characters.count))
autoCompleteCharacterCount = autoCompleteResult.characters.count
return autoCompleteResult
}

Note: If you are doing this on a white background the white auto-complete text obviously won’t show. Change your background colour or the colour of the auto-complete text.

I explained generally what these functions do in Step 3. If you are unsure, just ask me. I hope they are self explanatory.

Conclusion

I hope this helps you out, if it did please like this post and follow me. I am going to continue to post the solutions to problems I come across in my own app development.

Tutorial by aestusLabs. Share your knowledge.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.