@@ -191,8 +191,16 @@ open class WSTagsField: UIScrollView {
191
191
return false
192
192
}
193
193
194
+ open var allowsMultipleSelection : Bool = false {
195
+ didSet {
196
+ tagViews. forEach { $0. allowsMultipleSelection = allowsMultipleSelection }
197
+ }
198
+ }
199
+
200
+ open var autoSelectTagWhenAdded : Bool = false
201
+
194
202
open fileprivate( set) var tags = [ WSTag] ( )
195
- internal var tagViews = [ WSTagView] ( )
203
+ open var tagViews = [ WSTagView] ( )
196
204
197
205
// MARK: - Events
198
206
@@ -317,15 +325,21 @@ open class WSTagsField: UIScrollView {
317
325
318
326
open func beginEditing( ) {
319
327
self . textField. becomeFirstResponder ( )
320
- self . unselectAllTagViewsAnimated ( false )
328
+ if !allowsMultipleSelection {
329
+ self . unselectAllTagViewsAnimated ( false )
330
+ }
321
331
}
322
332
323
333
open func endEditing( ) {
324
- // NOTE: We used to check if .isFirstResponder and then resign first responder, but sometimes we noticed
325
- // that it would be the first responder, but still return isFirstResponder=NO.
334
+ // NOTE: We used to check if .isFirstResponder and then resign first responder, but sometimes we noticed
335
+ // that it would be the first responder, but still return isFirstResponder=NO.
326
336
// So always attempt to resign without checking.
327
337
self . textField. resignFirstResponder ( )
328
338
}
339
+
340
+ open func addInputAccessoryView( view: UIView ) {
341
+ textField. inputAccessoryView = view
342
+ }
329
343
330
344
// MARK: - Adding / Removing Tags
331
345
open func addTags( _ tags: [ String ] ) {
@@ -362,15 +376,16 @@ open class WSTagsField: UIScrollView {
362
376
tagView. borderColor = self . borderColor
363
377
tagView. keyboardAppearanceType = self . keyboardAppearance
364
378
tagView. layoutMargins = self . layoutMargins
379
+ tagView. allowsMultipleSelection = allowsMultipleSelection
365
380
366
381
tagView. onDidRequestSelection = { [ weak self] tagView in
367
- self ? . selectTagView ( tagView, animated: true )
382
+ self ? . toggleTagView ( tagView, animated: true )
368
383
}
369
384
370
385
tagView. onDidRequestDelete = { [ weak self] tagView, replacementText in
371
386
// First, refocus the text field
372
387
self ? . textField. becomeFirstResponder ( )
373
- if ( replacementText? . isEmpty ?? false ) == false {
388
+ if replacementText? . isEmpty == false {
374
389
self ? . textField. text = replacementText
375
390
}
376
391
// Then remove the view from our data
@@ -401,6 +416,15 @@ open class WSTagsField: UIScrollView {
401
416
repositionViews ( )
402
417
}
403
418
419
+ open func setRemovable( tags: [ String ] , removable: Bool = false ) {
420
+ assert ( tagViews. count > 0 , " There are no tagViews. Did you call this method after adding tags? " )
421
+ tagViews. filter { tags. contains ( $0. textLabel. text ?? " " ) } . forEach { $0. removable = removable }
422
+ }
423
+
424
+ open func getSelectedTagStrings( ) -> [ String ] {
425
+ return tagViews. filter { $0. selected } . compactMap { $0. textLabel. text }
426
+ }
427
+
404
428
open func removeTag( _ tag: String ) {
405
429
removeTag ( WSTag ( tag) )
406
430
}
@@ -415,6 +439,10 @@ open class WSTagsField: UIScrollView {
415
439
if index < 0 || index >= self . tags. count { return }
416
440
417
441
let tagView = self . tagViews [ index]
442
+ if !tagView. removable {
443
+ return
444
+ }
445
+
418
446
tagView. removeFromSuperview ( )
419
447
self . tagViews. remove ( at: index)
420
448
@@ -435,7 +463,18 @@ open class WSTagsField: UIScrollView {
435
463
let text = self . textField. text? . trimmingCharacters ( in: CharacterSet . whitespaces) ?? " "
436
464
if text. isEmpty == false && ( onVerifyTag ? ( self , text) ?? true ) {
437
465
let tag = WSTag ( text)
466
+ if self . tags. contains ( tag) {
467
+ self . textField. text = " "
468
+ return nil
469
+ }
470
+
438
471
addTag ( tag)
472
+ if let tagView = tagViews. last, autoSelectTagWhenAdded {
473
+ //There's a bug that causes the text to be truncated during animation ("New York" becomes "New Y..."). This delays animating the TagView until after it's set in the TextField.
474
+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.15 ) {
475
+ self . toggleTagView ( tagView, animated: false )
476
+ }
477
+ }
439
478
440
479
self . textField. text = " "
441
480
onTextFieldDidChange ( self . textField)
@@ -477,23 +516,25 @@ open class WSTagsField: UIScrollView {
477
516
}
478
517
}
479
518
480
- open func selectTagView ( _ tagView: WSTagView , animated: Bool = false ) {
519
+ open func toggleTagView ( _ tagView: WSTagView , animated: Bool = false ) {
481
520
if self . readOnly {
482
521
return
483
522
}
484
-
485
- if tagView. selected {
486
- tagView. onDidRequestDelete ? ( tagView, nil )
487
- return
523
+
524
+ tagView. selected = !tagView. selected
525
+
526
+ if !allowsMultipleSelection {
527
+ tagViews. filter { $0 != tagView } . forEach {
528
+ $0. selected = false
529
+ onDidUnselectTagView ? ( self , $0)
530
+ }
488
531
}
489
-
490
- tagView. selected = true
491
- tagViews . filter { $0 != tagView } . forEach {
492
- $0 . selected = false
493
- onDidUnselectTagView ? ( self , $0 )
532
+
533
+ if tagView. selected {
534
+ onDidSelectTagView ? ( self , tagView )
535
+ } else {
536
+ onDidUnselectTagView ? ( self , tagView )
494
537
}
495
-
496
- onDidSelectTagView ? ( self , tagView)
497
538
}
498
539
499
540
open func unselectAllTagViewsAnimated( _ animated: Bool = false ) {
@@ -592,10 +633,15 @@ extension WSTagsField {
592
633
}
593
634
594
635
textField. onDeleteBackwards = { [ weak self] in
595
- if self ? . readOnly ?? true { return }
636
+ if self ? . readOnly == true { return }
637
+
638
+ if self ? . allowsMultipleSelection == true , self ? . textField. text? . isEmpty == true , let lastTag = self ? . tags. last {
639
+ self ? . removeTag ( lastTag)
640
+ return
641
+ }
596
642
597
- if self ? . textField. text? . isEmpty ?? true , let tagView = self ? . tagViews. last {
598
- self ? . selectTagView ( tagView, animated: true )
643
+ if self ? . textField. text? . isEmpty == true , let tagView = self ? . tagViews. last {
644
+ self ? . toggleTagView ( tagView, animated: true )
599
645
self ? . textField. resignFirstResponder ( )
600
646
}
601
647
}
@@ -709,7 +755,7 @@ extension WSTagsField {
709
755
oldIntrinsicContentHeight = newIntrinsicContentHeight
710
756
}
711
757
712
- if self . enableScrolling {
758
+ if self . enableScrolling {
713
759
self . isScrollEnabled = contentRect. height + contentInset. top + contentInset. bottom >= newIntrinsicContentHeight
714
760
}
715
761
self . contentSize. width = self . bounds. width - contentInset. left - contentInset. right
@@ -746,7 +792,9 @@ extension WSTagsField: UITextFieldDelegate {
746
792
747
793
public func textFieldDidBeginEditing( _ textField: UITextField ) {
748
794
textDelegate? . textFieldDidBeginEditing ? ( textField)
749
- unselectAllTagViewsAnimated ( true )
795
+ if !allowsMultipleSelection {
796
+ unselectAllTagViewsAnimated ( true )
797
+ }
750
798
}
751
799
752
800
public func textFieldDidEndEditing( _ textField: UITextField ) {
0 commit comments