Reputation: 437
I'm a SwiftUI beginner. I have an array of "values" provided by an API and what I want is to make autocompletion when we tap characters in a "textfield". Can you please provide me an example of code for SwiftUI which can do this stuff ?
What I mean by autocompletion is this :
I have my own values and not those provided by google such here;
thx
Upvotes: 1
Views: 8714
Reputation: 655
Filter a list with values, easily with the build in .searchable function:
List {
ForEach(searchResults, id: \.self) { name in
Text(name)
}
}
.searchable(text: $textField, placement: .navigationBarDrawer(displayMode: .always))
The value for `placement displays the searchbar always.
Also add a filter variable:
var searchResults: [String] {
if textField.isEmpty {
return sectionNames
} else {
return sectionNames.filter { $0.lowercased().contains(textField.lowercased()) }
}
}
Of course don't miss out the textFieldInput:
@State var textField: String = ""
Upvotes: 1
Reputation: 1850
The code from this repository used: https://github.com/simonloewe/TextFieldInputPrediction
And modified so the predictions are returned as a list like this:
//
// ContentView.swift
// StackOverflow
//
// Created by Simon Löwe on 04.04.20.
// Copyright © 2020 Simon Löwe. All rights reserved.
//
import SwiftUI
struct ContentView: View {
@State var textFieldInput: String = ""
@State var predictableValues: Array<String> = ["First", "Second", "Third", "Fourth"]
@State var predictedValue: Array<String> = []
var body: some View {
VStack(alignment: .leading){
Text("Predictable Values: ").bold()
HStack{
ForEach(self.predictableValues, id: \.self){ value in
Text(value)
}
}
PredictingTextField(predictableValues: self.$predictableValues, predictedValues: self.$predictedValue, textFieldInput: self.$textFieldInput)
.textFieldStyle(RoundedBorderTextFieldStyle())
// This is the only modification from the example in the repository
List() {
ForEach(self.predictedValue, id: \.self){ value in
Text(value)
}
}
}.padding()
}
}
/// TextField capable of making predictions based on provided predictable values
struct PredictingTextField: View {
/// All possible predictable values. Can be only one.
@Binding var predictableValues: Array<String>
/// This returns the values that are being predicted based on the predictable values
@Binding var predictedValues: Array<String>
/// Current input of the user in the TextField. This is Binded as perhaps there is the urge to alter this during live time. E.g. when a predicted value was selected and the input should be cleared
@Binding var textFieldInput: String
/// The time interval between predictions based on current input. Default is 0.1 second. I would not recommend setting this to low as it can be CPU heavy.
@State var predictionInterval: Double?
/// Placeholder in empty TextField
@State var textFieldTitle: String?
@State private var isBeingEdited: Bool = false
init(predictableValues: Binding<Array<String>>, predictedValues: Binding<Array<String>>, textFieldInput: Binding<String>, textFieldTitle: String? = "", predictionInterval: Double? = 0.1){
self._predictableValues = predictableValues
self._predictedValues = predictedValues
self._textFieldInput = textFieldInput
self.textFieldTitle = textFieldTitle
self.predictionInterval = predictionInterval
}
var body: some View {
TextField(self.textFieldTitle ?? "", text: self.$textFieldInput, onEditingChanged: { editing in self.realTimePrediction(status: editing)}, onCommit: { self.makePrediction()})
}
/// Schedules prediction based on interval and only a if input is being made
private func realTimePrediction(status: Bool) {
self.isBeingEdited = status
if status == true {
Timer.scheduledTimer(withTimeInterval: self.predictionInterval ?? 1, repeats: true) { timer in
self.makePrediction()
if self.isBeingEdited == false {
timer.invalidate()
}
}
}
}
/// Capitalizes the first letter of a String
private func capitalizeFirstLetter(smallString: String) -> String {
return smallString.prefix(1).capitalized + smallString.dropFirst()
}
/// Makes prediciton based on current input
private func makePrediction() {
self.predictedValues = []
if !self.textFieldInput.isEmpty{
for value in self.predictableValues {
if self.textFieldInput.split(separator: " ").count > 1 {
self.makeMultiPrediction(value: value)
}else {
if value.contains(self.textFieldInput) || value.contains(self.capitalizeFirstLetter(smallString: self.textFieldInput)){
if !self.predictedValues.contains(String(value)) {
self.predictedValues.append(String(value))
}
}
}
}
}
}
/// Makes predictions if the input String is splittable
private func makeMultiPrediction(value: String) {
for subString in self.textFieldInput.split(separator: " ") {
if value.contains(String(subString)) || value.contains(self.capitalizeFirstLetter(smallString: String(subString))){
if !self.predictedValues.contains(value) {
self.predictedValues.append(value)
}
}
}
}
}
Provides the following outcome:
Tested on Version 11.5 and iOS 13.5
Upvotes: 7