@@ -23,6 +23,17 @@ open class SearchTableViewCell: UITableViewCell {
2323 ///
2424 open weak var delegate : SearchTableViewCellDelegate ?
2525
26+ /// If `true` the search delegate callback is called as the text field is edited.
27+ /// This class does not implement any Debouncer or assume a minimum character count because
28+ /// each search is different.
29+ ///
30+ open var liveSearch : Bool = false
31+
32+ /// If `true` then the user can type in spaces regularly. If `false` the whitespaces will be
33+ /// stripped before they're entered into the field.
34+ ///
35+ open var allowSpaces : Bool = true
36+
2637 /// Search UITextField's placeholder
2738 ///
2839 open var placeholder : String ? {
@@ -62,16 +73,68 @@ private extension SearchTableViewCell {
6273//
6374extension SearchTableViewCell : UITextFieldDelegate {
6475 open func textFieldShouldClear( _ textField: UITextField ) -> Bool {
65- delegate? . startSearch ( for: " " )
76+ if !liveSearch {
77+ delegate? . startSearch ( for: " " )
78+ }
79+
6680 return true
6781 }
6882
6983 open func textFieldShouldReturn( _ textField: UITextField ) -> Bool {
70- if let searchText = textField. text {
84+ if !liveSearch,
85+ let searchText = textField. text {
7186 delegate? . startSearch ( for: searchText)
7287 }
88+
89+ return false
90+ }
91+
92+ open func textField( _ textField: UITextField , shouldChangeCharactersIn range: NSRange , replacementString string: String ) -> Bool {
93+ let sanitizedString : String
94+
95+ if allowSpaces {
96+ sanitizedString = string
97+ } else {
98+ sanitizedString = string. trimmingCharacters ( in: . whitespacesAndNewlines)
99+ }
100+
101+ let hasValidEdits = sanitizedString. count > 0 || range. length > 0
102+
103+ if hasValidEdits {
104+ guard let start = textField. position ( from: textField. beginningOfDocument, offset: range. location) ,
105+ let end = textField. position ( from: start, offset: range. length) ,
106+ let textRange = textField. textRange ( from: start, to: end) else {
107+
108+ // This shouldn't really happen but if it does, let's at least let the edit go through
109+ return true
110+ }
111+
112+ textField. replace ( textRange, withText: sanitizedString)
113+
114+ if liveSearch {
115+ startLiveSearch ( )
116+ }
117+ }
118+
73119 return false
74120 }
121+
122+ /// Convenience method to abstract the logic that tells the delegate to start a live search.
123+ ///
124+ /// - Precondition: make sure you check if `liveSearch` is enabled before calling this method.
125+ ///
126+ private func startLiveSearch( ) {
127+ guard let delegate = delegate,
128+ let text = textField. text else {
129+ return
130+ }
131+
132+ if text. count == 0 {
133+ delegate. startSearch ( for: " " )
134+ } else {
135+ delegate. startSearch ( for: text)
136+ }
137+ }
75138}
76139
77140// MARK: - Loader
0 commit comments