├── .DS_Store
├── styleKit.pcvd
├── WATokenFieldView.gif
├── WATokenFieldView
├── .DS_Store
├── WASearchTextField.h
├── WATextField.h
├── WATokenFieldViewStyleKit.h
├── WATokenView.h
├── WATextField.m
├── WATokenFieldViewStyleKit.m
├── WATokenFieldView.h
├── WASearchTextField.m
├── WATokenView.m
└── WATokenFieldView.m
├── WATokenFieldViewExample
├── .DS_Store
├── Images.xcassets
│ ├── TokenBackground.imageset
│ │ ├── TokenBackground@2x.png
│ │ └── Contents.json
│ ├── ModernContactSelectionEmpty.imageset
│ │ ├── ModernContactSelectionEmpty.png
│ │ ├── ModernContactSelectionEmpty@2x.png
│ │ └── Contents.json
│ ├── ModernContactSelectionChecked.imageset
│ │ ├── ModernContactSelectionChecked.png
│ │ ├── ModernContactSelectionChecked@2x.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── AppDelegate.h
├── WACheckButton.h
├── ViewController.h
├── main.m
├── WAFriendTableViewCell.h
├── Base.lproj
│ ├── Main.storyboard
│ └── LaunchScreen.xib
├── Info.plist
├── AppDelegate.m
├── WACheckButton.m
├── WAFriendTableViewCell.m
└── ViewController.m
├── WATokenFieldViewExample.xcodeproj
├── project.xcworkspace
│ ├── xcuserdata
│ │ └── wenders.xcuserdatad
│ │ │ ├── UserInterfaceState.xcuserstate
│ │ │ └── WorkspaceSettings.xcsettings
│ └── contents.xcworkspacedata
├── xcuserdata
│ └── wenders.xcuserdatad
│ │ ├── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── WATokenFieldView.xcscheme
│ │ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
└── project.pbxproj
├── .gitignore
├── WATokenFieldViewTests
├── Info.plist
└── WATokenFieldViewTests.m
├── LICENSE
├── WATokenFieldView.podspec
└── README.md
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wendyabrantes/WATokenFieldView/HEAD/.DS_Store
--------------------------------------------------------------------------------
/styleKit.pcvd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wendyabrantes/WATokenFieldView/HEAD/styleKit.pcvd
--------------------------------------------------------------------------------
/WATokenFieldView.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wendyabrantes/WATokenFieldView/HEAD/WATokenFieldView.gif
--------------------------------------------------------------------------------
/WATokenFieldView/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wendyabrantes/WATokenFieldView/HEAD/WATokenFieldView/.DS_Store
--------------------------------------------------------------------------------
/WATokenFieldViewExample/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wendyabrantes/WATokenFieldView/HEAD/WATokenFieldViewExample/.DS_Store
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Images.xcassets/TokenBackground.imageset/TokenBackground@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wendyabrantes/WATokenFieldView/HEAD/WATokenFieldViewExample/Images.xcassets/TokenBackground.imageset/TokenBackground@2x.png
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Images.xcassets/ModernContactSelectionEmpty.imageset/ModernContactSelectionEmpty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wendyabrantes/WATokenFieldView/HEAD/WATokenFieldViewExample/Images.xcassets/ModernContactSelectionEmpty.imageset/ModernContactSelectionEmpty.png
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Images.xcassets/ModernContactSelectionEmpty.imageset/ModernContactSelectionEmpty@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wendyabrantes/WATokenFieldView/HEAD/WATokenFieldViewExample/Images.xcassets/ModernContactSelectionEmpty.imageset/ModernContactSelectionEmpty@2x.png
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Images.xcassets/ModernContactSelectionChecked.imageset/ModernContactSelectionChecked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wendyabrantes/WATokenFieldView/HEAD/WATokenFieldViewExample/Images.xcassets/ModernContactSelectionChecked.imageset/ModernContactSelectionChecked.png
--------------------------------------------------------------------------------
/WATokenFieldViewExample.xcodeproj/project.xcworkspace/xcuserdata/wenders.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wendyabrantes/WATokenFieldView/HEAD/WATokenFieldViewExample.xcodeproj/project.xcworkspace/xcuserdata/wenders.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Images.xcassets/ModernContactSelectionChecked.imageset/ModernContactSelectionChecked@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wendyabrantes/WATokenFieldView/HEAD/WATokenFieldViewExample/Images.xcassets/ModernContactSelectionChecked.imageset/ModernContactSelectionChecked@2x.png
--------------------------------------------------------------------------------
/WATokenFieldViewExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/WACheckButton.h:
--------------------------------------------------------------------------------
1 | //
2 | // GICheckButton.h
3 | // GetIn
4 | //
5 | // Created by Wendy Abrantes on 07/09/2015.
6 | // Copyright (c) 2015 nimbletank. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface WACheckButton : UIButton
12 |
13 | @property (nonatomic, strong) UIImageView *checkView;
14 |
15 | - (void)setChecked:(bool)checked animated:(bool)animated;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "WATokenFieldView.h"
11 |
12 | @interface ViewController : UIViewController
13 |
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample.xcodeproj/project.xcworkspace/xcuserdata/wenders.xcuserdatad/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges
6 |
7 | SnapshotAutomaticallyBeforeSignificantChanges
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Images.xcassets/TokenBackground.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "TokenBackground@2x.png"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/WATokenFieldView/WASearchTextField.h:
--------------------------------------------------------------------------------
1 | //
2 | // WASearchTextField.h
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 |
9 | #import "WATextField.h"
10 |
11 | @interface WASearchTextField : WATextField
12 |
13 | @property (nonatomic, strong) UILabel *customPlaceholderLabel;
14 |
15 | - (void)setShowPlaceholder:(bool)showPlaceholder animated:(bool)animated;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/WAFriendTableViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // InviteFriendTableViewCell.h
3 | // GetIn
4 | //
5 | // Created by Wendy Abrantes on 07/09/2015.
6 | // Copyright (c) 2015 nimbletank. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface WAFriendTableViewCell : UITableViewCell
12 | {
13 |
14 | }
15 |
16 | +(NSString*)reuseIdentifier;
17 |
18 | -(void)updateWithFirstName:(NSString*)firstName
19 | lastName:(NSString*)lastName;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/WATokenFieldView/WATextField.h:
--------------------------------------------------------------------------------
1 | //
2 | // WATextField.h
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface WATextField : UITextField
12 |
13 | @property (nonatomic) CGFloat editingRectOffset;
14 |
15 | @property (nonatomic, strong) UIColor *placeholderColor;
16 | @property (nonatomic, strong) UIFont *placeholderFont;
17 |
18 | @property (nonatomic) CGFloat leftInset;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Images.xcassets/ModernContactSelectionEmpty.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x",
6 | "filename" : "ModernContactSelectionEmpty.png"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x",
11 | "filename" : "ModernContactSelectionEmpty@2x.png"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Images.xcassets/ModernContactSelectionChecked.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x",
6 | "filename" : "ModernContactSelectionChecked.png"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x",
11 | "filename" : "ModernContactSelectionChecked@2x.png"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | #Pods/
27 |
--------------------------------------------------------------------------------
/WATokenFieldView/WATokenFieldViewStyleKit.h:
--------------------------------------------------------------------------------
1 | //
2 | // WATokenFieldViewStyleKit.h
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 | // Generated by PaintCode (www.paintcodeapp.com)
9 | //
10 |
11 | #import
12 | #import
13 |
14 |
15 | @interface WATokenFieldViewStyleKit : NSObject
16 |
17 | // iOS Controls Customization Outlets
18 | @property(strong, nonatomic) IBOutletCollection(NSObject) NSArray* tokenBackgroundHighlightedTargets;
19 |
20 | // Generated Images
21 | + (UIImage*)imageOfTokenBackgroundHighlighted;
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/WATokenFieldView/WATokenView.h:
--------------------------------------------------------------------------------
1 | //
2 | // WATokenView.h
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface WATokenView : UIButton
12 |
13 | @property (nonatomic, strong) NSString *label;
14 | @property (nonatomic) float preferredWidth;
15 |
16 | @property (nonatomic, strong) id tokenId;
17 |
18 | -(instancetype)initWithFrame:(CGRect)frame
19 | labelFont:(UIFont*)paramLabelFont
20 | textColor:(UIColor*)paramTextColor
21 | highlightedColor:(UIColor*)paramHighlightedColor;
22 |
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample.xcodeproj/xcuserdata/wenders.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | WATokenFieldView.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | C522BFAB1BA3F4C300D66DF9
16 |
17 | primary
18 |
19 |
20 | C522BFC41BA3F4C300D66DF9
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/WATokenFieldViewTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.wenders.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/WATokenFieldViewTests/WATokenFieldViewTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // WATokenFieldViewTests.m
3 | // WATokenFieldViewTests
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface WATokenFieldViewTests : XCTestCase
13 |
14 | @end
15 |
16 | @implementation WATokenFieldViewTests
17 |
18 | - (void)setUp {
19 | [super setUp];
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 | }
22 |
23 | - (void)tearDown {
24 | // Put teardown code here. This method is called after the invocation of each test method in the class.
25 | [super tearDown];
26 | }
27 |
28 | - (void)testExample {
29 | // This is an example of a functional test case.
30 | XCTAssert(YES, @"Pass");
31 | }
32 |
33 | - (void)testPerformanceExample {
34 | // This is an example of a performance test case.
35 | [self measureBlock:^{
36 | // Put the code you want to measure the time of here.
37 | }];
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Wendy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/WATokenFieldView.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod spec lint WATokenFieldView.podspec' to ensure this is a
3 | # valid spec and to remove all comments including this before submitting the spec.
4 | #
5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html
6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
7 | #
8 |
9 | Pod::Spec.new do |s|
10 |
11 | s.name = "WATokenFieldView"
12 | s.version = "1.0"
13 | s.authors = { "Wendy Abrantes" => "abranteswendy@gmail.com" }
14 | s.homepage = "https://github.com/wendyabrantes/WATokenFieldView"
15 | s.summary = "WATokenFieldView is custom view that let you add token view in a view while typing/searching."
16 | s.source = { :git => "https://github.com/wendyabrantes/WATokenFieldView.git",
17 | :tag => '1.0' }
18 | s.license = { :type => "MIT", :file => "LICENSE" }
19 | s.platform = :ios, '8.0'
20 | s.source_files = "WATokenFieldView/**/*.{h,m}"
21 |
22 | s.requires_arc = true
23 |
24 | s.ios.deployment_target = '8.0'
25 | s.ios.frameworks = ['UIKit', 'Foundation']
26 | end
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.wenders.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/WATokenFieldView/WATextField.m:
--------------------------------------------------------------------------------
1 | #import "WATextField.h"
2 |
3 | @implementation WATextField
4 |
5 | - (void)drawPlaceholderInRect:(CGRect)rect
6 | {
7 | if (_placeholderColor == nil || _placeholderFont == nil)
8 | [super drawPlaceholderInRect:rect];
9 | else
10 | {
11 | CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), _placeholderColor.CGColor);
12 |
13 | CGSize placeholderSize = [self.placeholder sizeWithAttributes:@{
14 | NSFontAttributeName :_placeholderFont
15 | }];
16 |
17 | CGPoint placeholderOrigin = CGPointMake(0.0f, floorf((rect.size.height - placeholderSize.height) / 2.0f) - 0.5f);
18 | if (self.textAlignment == NSTextAlignmentCenter)
19 | placeholderOrigin.x = floorf((rect.size.width - placeholderSize.width) / 2.0f);
20 | else if (self.textAlignment == NSTextAlignmentRight)
21 | placeholderOrigin.x = rect.size.width - placeholderSize.width;
22 |
23 | [self.placeholder drawAtPoint:placeholderOrigin
24 | withAttributes:@{
25 | NSFontAttributeName :_placeholderFont
26 | }];
27 | }
28 | }
29 |
30 |
31 | - (CGRect)textRectForBounds:(CGRect)bounds
32 | {
33 | CGRect rect = [super textRectForBounds:bounds];
34 | rect.origin.x += _leftInset;
35 | rect.size.width -= _leftInset;
36 | rect.origin.y = floorf((self.bounds.size.height - rect.size.height) / 2.0f);
37 | return rect;
38 | }
39 |
40 | - (CGRect)editingRectForBounds:(CGRect)bounds
41 | {
42 | return CGRectOffset([self textRectForBounds:bounds], 0.0f, 0.5f + _editingRectOffset);
43 | }
44 |
45 | - (CGRect)placeholderRectForBounds:(CGRect)bounds
46 | {
47 | return [self textRectForBounds:bounds];
48 | }
49 |
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/WATokenFieldView/WATokenFieldViewStyleKit.m:
--------------------------------------------------------------------------------
1 | //
2 | // WATokenFieldViewStyleKit.m
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 | // Generated by PaintCode (www.paintcodeapp.com)
9 | //
10 |
11 | #import "WATokenFieldViewStyleKit.h"
12 |
13 |
14 | @implementation WATokenFieldViewStyleKit
15 |
16 | #pragma mark Cache
17 |
18 | static UIImage* _imageOfTokenBackgroundHighlighted = nil;
19 |
20 | #pragma mark Initialization
21 |
22 | + (void)initialize
23 | {
24 | }
25 |
26 | #pragma mark Drawing Methods
27 |
28 | + (void)drawTokenBackgroundHighlighted
29 | {
30 |
31 | //// Rectangle Drawing
32 | UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(2, 2, 36, 52) cornerRadius: 6];
33 | [UIColor.grayColor setFill];
34 | [rectanglePath fill];
35 | }
36 |
37 | #pragma mark Generated Images
38 |
39 | + (UIImage*)imageOfTokenBackgroundHighlighted
40 | {
41 | if (_imageOfTokenBackgroundHighlighted)
42 | return _imageOfTokenBackgroundHighlighted;
43 |
44 | UIGraphicsBeginImageContextWithOptions(CGSizeMake(40, 56), NO, 0.0f);
45 | [WATokenFieldViewStyleKit drawTokenBackgroundHighlighted];
46 |
47 | _imageOfTokenBackgroundHighlighted = [[UIGraphicsGetImageFromCurrentImageContext() resizableImageWithCapInsets: UIEdgeInsetsMake(9, 8, 9, 9) resizingMode: UIImageResizingModeStretch] imageWithRenderingMode: UIImageRenderingModeAlwaysTemplate];
48 | UIGraphicsEndImageContext();
49 |
50 | return _imageOfTokenBackgroundHighlighted;
51 | }
52 |
53 | #pragma mark Customization Infrastructure
54 |
55 | - (void)setTokenBackgroundHighlightedTargets: (NSArray*)tokenBackgroundHighlightedTargets
56 | {
57 | _tokenBackgroundHighlightedTargets = tokenBackgroundHighlightedTargets;
58 |
59 | for (id target in self.tokenBackgroundHighlightedTargets)
60 | [target setImage: WATokenFieldViewStyleKit.imageOfTokenBackgroundHighlighted];
61 | }
62 |
63 |
64 | @end
65 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 | - (void)applicationWillResignActive:(UIApplication *)application {
24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
26 | }
27 |
28 | - (void)applicationDidEnterBackground:(UIApplication *)application {
29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
31 | }
32 |
33 | - (void)applicationWillEnterForeground:(UIApplication *)application {
34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
35 | }
36 |
37 | - (void)applicationDidBecomeActive:(UIApplication *)application {
38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
39 | }
40 |
41 | - (void)applicationWillTerminate:(UIApplication *)application {
42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/WATokenFieldView/WATokenFieldView.h:
--------------------------------------------------------------------------------
1 | //
2 | // WATokenFieldView.h
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class WATokenFieldView;
12 |
13 | @protocol WATokenFieldViewDelegate
14 |
15 | - (void)tokenFieldView:(WATokenFieldView *)tokenFieldView didChangeHeight:(float)height;
16 | - (void)tokenFieldView:(WATokenFieldView *)tokenFieldView didChangeText:(NSString *)text;
17 | - (void)tokenFieldView:(WATokenFieldView *)tokenFieldView didChangeSearchStatus:(bool)searchIsActive byClearingTextField:(bool)byClearingTextField;
18 | - (void)tokenFieldView:(WATokenFieldView *)tokenFieldView didDeleteTokenWithId:(id)tokenId;
19 |
20 | @end
21 |
22 | @interface WATokenFieldView : UIView
23 |
24 | @property (nonatomic, weak) id delegate;
25 |
26 | @property (nonatomic, strong) UIScrollView *scrollView;
27 | @property (nonatomic, strong) NSString *placeholder;
28 |
29 | - (float)preferredHeight;
30 | - (void)scrollToTextField:(bool)animated;
31 |
32 | - (bool)searchIsActive;
33 | - (void)clearText;
34 | - (bool)hasFirstResponder;
35 |
36 | - (void)beginTransition:(NSTimeInterval)duration;
37 |
38 | - (void)addToken:(NSString *)title tokenId:(id)tokenId animated:(bool)animated;
39 | - (NSArray *)tokenIds;
40 | - (void)removeTokensAtIndexes:(NSIndexSet *)indexSet;
41 |
42 | -(instancetype)initWithTextColor:(UIColor*)paramTextColor
43 | placeholderColor:(UIColor*)paramPlaceholderColor
44 | separatorColor:(UIColor*)paramSeparatorColor
45 | tagHighlightColor:(UIColor*)paramTagHighlightColor
46 | textFieldFont:(UIFont*)paramTextFieldFont
47 | maxNumberOfLines:(int)paramMaxNumberOfLines;
48 |
49 | -(instancetype)initWithTextColor:(UIColor*)paramTextColor
50 | placeholderColor:(UIColor*)paramPlaceholderColor
51 | separatorColor:(UIColor*)paramSeparatorColor
52 | tagHighlightColor:(UIColor*)paramTagHighlightColor
53 | textFieldFont:(UIFont*)paramTextFieldFont
54 | lineHeight:(float)paramLineHeight
55 | linePadding:(float)paramLinePadding
56 | lineSpacing:(float)paramLineSpacing
57 | maxNumberOfLines:(int)paramMaxNumberOfLines;
58 |
59 | @end
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WATokenFieldView
2 | WATokenFieldView is custom view that let you add token view in a view while typing/searching. A similar view is used by facebook application on the UpdateStatus screen. The component originate from telegram and has been made reusable.
3 |
4 | 
5 |
6 | ## Requirements
7 | * Xcode 6 or higher
8 | * Apple LLVM compiler
9 | * iOS 8.0 or higher (May work on previous versions, i just didnt test on it, feel free to edit)
10 | * ARC
11 |
12 | ## Demo
13 |
14 | Open and run the WATokenFieldViewExample project in Xcode to see WATokenFieldView in action. The example show how to filter a table view and add tag of people you want selected.
15 |
16 | ## Installation
17 |
18 | ### CocoaPods
19 |
20 | Install CocoaPods if it is not installed yet:
21 |
22 | ``` bash
23 | $ [sudo] gem install cocoapods
24 | $ pod setup
25 | ```
26 |
27 | Add WATokenFieldView to Podfile:
28 |
29 | ``` bash
30 | pod 'WATokenFieldView'
31 | ```
32 |
33 | Call 'pod install':
34 |
35 | ``` bash
36 | pod install
37 | ```
38 |
39 | ### Manual install
40 |
41 | All you need to do is drop WATokenFieldView folder into your project and include headers.
42 |
43 | ## Example usage
44 |
45 | ``` objective-c
46 | WATokenFieldView _tokenFieldView = [[WATokenFieldView alloc]initWithTextColor:[UIColor blackColor]
47 | placeholderColor:[UIColor lightGrayColor]
48 | separatorColor:[UIColor lightGrayColor]
49 | tagHighlightColor:[UIColor blueColor]
50 | textFieldFont:[UIFont systemFontOfSize:15]
51 | maxNumberOfLines:2];
52 |
53 | _tokenFieldView.delegate = self;
54 | _tokenFieldView.placeholder = @"enter a name";
55 | ```
56 |
57 | ## Contact
58 |
59 | Wendy Abrantes
60 |
61 | - https://github.com/wendyabrantes
62 | - https://twitter.com/wendyabrantes
63 | - abranteswendy@gmail.com
64 |
65 | ## License
66 |
67 | The MIT License (MIT)
68 |
69 | Copyright (c) 2015 Wendy Abrantes
70 |
71 | Permission is hereby granted, free of charge, to any person obtaining a copy
72 | of this software and associated documentation files (the "Software"), to deal
73 | in the Software without restriction, including without limitation the rights
74 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
75 | copies of the Software, and to permit persons to whom the Software is
76 | furnished to do so, subject to the following conditions:
77 |
78 | The above copyright notice and this permission notice shall be included in all
79 | copies or substantial portions of the Software.
80 |
81 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
82 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
83 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
84 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
85 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
86 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
87 | SOFTWARE.
88 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/WACheckButton.m:
--------------------------------------------------------------------------------
1 | //
2 | // GICheckButton.m
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 |
9 | #import "WACheckButton.h"
10 |
11 | @implementation WACheckButton
12 |
13 | - (id)initWithFrame:(CGRect)frame
14 | {
15 | self = [super initWithFrame:frame];
16 | if (self != nil)
17 | {
18 | [self _commonInit];
19 | }
20 | return self;
21 | }
22 |
23 | - (void)_commonInit
24 | {
25 | self.exclusiveTouch = true;
26 |
27 | _checkView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 22, 22)];
28 | [self addSubview:_checkView];
29 | }
30 |
31 | - (void)setHighlighted:(BOOL)highlighted
32 | {
33 | [super setHighlighted:highlighted];
34 |
35 | if (highlighted)
36 | _checkView.transform = CGAffineTransformMakeScale(0.8f, 0.8f);
37 | }
38 |
39 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
40 | {
41 | _checkView.transform = CGAffineTransformIdentity;
42 |
43 | [super touchesCancelled:touches withEvent:event];
44 | }
45 |
46 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
47 | {
48 | UITouch *touch = [touches anyObject];
49 |
50 | if (!CGRectContainsPoint(self.bounds, [touch locationInView:self]))
51 | _checkView.transform = CGAffineTransformIdentity;
52 | else
53 | _checkView.transform = CGAffineTransformMakeScale(0.8f, 0.8f);
54 |
55 | [super touchesEnded:touches withEvent:event];
56 | }
57 |
58 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
59 | {
60 | UITouch *touch = [touches anyObject];
61 |
62 | if (!CGRectContainsPoint(self.bounds, [touch locationInView:self]))
63 | _checkView.transform = CGAffineTransformIdentity;
64 | else
65 | _checkView.transform = CGAffineTransformMakeScale(0.8f, 0.8f);
66 |
67 | [super touchesMoved:touches withEvent:event];
68 | }
69 |
70 | - (void)setChecked:(bool)checked animated:(bool)animated
71 | {
72 | _checkView.image = checked ? contactCellCheckedImage() : contactCellCheckImage();
73 |
74 | if (animated)
75 | {
76 | _checkView.transform = CGAffineTransformMakeScale(0.8f, 0.8f);
77 | if (checked)
78 | {
79 | [UIView animateWithDuration:0.12 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^
80 | {
81 | _checkView.transform = CGAffineTransformMakeScale(1.16, 1.16f);
82 | } completion:^(BOOL finished)
83 | {
84 | if (finished)
85 | {
86 | [UIView animateWithDuration:0.08 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^
87 | {
88 | _checkView.transform = CGAffineTransformIdentity;
89 | } completion:nil];
90 | }
91 | }];
92 | }
93 | else
94 | {
95 | [UIView animateWithDuration:0.16 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^
96 | {
97 | _checkView.transform = CGAffineTransformIdentity;
98 | } completion:nil];
99 | }
100 | }
101 | else
102 | {
103 | _checkView.transform = CGAffineTransformIdentity;
104 | }
105 | }
106 |
107 | static UIImage *contactCellCheckImage()
108 | {
109 | static UIImage *image = nil;
110 | if (image == nil)
111 | image = [UIImage imageNamed:@"ModernContactSelectionEmpty.png"];
112 | return image;
113 | }
114 |
115 | static UIImage *contactCellCheckedImage()
116 | {
117 | static UIImage *image = nil;
118 | if (image == nil)
119 | image = [UIImage imageNamed:@"ModernContactSelectionChecked.png"];
120 | return image;
121 | }
122 |
123 | @end
124 |
--------------------------------------------------------------------------------
/WATokenFieldView/WASearchTextField.m:
--------------------------------------------------------------------------------
1 | #import "WASearchTextField.h"
2 |
3 | @interface WASearchTextField ()
4 |
5 | @end
6 |
7 | @implementation WASearchTextField
8 |
9 | @synthesize customPlaceholderLabel = _customPlaceholderLabel;
10 |
11 | - (id)initWithFrame:(CGRect)frame
12 | {
13 | self = [super initWithFrame:frame];
14 | if (self != nil)
15 | {
16 | [self commonInit];
17 | }
18 | return self;
19 | }
20 |
21 | - (void)commonInit
22 | {
23 | _customPlaceholderLabel = [[UILabel alloc] init];
24 | _customPlaceholderLabel.backgroundColor = [UIColor clearColor];
25 | //_customPlaceholderLabel.font = [UIFont style2Bold];
26 | [_customPlaceholderLabel sizeToFit];
27 | _customPlaceholderLabel.userInteractionEnabled = false;
28 | //_customPlaceholderLabel.textColor = [GetInStyleKit grey1];
29 |
30 |
31 | [self setTextAlignment:NSTextAlignmentLeft];
32 | }
33 |
34 | - (void)setShowPlaceholder:(bool)showPlaceholder animated:(bool)animated
35 | {
36 | if (showPlaceholder != _customPlaceholderLabel.alpha > FLT_EPSILON)
37 | {
38 | if (animated)
39 | {
40 | [UIView animateWithDuration:0.2 animations:^
41 | {
42 | _customPlaceholderLabel.alpha = showPlaceholder ? 1.0f : 0.0f;
43 | }];
44 | }
45 | else
46 | _customPlaceholderLabel.alpha = showPlaceholder ? 1.0f : 0.0f;
47 | }
48 | }
49 |
50 | - (void)setText:(NSString *)text
51 | {
52 | [super setText:text];
53 |
54 | _customPlaceholderLabel.hidden = text.length != 0;
55 | }
56 |
57 | - (void)deleteBackward1
58 | {
59 | bool wasEmpty = self.text.length == 0;
60 |
61 | [super deleteBackward];
62 |
63 | if (wasEmpty){
64 | [self deleteLastBackward];
65 | }
66 | }
67 |
68 | - (void)deleteLastBackward
69 | {
70 | id delegate = self.delegate;
71 | if ([delegate respondsToSelector:@selector(textFieldDidHitLastBackspace)])
72 | [delegate performSelector:@selector(textFieldDidHitLastBackspace)];
73 | }
74 |
75 | - (BOOL)becomeFirstResponder
76 | {
77 | if ([super becomeFirstResponder])
78 | {
79 | id delegate = self.delegate;
80 | if ([delegate respondsToSelector:@selector(textFieldDidBecomeFirstResponder)])
81 | [delegate performSelector:@selector(textFieldDidBecomeFirstResponder)];
82 | return true;
83 | }
84 | return false;
85 | }
86 |
87 | - (BOOL)resignFirstResponder
88 | {
89 | if ([super resignFirstResponder])
90 | {
91 | id delegate = self.delegate;
92 | if ([delegate respondsToSelector:@selector(textFieldDidResignFirstResponder)])
93 | [delegate performSelector:@selector(textFieldDidResignFirstResponder)];
94 | return true;
95 | }
96 | return false;
97 | }
98 |
99 | - (BOOL)keyboardInputShouldDelete:(UITextField *)textField
100 | {
101 | BOOL shouldDelete = YES;
102 |
103 | if ([UITextField instancesRespondToSelector:_cmd])
104 | {
105 | bool wasEmpty = self.text.length == 0;
106 |
107 | BOOL (*keyboardInputShouldDelete)(id, SEL, UITextField *) = (BOOL (*)(id, SEL, UITextField *))[UITextField instanceMethodForSelector:_cmd];
108 |
109 | if (keyboardInputShouldDelete)
110 | shouldDelete = keyboardInputShouldDelete(self, _cmd, textField);
111 |
112 | if (wasEmpty)
113 | shouldDelete = false;
114 |
115 | if (wasEmpty){
116 | [self deleteLastBackward];
117 | }
118 | }
119 |
120 | return shouldDelete;
121 | }
122 |
123 | @end
124 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/WAFriendTableViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // InviteFriendTableViewCell.m
3 | // GetIn
4 | //
5 | // Created by Wendy Abrantes on 07/09/2015.
6 | // Copyright (c) 2015 nimbletank. All rights reserved.
7 | //
8 |
9 | #import "WAFriendTableViewCell.h"
10 |
11 | #import "WACheckButton.h"
12 |
13 | @implementation WAFriendTableViewCell
14 | {
15 | CALayer *dividerLayer;
16 |
17 | UILabel *firstNameLabel;
18 | UILabel *lastNameLabel;
19 |
20 | WACheckButton *checkButton;
21 |
22 | BOOL isConstraintSetup;
23 | }
24 |
25 | +(NSString*)reuseIdentifier
26 | {
27 | return @"WAFriendTableViewCell";
28 | }
29 |
30 | -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
31 | {
32 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
33 | if (self) {
34 | [self setupLayout];
35 | }
36 | return self;
37 | }
38 |
39 | -(void)prepareForReuse
40 | {
41 | [super prepareForReuse];
42 |
43 | firstNameLabel.text = nil;
44 | lastNameLabel.text = nil;
45 | }
46 |
47 | -(void)layoutSubviews
48 | {
49 | [self setupConstraint];
50 | [super layoutSubviews];
51 | }
52 |
53 |
54 | -(void)setupLayout
55 | {
56 | self.selectionStyle = UITableViewCellSelectionStyleNone;
57 |
58 | firstNameLabel = [UILabel new];
59 | lastNameLabel = [UILabel new];
60 | firstNameLabel.textColor = lastNameLabel.textColor = [UIColor blackColor];
61 |
62 | firstNameLabel.font = [UIFont systemFontOfSize:15];
63 | lastNameLabel.font = [UIFont systemFontOfSize:15];
64 |
65 | dividerLayer = [CALayer layer];
66 | dividerLayer.backgroundColor = [UIColor lightGrayColor].CGColor;
67 |
68 | checkButton = [[WACheckButton alloc] init];
69 | checkButton.userInteractionEnabled = NO;
70 |
71 |
72 |
73 | [self.contentView addSubview:firstNameLabel];
74 | [self.contentView addSubview:lastNameLabel];
75 | [self.contentView addSubview:checkButton];
76 |
77 | [self.contentView.layer addSublayer:dividerLayer];
78 | }
79 |
80 | -(void)setupConstraint
81 | {
82 | float paddingLeft = 15.0f;
83 |
84 | firstNameLabel.frame = CGRectMake(paddingLeft,
85 | self.frame.size.height/2 - (firstNameLabel.frame.size.height+2),
86 | 200,
87 | firstNameLabel.frame.size.height);
88 |
89 | lastNameLabel.frame = CGRectMake(paddingLeft,
90 | self.frame.size.height/2 + 2,
91 | 200,
92 | lastNameLabel.frame.size.height);
93 |
94 | dividerLayer.frame = CGRectMake(0,
95 | self.frame.size.height-1,
96 | self.frame.size.width,
97 | 1);
98 |
99 | checkButton.frame = CGRectMake(self.frame.size.width - 40,
100 | self.contentView.center.y - 10,
101 | 20,
102 | 20);
103 |
104 | [self setNeedsDisplay];
105 | }
106 |
107 | -(void)setHighlighted:(BOOL)highlighted
108 | {
109 |
110 | }
111 | -(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
112 | {
113 |
114 | }
115 |
116 | -(void)setSelected:(BOOL)selected animated:(BOOL)animated
117 | {
118 | [super setSelected:selected animated:animated];
119 | [checkButton setChecked:selected animated:YES];
120 | }
121 |
122 | -(void)updateWithFirstName:(NSString*)firstName
123 | lastName:(NSString*)lastName
124 | {
125 | firstNameLabel.text = [firstName uppercaseString];
126 | lastNameLabel.text = [lastName uppercaseString];
127 |
128 | [firstNameLabel sizeToFit];
129 | [lastNameLabel sizeToFit];
130 |
131 |
132 | }
133 |
134 |
135 |
136 |
137 |
138 | @end
139 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample.xcodeproj/xcuserdata/wenders.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
20 |
21 |
22 |
24 |
36 |
37 |
38 |
40 |
52 |
53 |
54 |
56 |
68 |
69 |
70 |
72 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/WATokenFieldView/WATokenView.m:
--------------------------------------------------------------------------------
1 | #import "WATokenView.h"
2 | #import "WATokenFieldViewStyleKit.h"
3 |
4 | static UIImage *tokenBackgroundImage()
5 | {
6 | static UIImage *image = nil;
7 | if (image == nil)
8 | {
9 | UIImage *rawImage = [UIImage imageNamed:@"TokenBackground.png"];
10 | image = [rawImage stretchableImageWithLeftCapWidth:(int)(rawImage.size.width / 2) topCapHeight:0];
11 | }
12 | return image;
13 | }
14 |
15 | @implementation WATokenView
16 | {
17 | UIFont *labelFont;
18 | UIColor *titleColor;
19 | UIColor *highlightedColor;
20 | }
21 |
22 | -(instancetype)initWithFrame:(CGRect)frame
23 | labelFont:(UIFont*)paramLabelFont
24 | textColor:(UIColor*)paramTextColor
25 | highlightedColor:(UIColor*)paramHighlightedColor
26 | {
27 | self = [super initWithFrame:frame];
28 | if (self != nil)
29 | {
30 | labelFont = paramLabelFont;
31 | titleColor = paramTextColor;
32 | highlightedColor = paramHighlightedColor;
33 |
34 | [self commonInit];
35 | }
36 | return self;
37 | }
38 |
39 | - (void)commonInit
40 | {
41 |
42 | [self setBackgroundImage:tokenBackgroundImage() forState:UIControlStateNormal];
43 | [self setBackgroundImage:[WATokenFieldViewStyleKit imageOfTokenBackgroundHighlighted]
44 | forState:UIControlStateHighlighted];
45 | [self setBackgroundImage:[WATokenFieldViewStyleKit imageOfTokenBackgroundHighlighted]
46 | forState:UIControlStateSelected];
47 | [self setBackgroundImage:[WATokenFieldViewStyleKit imageOfTokenBackgroundHighlighted]
48 | forState:UIControlStateHighlighted | UIControlStateSelected];
49 |
50 | self.tintColor = highlightedColor;
51 |
52 | self.titleLabel.font = labelFont;
53 | [self setTitleEdgeInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
54 |
55 | [self setTitleColor:titleColor forState:UIControlStateNormal];
56 | [self setTitleShadowColor:nil forState:UIControlStateNormal];
57 |
58 | [self setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
59 | [self setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
60 | [self setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted | UIControlStateSelected];
61 |
62 | [self addTarget:self action:@selector(buttonPressed) forControlEvents:UIControlEventTouchDown];
63 | }
64 |
65 | - (void)buttonPressed
66 | {
67 | [self becomeFirstResponder];
68 | }
69 |
70 | - (void)setLabel:(NSString *)label
71 | {
72 | _label = label;
73 |
74 | [self setTitle:label forState:UIControlStateNormal];
75 |
76 | _preferredWidth = [label sizeWithFont:self.titleLabel.font].width + 10;
77 | }
78 |
79 | - (float)preferredWidth
80 | {
81 | return MAX(_preferredWidth, 10);
82 | }
83 |
84 | #pragma mark -
85 |
86 | - (BOOL)becomeFirstResponder
87 | {
88 | if ([super becomeFirstResponder])
89 | {
90 | if ([self.superview.superview respondsToSelector:@selector(highlightToken:)])
91 | [self.superview.superview performSelector:@selector(highlightToken:) withObject:self];
92 | return true;
93 | }
94 |
95 | return false;
96 | }
97 |
98 | - (BOOL)resignFirstResponder
99 | {
100 | if ([super resignFirstResponder])
101 | {
102 | if ([self.superview.superview respondsToSelector:@selector(unhighlightToken:)])
103 | [self.superview.superview performSelector:@selector(unhighlightToken:) withObject:self];
104 | return true;
105 | }
106 |
107 | return false;
108 | }
109 |
110 | - (void)deleteBackward
111 | {
112 | if ([self.superview.superview respondsToSelector:@selector(deleteToken:)])
113 | [self.superview.superview performSelector:@selector(deleteToken:) withObject:self];
114 | }
115 |
116 | - (BOOL)hasText
117 | {
118 | return false;
119 | }
120 |
121 | - (void)insertText:(NSString *)__unused text
122 | {
123 | }
124 |
125 | - (BOOL)canBecomeFirstResponder
126 | {
127 | return true;
128 | }
129 |
130 | @end
131 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample.xcodeproj/xcuserdata/wenders.xcuserdatad/xcschemes/WATokenFieldView.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
75 |
77 |
83 |
84 |
85 |
86 |
87 |
88 |
94 |
96 |
102 |
103 |
104 |
105 |
107 |
108 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // WATokenFieldView
4 | //
5 | // Created by Wendy Abrantes on 12/09/2015.
6 | // Copyright (c) 2015 ABRANTES DIGITAL LTD. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 |
11 | #import "WAFriendTableViewCell.h"
12 |
13 | @interface ViewController ()
14 | {
15 | CGFloat tokenViewHeight;
16 | CGFloat minimumHeight;
17 | WATokenFieldView *_tokenFieldView;
18 | UITableView *_friendsTableView;
19 |
20 | NSArray *allFriendsData;
21 | NSArray *filteredFriendsData;
22 | NSArray *currentData;
23 |
24 | }
25 | @end
26 |
27 | @implementation ViewController
28 |
29 | - (void)viewDidLoad {
30 | [super viewDidLoad];
31 | // Do any additional setup after loading the view, typically from a nib.
32 |
33 | allFriendsData = @[
34 | @{
35 | @"id": @"435gdrg45",
36 | @"first_name":@"bessie",
37 | @"last_name":@"smith"
38 | },
39 | @{
40 | @"id": @"dvrt435",
41 | @"first_name":@"michelle",
42 | @"last_name":@"obama"
43 | },
44 | @{
45 | @"id": @"3juk78",
46 | @"first_name":@"serena",
47 | @"last_name":@"williams"
48 | },
49 | @{
50 | @"id": @"ghkio53",
51 | @"first_name":@"oprah",
52 | @"last_name":@"winfrey"
53 | },
54 | @{
55 | @"id": @"srthrb3",
56 | @"first_name":@"halle",
57 | @"last_name":@"berry"
58 | },
59 | @{
60 | @"id": @"fgnhjklu34",
61 | @"first_name":@"lolo",
62 | @"last_name":@"jones"
63 | },
64 | @{
65 | @"id": @"scq325",
66 | @"first_name":@"venus",
67 | @"last_name":@"williams"
68 | },
69 | @{
70 | @"id": @"dfbtkuy96",
71 | @"first_name":@"marcia",
72 | @"last_name":@"fudge"
73 | },
74 | @{
75 | @"id": @"luodf34",
76 | @"first_name":@"gween",
77 | @"last_name":@"moore"
78 | },
79 | @{
80 | @"id": @"bvnstuy",
81 | @"first_name":@"laura",
82 | @"last_name":@"richardson"
83 | },
84 | @{
85 | @"id": @"fbwrt47",
86 | @"first_name":@"barbara lee",
87 | @"last_name":@"winfrey"
88 | },
89 | @{
90 | @"id": @"hg0daf",
91 | @"first_name":@"toni",
92 | @"last_name":@"morrisson"
93 | },
94 | @{
95 | @"id": @"xvewftyr54",
96 | @"first_name":@"audrey",
97 | @"last_name":@"lorde"
98 | },
99 | @{
100 | @"id": @"bgfjyt3456",
101 | @"first_name":@"wanda",
102 | @"last_name":@"coleman"
103 | },
104 | @{
105 | @"id": @"gfnoi765543fv",
106 | @"first_name":@"alice",
107 | @"last_name":@"walker"
108 | },
109 | @{
110 | @"id": @"vbwoi765",
111 | @"first_name":@"rita",
112 | @"last_name":@"dove"
113 | },
114 | @{
115 | @"id": @"vfs423i8uyt",
116 | @"first_name":@"maya",
117 | @"last_name":@"angelou"
118 | },
119 | @{
120 | @"id": @"gfyti9765432fe",
121 | @"first_name":@"lisa",
122 | @"last_name":@"leslie"
123 | }];
124 |
125 | currentData = allFriendsData;
126 |
127 | minimumHeight = 50;
128 | tokenViewHeight = 100;
129 |
130 | _tokenFieldView = [[WATokenFieldView alloc]initWithTextColor:[UIColor blackColor]
131 | placeholderColor:[UIColor lightGrayColor]
132 | separatorColor:[UIColor lightGrayColor]
133 | tagHighlightColor:[UIColor blueColor]
134 | textFieldFont:[UIFont systemFontOfSize:15]
135 | maxNumberOfLines:2];
136 |
137 | _tokenFieldView.delegate = self;
138 | _tokenFieldView.placeholder = @"enter a name";
139 |
140 | _friendsTableView = [[UITableView alloc] init];
141 | _friendsTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
142 | _friendsTableView.dataSource = self;
143 | _friendsTableView.delegate = self;
144 | _friendsTableView.allowsMultipleSelection = YES;
145 | [_friendsTableView registerClass:[WAFriendTableViewCell class] forCellReuseIdentifier:[WAFriendTableViewCell reuseIdentifier]];
146 |
147 | [self.view addSubview:_tokenFieldView];
148 | [self.view addSubview:_friendsTableView];
149 |
150 | [_friendsTableView reloadData];
151 | }
152 |
153 | -(void)viewWillLayoutSubviews
154 | {
155 | [super viewWillLayoutSubviews];
156 | [self doLayout];
157 | }
158 |
159 | -(void)doLayout{
160 |
161 | _tokenFieldView.frame = CGRectMake(0, 20, CGRectGetWidth(self.view.frame), tokenViewHeight);
162 | _friendsTableView.frame = CGRectMake(0,
163 | CGRectGetMaxY(_tokenFieldView.frame) + 1,
164 | CGRectGetWidth(self.view.frame),
165 | CGRectGetHeight(self.view.frame) - CGRectGetMaxY(_tokenFieldView.frame));
166 | }
167 |
168 | - (void)didReceiveMemoryWarning {
169 | [super didReceiveMemoryWarning];
170 | // Dispose of any resources that can be recreated.
171 | }
172 |
173 |
174 | #pragma mark TABLE VIEW
175 |
176 | -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
177 | {
178 | WAFriendTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[WAFriendTableViewCell reuseIdentifier]];
179 |
180 | NSDictionary *friend = [currentData objectAtIndex:indexPath.row];
181 |
182 | if(!cell){
183 | cell = [[WAFriendTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[WAFriendTableViewCell reuseIdentifier]];
184 | }
185 |
186 | if([_tokenFieldView.tokenIds containsObject:friend[@"id"]]){
187 | [_friendsTableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
188 | }
189 |
190 | [cell updateWithFirstName:friend[@"first_name"]
191 | lastName:friend[@"last_name"]];
192 |
193 | return cell;
194 | }
195 |
196 | -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
197 | {
198 | return 100;
199 | }
200 |
201 | -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
202 | {
203 | return currentData.count;
204 | }
205 |
206 | -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
207 | {
208 | return 1;
209 | }
210 |
211 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
212 | {
213 | NSDictionary *friend = [currentData objectAtIndex:indexPath.row];
214 |
215 | //check doesnt exist in token views
216 | if(![[_tokenFieldView tokenIds] containsObject:friend[@"id"]]){
217 | [_tokenFieldView addToken:[NSString stringWithFormat:@"%@ %@", friend[@"first_name"], friend[@"last_name"]]
218 | tokenId:friend[@"id"]
219 | animated:YES];
220 | }
221 | }
222 |
223 | -(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
224 | {
225 | NSDictionary *friend = [currentData objectAtIndex:indexPath.row];
226 | if(friend){
227 | if([[_tokenFieldView tokenIds] indexOfObject:friend[@"id"]] != -1)
228 | {
229 | NSInteger index = [[_tokenFieldView tokenIds] indexOfObject:friend[@"id"]];
230 | [_tokenFieldView removeTokensAtIndexes:[[NSIndexSet alloc] initWithIndex:index]];
231 | }
232 | }
233 | }
234 |
235 | #pragma mark SCROLLVIEW DELEGATE
236 | - (void)clearFirstResponder:(UIView *)v
237 | {
238 | if (v == nil)
239 | return;
240 |
241 | for (UIView *view in v.subviews)
242 | {
243 | if ([view isFirstResponder])
244 | {
245 | [view resignFirstResponder];
246 | return;
247 | }
248 | [self clearFirstResponder:view];
249 | }
250 | }
251 |
252 | -(void)scrollViewDidScroll:(UIScrollView *)scrollView
253 | {
254 | if([_tokenFieldView hasFirstResponder]){
255 | [self clearFirstResponder:_tokenFieldView];
256 | }
257 | }
258 |
259 | #pragma mark TOKEN VIEW
260 |
261 |
262 | -(void)tokenFieldView:(WATokenFieldView *)tokenFieldView didChangeHeight:(float)height
263 | {
264 | if(height != tokenFieldView.frame.size.height)
265 | {
266 | tokenViewHeight = height;
267 | [UIView animateWithDuration:0.3
268 | animations:^{
269 | [self doLayout];
270 | }];
271 | }
272 | }
273 |
274 | -(void)tokenFieldView:(WATokenFieldView *)tokenFieldView didChangeSearchStatus:(bool)searchIsActive byClearingTextField:(bool)byClearingTextField
275 | {
276 | if(!searchIsActive){
277 | currentData = allFriendsData;
278 | [_friendsTableView reloadData];
279 | }
280 | }
281 |
282 | -(void)beginSearchWithName:(NSString*)paramName
283 | {
284 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(first_name CONTAINS[cd] %@) || (last_name CONTAINS[cd] %@)", paramName, paramName];
285 | filteredFriendsData = [allFriendsData filteredArrayUsingPredicate:predicate];
286 | currentData = filteredFriendsData;
287 | [_friendsTableView reloadData];
288 | }
289 |
290 | -(void)tokenFieldView:(WATokenFieldView *)tokenFieldView
291 | didChangeText:(NSString *)text
292 | {
293 | [self beginSearchWithName:text];
294 | }
295 |
296 | -(void)tokenFieldView:(WATokenFieldView *)tokenFieldView
297 | didDeleteTokenWithId:(id)tokenId
298 | {
299 | if (tokenFieldView == _tokenFieldView)
300 | {
301 | if ([tokenId isKindOfClass:[NSString class]])
302 | {
303 | NSMutableArray *selectedRows = [NSMutableArray arrayWithArray:_friendsTableView.indexPathsForSelectedRows];
304 | for(NSIndexPath *indexPath in selectedRows)
305 | {
306 | if([currentData objectAtIndex:indexPath.row][@"id"] == tokenId){
307 |
308 | [_friendsTableView deselectRowAtIndexPath:indexPath
309 | animated:NO];
310 | }
311 | }
312 | }
313 | }
314 | }
315 |
316 | @end
317 |
--------------------------------------------------------------------------------
/WATokenFieldViewExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | C522BFE11BA3F63A00D66DF9 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C522BFD71BA3F63A00D66DF9 /* AppDelegate.m */; };
11 | C522BFE21BA3F63A00D66DF9 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = C522BFD81BA3F63A00D66DF9 /* LaunchScreen.xib */; };
12 | C522BFE31BA3F63A00D66DF9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C522BFDA1BA3F63A00D66DF9 /* Main.storyboard */; };
13 | C522BFE41BA3F63A00D66DF9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C522BFDC1BA3F63A00D66DF9 /* Images.xcassets */; };
14 | C522BFE51BA3F63A00D66DF9 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C522BFDD1BA3F63A00D66DF9 /* Info.plist */; };
15 | C522BFE61BA3F63A00D66DF9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C522BFDE1BA3F63A00D66DF9 /* main.m */; };
16 | C522BFE71BA3F63A00D66DF9 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C522BFE01BA3F63A00D66DF9 /* ViewController.m */; };
17 | C55042541BA4022C0004C2EF /* WACheckButton.m in Sources */ = {isa = PBXBuildFile; fileRef = C55042511BA4022C0004C2EF /* WACheckButton.m */; };
18 | C55042551BA4022C0004C2EF /* WAFriendTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C55042531BA4022C0004C2EF /* WAFriendTableViewCell.m */; };
19 | C550425F1BA412F10004C2EF /* WASearchTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = C55042581BA412F10004C2EF /* WASearchTextField.m */; };
20 | C55042601BA412F10004C2EF /* WATextField.m in Sources */ = {isa = PBXBuildFile; fileRef = C550425A1BA412F10004C2EF /* WATextField.m */; };
21 | C55042611BA412F10004C2EF /* WATokenFieldView.m in Sources */ = {isa = PBXBuildFile; fileRef = C550425C1BA412F10004C2EF /* WATokenFieldView.m */; };
22 | C55042621BA412F10004C2EF /* WATokenView.m in Sources */ = {isa = PBXBuildFile; fileRef = C550425E1BA412F10004C2EF /* WATokenView.m */; };
23 | C55042651BA413AD0004C2EF /* WATokenFieldViewStyleKit.m in Sources */ = {isa = PBXBuildFile; fileRef = C55042641BA413AD0004C2EF /* WATokenFieldViewStyleKit.m */; };
24 | /* End PBXBuildFile section */
25 |
26 | /* Begin PBXFileReference section */
27 | C522BFAC1BA3F4C300D66DF9 /* WATokenFieldViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WATokenFieldViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
28 | C522BFCA1BA3F4C300D66DF9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
29 | C522BFCB1BA3F4C300D66DF9 /* WATokenFieldViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WATokenFieldViewTests.m; sourceTree = ""; };
30 | C522BFD61BA3F63A00D66DF9 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
31 | C522BFD71BA3F63A00D66DF9 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
32 | C522BFD91BA3F63A00D66DF9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
33 | C522BFDB1BA3F63A00D66DF9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
34 | C522BFDC1BA3F63A00D66DF9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
35 | C522BFDD1BA3F63A00D66DF9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
36 | C522BFDE1BA3F63A00D66DF9 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
37 | C522BFDF1BA3F63A00D66DF9 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
38 | C522BFE01BA3F63A00D66DF9 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
39 | C55042501BA4022C0004C2EF /* WACheckButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WACheckButton.h; sourceTree = ""; };
40 | C55042511BA4022C0004C2EF /* WACheckButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WACheckButton.m; sourceTree = ""; };
41 | C55042521BA4022C0004C2EF /* WAFriendTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WAFriendTableViewCell.h; sourceTree = ""; };
42 | C55042531BA4022C0004C2EF /* WAFriendTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WAFriendTableViewCell.m; sourceTree = ""; };
43 | C55042571BA412F10004C2EF /* WASearchTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WASearchTextField.h; sourceTree = ""; };
44 | C55042581BA412F10004C2EF /* WASearchTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WASearchTextField.m; sourceTree = ""; };
45 | C55042591BA412F10004C2EF /* WATextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WATextField.h; sourceTree = ""; };
46 | C550425A1BA412F10004C2EF /* WATextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WATextField.m; sourceTree = ""; };
47 | C550425B1BA412F10004C2EF /* WATokenFieldView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WATokenFieldView.h; sourceTree = ""; };
48 | C550425C1BA412F10004C2EF /* WATokenFieldView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WATokenFieldView.m; sourceTree = ""; };
49 | C550425D1BA412F10004C2EF /* WATokenView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WATokenView.h; sourceTree = ""; };
50 | C550425E1BA412F10004C2EF /* WATokenView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WATokenView.m; sourceTree = ""; };
51 | C55042631BA413AD0004C2EF /* WATokenFieldViewStyleKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WATokenFieldViewStyleKit.h; sourceTree = ""; };
52 | C55042641BA413AD0004C2EF /* WATokenFieldViewStyleKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WATokenFieldViewStyleKit.m; sourceTree = ""; };
53 | /* End PBXFileReference section */
54 |
55 | /* Begin PBXFrameworksBuildPhase section */
56 | C522BFA91BA3F4C300D66DF9 /* Frameworks */ = {
57 | isa = PBXFrameworksBuildPhase;
58 | buildActionMask = 2147483647;
59 | files = (
60 | );
61 | runOnlyForDeploymentPostprocessing = 0;
62 | };
63 | /* End PBXFrameworksBuildPhase section */
64 |
65 | /* Begin PBXGroup section */
66 | C522BFA31BA3F4C300D66DF9 = {
67 | isa = PBXGroup;
68 | children = (
69 | C522BFD51BA3F63A00D66DF9 /* WATokenFieldViewExample */,
70 | C522BFC81BA3F4C300D66DF9 /* WATokenFieldViewTests */,
71 | C522BFAD1BA3F4C300D66DF9 /* Products */,
72 | );
73 | sourceTree = "";
74 | };
75 | C522BFAD1BA3F4C300D66DF9 /* Products */ = {
76 | isa = PBXGroup;
77 | children = (
78 | C522BFAC1BA3F4C300D66DF9 /* WATokenFieldViewExample.app */,
79 | );
80 | name = Products;
81 | sourceTree = "";
82 | };
83 | C522BFC81BA3F4C300D66DF9 /* WATokenFieldViewTests */ = {
84 | isa = PBXGroup;
85 | children = (
86 | C522BFCB1BA3F4C300D66DF9 /* WATokenFieldViewTests.m */,
87 | C522BFC91BA3F4C300D66DF9 /* Supporting Files */,
88 | );
89 | path = WATokenFieldViewTests;
90 | sourceTree = "";
91 | };
92 | C522BFC91BA3F4C300D66DF9 /* Supporting Files */ = {
93 | isa = PBXGroup;
94 | children = (
95 | C522BFCA1BA3F4C300D66DF9 /* Info.plist */,
96 | );
97 | name = "Supporting Files";
98 | sourceTree = "";
99 | };
100 | C522BFD51BA3F63A00D66DF9 /* WATokenFieldViewExample */ = {
101 | isa = PBXGroup;
102 | children = (
103 | C55042561BA412F10004C2EF /* WATokenFieldView */,
104 | C522BFD61BA3F63A00D66DF9 /* AppDelegate.h */,
105 | C522BFD71BA3F63A00D66DF9 /* AppDelegate.m */,
106 | C522BFD81BA3F63A00D66DF9 /* LaunchScreen.xib */,
107 | C522BFDA1BA3F63A00D66DF9 /* Main.storyboard */,
108 | C522BFDC1BA3F63A00D66DF9 /* Images.xcassets */,
109 | C522BFDD1BA3F63A00D66DF9 /* Info.plist */,
110 | C522BFDE1BA3F63A00D66DF9 /* main.m */,
111 | C522BFDF1BA3F63A00D66DF9 /* ViewController.h */,
112 | C55042501BA4022C0004C2EF /* WACheckButton.h */,
113 | C55042511BA4022C0004C2EF /* WACheckButton.m */,
114 | C55042521BA4022C0004C2EF /* WAFriendTableViewCell.h */,
115 | C55042531BA4022C0004C2EF /* WAFriendTableViewCell.m */,
116 | C522BFE01BA3F63A00D66DF9 /* ViewController.m */,
117 | );
118 | path = WATokenFieldViewExample;
119 | sourceTree = "";
120 | };
121 | C55042561BA412F10004C2EF /* WATokenFieldView */ = {
122 | isa = PBXGroup;
123 | children = (
124 | C55042631BA413AD0004C2EF /* WATokenFieldViewStyleKit.h */,
125 | C55042641BA413AD0004C2EF /* WATokenFieldViewStyleKit.m */,
126 | C55042571BA412F10004C2EF /* WASearchTextField.h */,
127 | C55042581BA412F10004C2EF /* WASearchTextField.m */,
128 | C55042591BA412F10004C2EF /* WATextField.h */,
129 | C550425A1BA412F10004C2EF /* WATextField.m */,
130 | C550425B1BA412F10004C2EF /* WATokenFieldView.h */,
131 | C550425C1BA412F10004C2EF /* WATokenFieldView.m */,
132 | C550425D1BA412F10004C2EF /* WATokenView.h */,
133 | C550425E1BA412F10004C2EF /* WATokenView.m */,
134 | );
135 | path = WATokenFieldView;
136 | sourceTree = SOURCE_ROOT;
137 | };
138 | /* End PBXGroup section */
139 |
140 | /* Begin PBXNativeTarget section */
141 | C522BFAB1BA3F4C300D66DF9 /* WATokenFieldViewExample */ = {
142 | isa = PBXNativeTarget;
143 | buildConfigurationList = C522BFCF1BA3F4C300D66DF9 /* Build configuration list for PBXNativeTarget "WATokenFieldViewExample" */;
144 | buildPhases = (
145 | C522BFA81BA3F4C300D66DF9 /* Sources */,
146 | C522BFA91BA3F4C300D66DF9 /* Frameworks */,
147 | C522BFAA1BA3F4C300D66DF9 /* Resources */,
148 | );
149 | buildRules = (
150 | );
151 | dependencies = (
152 | );
153 | name = WATokenFieldViewExample;
154 | productName = WATokenFieldView;
155 | productReference = C522BFAC1BA3F4C300D66DF9 /* WATokenFieldViewExample.app */;
156 | productType = "com.apple.product-type.application";
157 | };
158 | /* End PBXNativeTarget section */
159 |
160 | /* Begin PBXProject section */
161 | C522BFA41BA3F4C300D66DF9 /* Project object */ = {
162 | isa = PBXProject;
163 | attributes = {
164 | LastUpgradeCheck = 0640;
165 | ORGANIZATIONNAME = "ABRANTES DIGITAL LTD";
166 | TargetAttributes = {
167 | C522BFAB1BA3F4C300D66DF9 = {
168 | CreatedOnToolsVersion = 6.4;
169 | DevelopmentTeam = GGL87HT5RK;
170 | };
171 | };
172 | };
173 | buildConfigurationList = C522BFA71BA3F4C300D66DF9 /* Build configuration list for PBXProject "WATokenFieldViewExample" */;
174 | compatibilityVersion = "Xcode 3.2";
175 | developmentRegion = English;
176 | hasScannedForEncodings = 0;
177 | knownRegions = (
178 | en,
179 | Base,
180 | );
181 | mainGroup = C522BFA31BA3F4C300D66DF9;
182 | productRefGroup = C522BFAD1BA3F4C300D66DF9 /* Products */;
183 | projectDirPath = "";
184 | projectRoot = "";
185 | targets = (
186 | C522BFAB1BA3F4C300D66DF9 /* WATokenFieldViewExample */,
187 | );
188 | };
189 | /* End PBXProject section */
190 |
191 | /* Begin PBXResourcesBuildPhase section */
192 | C522BFAA1BA3F4C300D66DF9 /* Resources */ = {
193 | isa = PBXResourcesBuildPhase;
194 | buildActionMask = 2147483647;
195 | files = (
196 | C522BFE51BA3F63A00D66DF9 /* Info.plist in Resources */,
197 | C522BFE41BA3F63A00D66DF9 /* Images.xcassets in Resources */,
198 | C522BFE21BA3F63A00D66DF9 /* LaunchScreen.xib in Resources */,
199 | C522BFE31BA3F63A00D66DF9 /* Main.storyboard in Resources */,
200 | );
201 | runOnlyForDeploymentPostprocessing = 0;
202 | };
203 | /* End PBXResourcesBuildPhase section */
204 |
205 | /* Begin PBXSourcesBuildPhase section */
206 | C522BFA81BA3F4C300D66DF9 /* Sources */ = {
207 | isa = PBXSourcesBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | C55042621BA412F10004C2EF /* WATokenView.m in Sources */,
211 | C522BFE71BA3F63A00D66DF9 /* ViewController.m in Sources */,
212 | C522BFE61BA3F63A00D66DF9 /* main.m in Sources */,
213 | C55042541BA4022C0004C2EF /* WACheckButton.m in Sources */,
214 | C55042601BA412F10004C2EF /* WATextField.m in Sources */,
215 | C55042551BA4022C0004C2EF /* WAFriendTableViewCell.m in Sources */,
216 | C55042611BA412F10004C2EF /* WATokenFieldView.m in Sources */,
217 | C522BFE11BA3F63A00D66DF9 /* AppDelegate.m in Sources */,
218 | C55042651BA413AD0004C2EF /* WATokenFieldViewStyleKit.m in Sources */,
219 | C550425F1BA412F10004C2EF /* WASearchTextField.m in Sources */,
220 | );
221 | runOnlyForDeploymentPostprocessing = 0;
222 | };
223 | /* End PBXSourcesBuildPhase section */
224 |
225 | /* Begin PBXVariantGroup section */
226 | C522BFD81BA3F63A00D66DF9 /* LaunchScreen.xib */ = {
227 | isa = PBXVariantGroup;
228 | children = (
229 | C522BFD91BA3F63A00D66DF9 /* Base */,
230 | );
231 | name = LaunchScreen.xib;
232 | sourceTree = "";
233 | };
234 | C522BFDA1BA3F63A00D66DF9 /* Main.storyboard */ = {
235 | isa = PBXVariantGroup;
236 | children = (
237 | C522BFDB1BA3F63A00D66DF9 /* Base */,
238 | );
239 | name = Main.storyboard;
240 | sourceTree = "";
241 | };
242 | /* End PBXVariantGroup section */
243 |
244 | /* Begin XCBuildConfiguration section */
245 | C522BFCD1BA3F4C300D66DF9 /* Debug */ = {
246 | isa = XCBuildConfiguration;
247 | buildSettings = {
248 | ALWAYS_SEARCH_USER_PATHS = NO;
249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
250 | CLANG_CXX_LIBRARY = "libc++";
251 | CLANG_ENABLE_MODULES = YES;
252 | CLANG_ENABLE_OBJC_ARC = YES;
253 | CLANG_WARN_BOOL_CONVERSION = YES;
254 | CLANG_WARN_CONSTANT_CONVERSION = YES;
255 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
256 | CLANG_WARN_EMPTY_BODY = YES;
257 | CLANG_WARN_ENUM_CONVERSION = YES;
258 | CLANG_WARN_INT_CONVERSION = YES;
259 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
260 | CLANG_WARN_UNREACHABLE_CODE = YES;
261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
263 | COPY_PHASE_STRIP = NO;
264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
265 | ENABLE_STRICT_OBJC_MSGSEND = YES;
266 | GCC_C_LANGUAGE_STANDARD = gnu99;
267 | GCC_DYNAMIC_NO_PIC = NO;
268 | GCC_NO_COMMON_BLOCKS = YES;
269 | GCC_OPTIMIZATION_LEVEL = 0;
270 | GCC_PREPROCESSOR_DEFINITIONS = (
271 | "DEBUG=1",
272 | "$(inherited)",
273 | );
274 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
275 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
276 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
277 | GCC_WARN_UNDECLARED_SELECTOR = YES;
278 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
279 | GCC_WARN_UNUSED_FUNCTION = YES;
280 | GCC_WARN_UNUSED_VARIABLE = YES;
281 | IPHONEOS_DEPLOYMENT_TARGET = 8.4;
282 | MTL_ENABLE_DEBUG_INFO = YES;
283 | ONLY_ACTIVE_ARCH = YES;
284 | SDKROOT = iphoneos;
285 | TARGETED_DEVICE_FAMILY = "1,2";
286 | };
287 | name = Debug;
288 | };
289 | C522BFCE1BA3F4C300D66DF9 /* Release */ = {
290 | isa = XCBuildConfiguration;
291 | buildSettings = {
292 | ALWAYS_SEARCH_USER_PATHS = NO;
293 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
294 | CLANG_CXX_LIBRARY = "libc++";
295 | CLANG_ENABLE_MODULES = YES;
296 | CLANG_ENABLE_OBJC_ARC = YES;
297 | CLANG_WARN_BOOL_CONVERSION = YES;
298 | CLANG_WARN_CONSTANT_CONVERSION = YES;
299 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
300 | CLANG_WARN_EMPTY_BODY = YES;
301 | CLANG_WARN_ENUM_CONVERSION = YES;
302 | CLANG_WARN_INT_CONVERSION = YES;
303 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
304 | CLANG_WARN_UNREACHABLE_CODE = YES;
305 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
306 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
307 | COPY_PHASE_STRIP = NO;
308 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
309 | ENABLE_NS_ASSERTIONS = NO;
310 | ENABLE_STRICT_OBJC_MSGSEND = YES;
311 | GCC_C_LANGUAGE_STANDARD = gnu99;
312 | GCC_NO_COMMON_BLOCKS = YES;
313 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
314 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
315 | GCC_WARN_UNDECLARED_SELECTOR = YES;
316 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
317 | GCC_WARN_UNUSED_FUNCTION = YES;
318 | GCC_WARN_UNUSED_VARIABLE = YES;
319 | IPHONEOS_DEPLOYMENT_TARGET = 8.4;
320 | MTL_ENABLE_DEBUG_INFO = NO;
321 | SDKROOT = iphoneos;
322 | TARGETED_DEVICE_FAMILY = "1,2";
323 | VALIDATE_PRODUCT = YES;
324 | };
325 | name = Release;
326 | };
327 | C522BFD01BA3F4C300D66DF9 /* Debug */ = {
328 | isa = XCBuildConfiguration;
329 | buildSettings = {
330 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
331 | CODE_SIGN_IDENTITY = "iPhone Developer";
332 | INFOPLIST_FILE = "$(SRCROOT)/WATokenFieldViewExample/Info.plist";
333 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
334 | PRODUCT_NAME = WATokenFieldViewExample;
335 | };
336 | name = Debug;
337 | };
338 | C522BFD11BA3F4C300D66DF9 /* Release */ = {
339 | isa = XCBuildConfiguration;
340 | buildSettings = {
341 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
342 | CODE_SIGN_IDENTITY = "iPhone Developer";
343 | INFOPLIST_FILE = "$(SRCROOT)/WATokenFieldViewExample/Info.plist";
344 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
345 | PRODUCT_NAME = WATokenFieldViewExample;
346 | };
347 | name = Release;
348 | };
349 | /* End XCBuildConfiguration section */
350 |
351 | /* Begin XCConfigurationList section */
352 | C522BFA71BA3F4C300D66DF9 /* Build configuration list for PBXProject "WATokenFieldViewExample" */ = {
353 | isa = XCConfigurationList;
354 | buildConfigurations = (
355 | C522BFCD1BA3F4C300D66DF9 /* Debug */,
356 | C522BFCE1BA3F4C300D66DF9 /* Release */,
357 | );
358 | defaultConfigurationIsVisible = 0;
359 | defaultConfigurationName = Release;
360 | };
361 | C522BFCF1BA3F4C300D66DF9 /* Build configuration list for PBXNativeTarget "WATokenFieldViewExample" */ = {
362 | isa = XCConfigurationList;
363 | buildConfigurations = (
364 | C522BFD01BA3F4C300D66DF9 /* Debug */,
365 | C522BFD11BA3F4C300D66DF9 /* Release */,
366 | );
367 | defaultConfigurationIsVisible = 0;
368 | defaultConfigurationName = Release;
369 | };
370 | /* End XCConfigurationList section */
371 | };
372 | rootObject = C522BFA41BA3F4C300D66DF9 /* Project object */;
373 | }
374 |
--------------------------------------------------------------------------------
/WATokenFieldView/WATokenFieldView.m:
--------------------------------------------------------------------------------
1 | #import "WATokenFieldView.h"
2 |
3 | #import "WATokenView.h"
4 | #import "WASearchTextField.h"
5 |
6 | #import
7 |
8 |
9 | @interface WATokenFieldScrollView : UIScrollView
10 |
11 | @end
12 |
13 | @implementation WATokenFieldScrollView
14 |
15 | - (BOOL)touchesShouldCancelInContentView:(UIView *)__unused view
16 | {
17 | return true;
18 | }
19 |
20 | - (void)scrollRectToVisible:(CGRect)__unused rect animated:(BOOL)__unused animated
21 | {
22 | }
23 |
24 | @end
25 |
26 | @interface WATokenFieldView ()
27 |
28 | @property (nonatomic, strong) NSMutableDictionary *tokenAnimations;
29 |
30 | @property (nonatomic, strong) NSMutableArray *tokenList;
31 | @property (nonatomic, strong) WASearchTextField *textField;
32 |
33 | @property (nonatomic) int currentNumberOfLines;
34 |
35 | @property (nonatomic, strong) UIView *shadowView;
36 |
37 | @property (nonatomic, strong) UIColor *separatorColor;
38 | @property (nonatomic, strong) UIColor *placeholderColor;
39 | @property (nonatomic, strong) UIColor *textColor;
40 | @property (nonatomic, strong) UIFont *textFieldFont;
41 | @property (nonatomic, strong) UIColor *tagHighlightColor;
42 |
43 | @property (nonatomic) float lineHeight;
44 | @property (nonatomic) float linePadding;
45 | @property (nonatomic) float lineSpacing;
46 | @property (nonatomic) int maxNumberOfLines;
47 |
48 | @end
49 |
50 | @implementation WATokenFieldView
51 |
52 |
53 | -(instancetype)initWithTextColor:(UIColor*)paramTextColor
54 | placeholderColor:(UIColor*)paramPlaceholderColor
55 | separatorColor:(UIColor*)paramSeparatorColor
56 | tagHighlightColor:(UIColor*)paramTagHighlightColor
57 | textFieldFont:(UIFont*)paramTextFieldFont
58 | lineHeight:(float)paramLineHeight
59 | linePadding:(float)paramLinePadding
60 | lineSpacing:(float)paramLineSpacing
61 | maxNumberOfLines:(int)paramMaxNumberOfLines
62 | {
63 | self = [super init];
64 | if(self){
65 |
66 | _textColor = paramTextColor;
67 | _separatorColor = paramSeparatorColor;
68 | _placeholderColor = paramPlaceholderColor;
69 | _textFieldFont = paramTextFieldFont;
70 | _tagHighlightColor = paramTagHighlightColor;
71 | _maxNumberOfLines = paramMaxNumberOfLines;
72 | _lineHeight = paramLineHeight;
73 | _linePadding = paramLinePadding;
74 | _lineSpacing = paramLineSpacing;
75 |
76 | [self commonInit];
77 |
78 | }
79 | return self;
80 | }
81 |
82 | -(instancetype)initWithTextColor:(UIColor*)paramTextColor
83 | placeholderColor:(UIColor*)paramPlaceholderColor
84 | separatorColor:(UIColor*)paramSeparatorColor
85 | tagHighlightColor:(UIColor*)paramTagHighlightColor
86 | textFieldFont:(UIFont*)paramTextFieldFont
87 | maxNumberOfLines:(int)paramMaxNumberOfLines
88 | {
89 | self = [super init];
90 | if(self){
91 |
92 | _textColor = paramTextColor;
93 | _separatorColor = paramSeparatorColor;
94 | _placeholderColor = paramPlaceholderColor;
95 | _textFieldFont = paramTextFieldFont;
96 | _tagHighlightColor = paramTagHighlightColor;
97 | _maxNumberOfLines = paramMaxNumberOfLines;
98 |
99 | [self commonInit];
100 |
101 | }
102 | return self;
103 | }
104 |
105 | - (id)initWithFrame:(CGRect)frame
106 | {
107 | self = [super initWithFrame:frame];
108 | if (self != nil)
109 | {
110 | _lineHeight = 16;
111 | _linePadding = 12;
112 | _lineSpacing = 10;
113 | _maxNumberOfLines = 2;
114 |
115 | _currentNumberOfLines = 1;
116 | [self commonInit];
117 | }
118 | return self;
119 | }
120 |
121 | - (void)commonInit
122 | {
123 |
124 | //default color
125 | if(!_separatorColor)
126 | {
127 | _separatorColor = [UIColor grayColor];
128 | }
129 | if(!_textColor)
130 | {
131 | _textColor = [UIColor blackColor];
132 | }
133 | if(!_textFieldFont)
134 | {
135 | _textFieldFont = [UIFont systemFontOfSize:16];
136 | }
137 | if(!_tagHighlightColor)
138 | {
139 | _tagHighlightColor = [UIColor blueColor];
140 | }
141 | if(!_placeholderColor)
142 | {
143 | _placeholderColor = [UIColor lightGrayColor];
144 | }
145 |
146 | _shadowView = [[UIView alloc] init];
147 | CGFloat separatorHeight = 0.5f;
148 |
149 | _shadowView.frame = CGRectMake(0, self.frame.size.height, self.frame.size.width, separatorHeight);
150 | _shadowView.backgroundColor = _separatorColor;
151 | _shadowView.layer.zPosition = 1;
152 | [self addSubview:_shadowView];
153 |
154 | self.clipsToBounds = false;
155 | self.backgroundColor = [UIColor whiteColor];
156 |
157 | _scrollView = [[WATokenFieldScrollView alloc] initWithFrame:self.bounds];
158 | _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
159 | _scrollView.delaysContentTouches = true;
160 | _scrollView.canCancelContentTouches = true;
161 | _scrollView.scrollsToTop = false;
162 | _scrollView.backgroundColor = [UIColor whiteColor];
163 | _scrollView.opaque = true;
164 | [self addSubview:_scrollView];
165 |
166 | UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapRecognized:)];
167 | tapRecognizer.cancelsTouchesInView = false;
168 | [_scrollView addGestureRecognizer:tapRecognizer];
169 |
170 | _textField = [[WASearchTextField alloc] initWithFrame:CGRectMake(0, 0, 10, 42)];
171 | _textField.text = @"";
172 | _textField.delegate = self;
173 | _textField.autocorrectionType = UITextAutocorrectionTypeNo;
174 | _textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
175 | _textField.backgroundColor = [UIColor whiteColor];
176 | _textField.font = _textFieldFont;
177 | _textField.textColor = _textColor;
178 | _textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
179 | [_scrollView addSubview:_textField];
180 |
181 | _textField.customPlaceholderLabel.text = _placeholder;
182 | _textField.customPlaceholderLabel.font = _textFieldFont;
183 | _textField.customPlaceholderLabel.textColor = _placeholderColor;
184 | [_textField.customPlaceholderLabel sizeToFit];
185 | _textField.customPlaceholderLabel.frame = CGRectOffset(_textField.customPlaceholderLabel.frame, 9+6, _linePadding+4);
186 |
187 | [_scrollView addSubview:_textField.customPlaceholderLabel];
188 |
189 | _tokenList = [[NSMutableArray alloc] init];
190 | }
191 |
192 | - (void)setPlaceholder:(NSString *)placeholder
193 | {
194 | _placeholder = placeholder;
195 | _textField.customPlaceholderLabel.text = _placeholder;
196 | [_textField.customPlaceholderLabel sizeToFit];
197 | }
198 |
199 | - (void)addToken:(NSString *)title tokenId:(id)tokenId animated:(bool)animated
200 | {
201 | WATokenView *tokenView = [[WATokenView alloc] initWithFrame:CGRectMake(0, 0, 20, 28)
202 | labelFont:_textFieldFont
203 | textColor:_textColor
204 | highlightedColor:_tagHighlightColor];
205 |
206 | tokenView.label = title;
207 | tokenView.tokenId = tokenId;
208 | [_tokenList addObject:tokenView];
209 | [_scrollView addSubview:tokenView];
210 |
211 | if (animated)
212 | {
213 | if (_tokenAnimations == nil)
214 | _tokenAnimations = [[NSMutableDictionary alloc] init];
215 |
216 | if (tokenId != nil)
217 | [_tokenAnimations setObject:[[NSNumber alloc] initWithInt:1] forKey:tokenId];
218 | }
219 |
220 | [_textField setShowPlaceholder:false animated:_textField.text.length == 0];
221 | [self updateCounter];
222 |
223 | [self setNeedsLayout];
224 | }
225 |
226 | - (void)updateCounter
227 | {
228 | }
229 |
230 | - (NSArray *)tokenIds
231 | {
232 | NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:_tokenList.count];
233 |
234 | for (WATokenView *tokenView in _tokenList)
235 | {
236 | if (tokenView.tokenId != nil)
237 | [array addObject:tokenView.tokenId];
238 | }
239 |
240 | return array;
241 | }
242 |
243 | - (void)removeTokensAtIndexes:(NSIndexSet *)indexSet
244 | {
245 | NSUInteger lastCount = _tokenList.count;
246 |
247 | [indexSet enumerateIndexesUsingBlock:^(NSUInteger index, __unused BOOL *stop)
248 | {
249 | WATokenView *tokenView = [_tokenList objectAtIndex:index];
250 | if ([tokenView isFirstResponder])
251 | [tokenView resignFirstResponder];
252 |
253 | [UIView animateWithDuration:0.2 animations:^
254 | {
255 | tokenView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
256 | tokenView.alpha = 0.0f;
257 | } completion:^(__unused BOOL finished)
258 | {
259 | [tokenView removeFromSuperview];
260 | }];
261 | }];
262 |
263 | [_tokenList removeObjectsAtIndexes:indexSet];
264 |
265 | if (_tokenAnimations == nil)
266 | _tokenAnimations = [[NSMutableDictionary alloc] init];
267 |
268 | [self setNeedsLayout];
269 |
270 | if (_tokenList.count == 0)
271 | [_textField setShowPlaceholder:true animated:lastCount != _tokenList.count];
272 | [self updateCounter];
273 | }
274 |
275 | - (float)preferredHeight
276 | {
277 | int visibleNumberOfLines = MIN(MAX(1, _currentNumberOfLines), _maxNumberOfLines);
278 | return _lineHeight * visibleNumberOfLines + MAX(0, visibleNumberOfLines - 1) * _lineSpacing + _linePadding * 2;
279 | }
280 |
281 | - (void)setFrame:(CGRect)frame
282 | {
283 | [super setFrame:frame];
284 |
285 | CGFloat separatorHeight = 0.5f;
286 | _shadowView.frame = CGRectMake(0.0f, frame.size.height, frame.size.width, separatorHeight);
287 | }
288 |
289 | - (void)layoutSubviews
290 | {
291 | [super layoutSubviews];
292 |
293 | [self doLayout:_tokenAnimations != nil];
294 |
295 | if (_tokenAnimations != nil)
296 | {
297 | for (WATokenView *tokenView in _tokenList)
298 | {
299 | if (tokenView.tokenId == nil)
300 | continue;
301 |
302 | NSNumber *nAnimation = [_tokenAnimations objectForKey:tokenView.tokenId];
303 | if (nAnimation != nil)
304 | {
305 | tokenView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
306 | tokenView.alpha = 0.0f;
307 | }
308 | }
309 |
310 | [UIView animateWithDuration:0.2 animations:^
311 | {
312 | for (WATokenView *tokenView in _tokenList)
313 | {
314 | if (tokenView.tokenId == nil)
315 | continue;
316 |
317 | NSNumber *nAnimation = [_tokenAnimations objectForKey:tokenView.tokenId];
318 | if (nAnimation != nil)
319 | {
320 | tokenView.transform = CGAffineTransformIdentity;
321 | tokenView.alpha = 1.0f;
322 | }
323 | }
324 | }];
325 |
326 | _tokenAnimations = nil;
327 | }
328 | }
329 |
330 | - (void)doLayout:(bool)animated
331 | {
332 | float width = (float)self.frame.size.width;
333 |
334 | const float textFieldMinWidth = 60;
335 | const float padding = 9;
336 | const float textFieldPadding = 5;
337 | const float spacing = 1;
338 |
339 | int currentLine = 0;
340 | float currentX = padding;
341 | float currentY = _linePadding;
342 |
343 | float additionalPadding = 0;
344 |
345 | CGRect targetFrames[_tokenList.count];
346 | memset(targetFrames, 0, sizeof(CGRect) * _tokenList.count);
347 |
348 | int index = -1;
349 | for (WATokenView *tokenView in _tokenList)
350 | {
351 | index++;
352 |
353 | float tokenWidth = [tokenView preferredWidth];
354 |
355 | if (width - padding - currentX - additionalPadding < MAX(tokenWidth, textFieldMinWidth) && currentX > padding + FLT_EPSILON)
356 | {
357 | currentLine++;
358 | currentY += _lineHeight + _lineSpacing;
359 | currentX = padding;
360 | }
361 |
362 | CGRect tokenFrame = CGRectMake(currentX, currentY - 1, MIN(tokenWidth, width - padding - currentX - additionalPadding), tokenView.frame.size.height);
363 |
364 | if (animated && tokenView.frame.origin.x > FLT_EPSILON)
365 | targetFrames[index] = tokenFrame;
366 | else
367 | tokenView.frame = tokenFrame;
368 | currentX += tokenFrame.size.width + spacing;
369 | }
370 |
371 | bool lastLineContainsTextFieldOnly = false;
372 |
373 | if (width - padding - currentX - additionalPadding < textFieldMinWidth)
374 | {
375 | currentLine++;
376 | currentY += _lineHeight + _lineSpacing;
377 | currentX = padding;
378 |
379 | lastLineContainsTextFieldOnly = true;
380 | }
381 |
382 | if (currentLine + 1 != _currentNumberOfLines)
383 | animated = true;
384 |
385 | CGRect textFieldFrame = CGRectMake(currentX + textFieldPadding, currentY + 4 - 12, width - padding - currentX - textFieldPadding * 2 - additionalPadding + 4, _textField.frame.size.height);
386 | _textField.frame = textFieldFrame;
387 | if (animated)
388 | {
389 | _textField.alpha = 0.0f;
390 |
391 | [UIView animateWithDuration:0.2 animations:^
392 | {
393 | _textField.alpha = 1.0f;
394 | }];
395 | }
396 |
397 | if (lastLineContainsTextFieldOnly && ![self hasFirstResponder])
398 | {
399 | currentLine--;
400 | currentY -= _lineHeight + _lineSpacing;
401 | }
402 |
403 | if (animated)
404 | {
405 | [UIView beginAnimations:@"tokenField" context:nil];
406 | [UIView setAnimationDuration:0.15];
407 |
408 | int index = -1;
409 | for (WATokenView *tokenView in _tokenList)
410 | {
411 | index++;
412 |
413 | if (targetFrames[index].origin.x > FLT_EPSILON)
414 | tokenView.frame = targetFrames[index];
415 | }
416 | }
417 |
418 | currentY += _lineHeight + _linePadding;
419 |
420 | _scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width, currentY);
421 |
422 | if (animated)
423 | {
424 | [UIView commitAnimations];
425 | }
426 |
427 | if (MIN(currentLine + 1, _maxNumberOfLines) != MIN(_currentNumberOfLines, _maxNumberOfLines))
428 | {
429 | id delegate = _delegate;
430 |
431 | [delegate tokenFieldView:self didChangeHeight:_lineHeight * MIN(currentLine + 1, _maxNumberOfLines) + MAX(0, currentLine) * _lineSpacing + _linePadding * 2];
432 | }
433 | else if (currentLine + 1 > _currentNumberOfLines)
434 | {
435 | [self scrollToTextField:true];
436 | }
437 |
438 | _currentNumberOfLines = currentLine + 1;
439 | }
440 |
441 | - (bool)hasFirstResponder
442 | {
443 | return _textField.isFirstResponder;
444 | }
445 |
446 | - (UIView *)findFirstResponder:(UIView *)view
447 | {
448 | if ([view isFirstResponder])
449 | return view;
450 |
451 | for (UIView *subview in view.subviews)
452 | {
453 | UIView *result = [self findFirstResponder:subview];
454 | if (result != nil)
455 | return result;
456 | }
457 |
458 | return nil;
459 | }
460 |
461 | #pragma mark -
462 |
463 | - (BOOL)textFieldShouldReturn:(UITextField *)textField
464 | {
465 | if (textField == _textField)
466 | {
467 |
468 | bool wasEmpty = textField.text.length == 0;
469 | textField.text = @"";
470 |
471 | if (_delegate != nil)
472 | {
473 | id delegate = _delegate;
474 |
475 | [delegate tokenFieldView:self didChangeText:textField.text];
476 | if (wasEmpty != textField.text.length == 0)
477 | [delegate tokenFieldView:self didChangeSearchStatus:[self searchIsActive] byClearingTextField:true];
478 | }
479 |
480 | [self scrollToTextField:false];
481 |
482 | _textField.hidden = true;
483 | dispatch_async(dispatch_get_main_queue(), ^
484 | {
485 | _textField.hidden = false;
486 | });
487 | }
488 |
489 | return false;
490 | }
491 |
492 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
493 | {
494 | if (textField == _textField)
495 | {
496 | bool wasEmpty = textField.text.length == 0;
497 | textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
498 |
499 | [self scrollToTextField:true];
500 |
501 | id delegate = _delegate;
502 |
503 | if (delegate != nil)
504 | {
505 | [delegate tokenFieldView:self didChangeText:textField.text];
506 | if (wasEmpty != textField.text.length == 0)
507 | [delegate tokenFieldView:self didChangeSearchStatus:[self searchIsActive] byClearingTextField:true];
508 | }
509 | }
510 |
511 | return false;
512 | }
513 |
514 | - (void)textFieldDidHitLastBackspace
515 | {
516 | if (_tokenList.count != 0)
517 | {
518 | [[_tokenList lastObject] becomeFirstResponder];
519 | }
520 | }
521 |
522 | - (void)textFieldDidBecomeFirstResponder
523 | {
524 | [self setNeedsLayout];
525 | }
526 |
527 | - (void)textFieldDidResignFirstResponder
528 | {
529 | if (_tokenAnimations == nil)
530 | _tokenAnimations = [[NSMutableDictionary alloc] init];
531 |
532 | [self setNeedsLayout];
533 | }
534 |
535 | - (void)scrollToTextField:(bool)animated
536 | {
537 | CGPoint contentOffset = _scrollView.contentOffset;
538 | CGSize contentSize = _scrollView.contentSize;
539 | CGSize frameSize = _scrollView.frame.size;
540 | if (contentOffset.y < contentSize.height - frameSize.height)
541 | contentOffset = CGPointMake(0, contentSize.height - frameSize.height);
542 | if (contentOffset.y < 0)
543 | contentOffset.y = 0;
544 |
545 | if (!animated)
546 | [_scrollView setContentOffset:contentOffset animated:animated];
547 | else
548 | {
549 | [UIView animateWithDuration:0.2 animations:^
550 | {
551 | [_scrollView setContentOffset:contentOffset animated:false];
552 | }];
553 | }
554 | }
555 |
556 | - (bool)searchIsActive
557 | {
558 | return /*_textField.isFirstResponder && */_textField.text.length != 0;
559 | }
560 |
561 | - (void)clearText
562 | {
563 | _textField.text = @"";
564 |
565 | id delegate = _delegate;
566 |
567 | if (delegate != nil)
568 | [delegate tokenFieldView:self didChangeSearchStatus:[self searchIsActive] byClearingTextField:false];
569 | }
570 |
571 | - (void)highlightToken:(WATokenView *)tokenView
572 | {
573 | for (WATokenView *view in _tokenList)
574 | {
575 | if (view != tokenView)
576 | {
577 | if (view.selected)
578 | view.selected = false;
579 | }
580 | }
581 |
582 | tokenView.selected = true;
583 |
584 | [self setNeedsLayout];
585 | }
586 |
587 | - (void)unhighlightToken:(WATokenView *)tokenView
588 | {
589 | tokenView.selected = false;
590 |
591 | if (_tokenAnimations == nil)
592 | _tokenAnimations = [[NSMutableDictionary alloc] init];
593 |
594 | [self setNeedsLayout];
595 | }
596 |
597 | - (void)deleteToken:(WATokenView *)tokenView
598 | {
599 | int index = -1;
600 | for (WATokenView *view in _tokenList)
601 | {
602 | index++;
603 |
604 | if (view == tokenView)
605 | {
606 | [_tokenList removeObjectAtIndex:index];
607 | break;
608 | }
609 | }
610 |
611 | [tokenView removeFromSuperview];
612 | [_textField becomeFirstResponder];
613 |
614 | [self setNeedsLayout];
615 |
616 | id delegate = _delegate;
617 |
618 | if (delegate != nil)
619 | [delegate tokenFieldView:self didDeleteTokenWithId:tokenView.tokenId];
620 |
621 | if (_tokenList.count == 0)
622 | [_textField setShowPlaceholder:true animated:false];
623 | [self updateCounter];
624 | }
625 |
626 | - (void)beginTransition:(NSTimeInterval)duration
627 | {
628 | UIImage *inputFieldImage = nil;
629 | UIImageView *temporaryImageView = nil;
630 |
631 | UIGraphicsBeginImageContextWithOptions(_scrollView.bounds.size, true, 0.0f);
632 | [self.layer renderInContext:UIGraphicsGetCurrentContext()];
633 | inputFieldImage = UIGraphicsGetImageFromCurrentImageContext();
634 | UIGraphicsEndImageContext();
635 | temporaryImageView = [[UIImageView alloc] initWithImage:inputFieldImage];
636 | temporaryImageView.frame = _scrollView.bounds;
637 |
638 | UIView *temporaryImageViewContainer = [[UIView alloc] initWithFrame:_scrollView.frame];
639 | temporaryImageViewContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
640 | temporaryImageViewContainer.clipsToBounds = true;
641 | [temporaryImageViewContainer addSubview:temporaryImageView];
642 |
643 | [self insertSubview:temporaryImageViewContainer aboveSubview:_scrollView];
644 | _scrollView.alpha = 0.0f;
645 |
646 | [UIView animateWithDuration:duration animations:^
647 | {
648 | temporaryImageView.alpha = 0.0f;
649 | _scrollView.alpha = 1.0f;
650 | } completion:^(__unused BOOL finished)
651 | {
652 | [temporaryImageView removeFromSuperview];
653 | [temporaryImageViewContainer removeFromSuperview];
654 | }];
655 | }
656 |
657 | - (void)tapRecognized:(UITapGestureRecognizer *)recognizer
658 | {
659 | if (recognizer.state == UIGestureRecognizerStateRecognized)
660 | {
661 | [_textField becomeFirstResponder];
662 | }
663 | }
664 |
665 | - (BOOL)hasText
666 | {
667 | return false;
668 | }
669 |
670 | - (void)insertText:(NSString *)__unused text
671 | {
672 | }
673 |
674 | - (void)deleteBackward
675 | {
676 | }
677 |
678 | @end
679 |
--------------------------------------------------------------------------------