├── CHANGELOG
├── LICENSE
├── README.md
├── SCPinViewController.podspec
├── SCPinViewController.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── maxim.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── maxim.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ ├── SCPinViewController.xcscheme
│ └── xcschememanagement.plist
├── SCPinViewController
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
├── SCPinViewController
│ ├── Appearance
│ │ ├── SCPinAppearance.h
│ │ └── SCPinAppearance.m
│ ├── SCPinViewController.h
│ ├── SCPinViewController.m
│ ├── SCPinViewController.xib
│ ├── View
│ │ ├── PinButton
│ │ │ ├── SCPinButton.h
│ │ │ └── SCPinButton.m
│ │ └── PinView
│ │ │ ├── SCPinView.h
│ │ │ └── SCPinView.m
│ └── images
│ │ ├── sc_img_delete.png
│ │ ├── sc_img_delete@2x.png
│ │ ├── sc_img_delete@3x.png
│ │ ├── sc_img_touchid.png
│ │ ├── sc_img_touchid@2x.png
│ │ └── sc_img_touchid@3x.png
├── Supporting Files
│ └── main.m
├── ViewController.h
└── ViewController.m
└── Screen.png
/CHANGELOG:
--------------------------------------------------------------------------------
1 | ### Version 1.0.0
2 | Initial version
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Sugar and Candy
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
SCPinViewController
2 |
3 |
4 |
5 |
6 |
7 | Overview
8 | -------------
9 | Super customization Pin controller.
10 |
11 | Feature
12 | -------------
13 | - Touch ID
14 | - Autolayout
15 | - Cocoapods
16 | - Customization
17 |
18 | ### Installation with CocoaPods
19 |
20 | [CocoaPods](http://cocoapods.org) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries like SCPinViewController in your projects. See the ["Getting Started" guide for more information].
21 |
22 |
23 | #### Podfile
24 |
25 | ```ruby
26 | platform :ios, '8.0'
27 | pod 'SCPinViewController'
28 |
29 | ```
30 |
31 |
32 | ### Usage for validate
33 |
34 | ```
35 | // Import the class
36 | #import
37 |
38 | ...
39 |
40 | - (void)showPinAction:(UIButton *)sender {
41 | SCPinViewController *vc = [[SCPinViewController alloc] initWithScope:SCPinViewControllerScopeValidate];
42 |
43 | vc.dataSource = self;
44 | vc.validateDelegate = self;
45 | [self presentViewController:vc animated:YES completion:nil];
46 | }
47 |
48 | -(NSString *)codeForPinViewController:(SCPinViewController *)pinViewController {
49 | return @"1234";
50 | }
51 |
52 |
53 | -(void)pinViewControllerDidSetWrongPin:(SCPinViewController *)pinViewController {
54 | NSLog(@"Wrong")
55 | }
56 |
57 | -(void)pinViewControllerDidSetСorrectPin:(SCPinViewController *)pinViewController{
58 | [self dismissViewControllerAnimated:YES completion:nil];
59 |
60 | }
61 |
62 | ...
63 |
64 | ```
65 |
66 | ### Usage for create
67 |
68 | ```
69 | // Import the class
70 | #import
71 |
72 | ...
73 |
74 | - (void)showPinAction:(UIButton *)sender {
75 | SCPinViewController *vc = [[SCPinViewController alloc] initWithScope:SCPinViewControllerScopeCreate];
76 | vc.createDelegate = self;
77 | [self presentViewController:vc animated:YES completion:nil];
78 | }
79 |
80 | -(void)pinViewController:(SCPinViewController *)pinViewController didSetNewPin:(NSString *)pin {
81 | NSLog(@"pinViewController: %@",pinViewController);
82 | [[NSUserDefaults standardUserDefaults] setObject:pin forKey:kViewControllerPin];
83 | [[NSUserDefaults standardUserDefaults] synchronize];
84 | [self dismissViewControllerAnimated:YES completion:nil];
85 | }
86 |
87 | ...
88 |
89 | ```
90 |
91 |
92 | ### Apply custom appearance
93 |
94 | ```
95 | // Import the class
96 | #import
97 | #import
98 |
99 | ...
100 |
101 | - (void)showPinAction:(UIButton *)sender {
102 | SCPinAppearance *appearance = [SCPinAppearance defaultAppearance];
103 |
104 | appearance.titleText = @"Enter PIN";
105 | appearance.numberButtonStrokeWitdh = 1.0f;
106 | appearance.pinSize = CGSizeMake(8.0f, 8.0f);
107 | [SCPinViewController setNewAppearance:appearance];
108 |
109 | SCPinViewController *vc = [[SCPinViewController alloc] initWithScope:SCPinViewControllerScopeValidate];
110 |
111 | vc.dataSource = self;
112 | vc.validateDelegate = self;
113 | [self presentViewController:vc animated:YES completion:nil];
114 | }
115 |
116 | -(NSString *)codeForPinViewController:(SCPinViewController *)pinViewController {
117 | return @"1234";
118 | }
119 |
120 |
121 | -(void)pinViewControllerDidSetWrongPin:(SCPinViewController *)pinViewController {
122 | NSLog(@"Wrong")
123 | }
124 |
125 | -(void)pinViewControllerDidSetСorrectPin:(SCPinViewController *)pinViewController{
126 | [self dismissViewControllerAnimated:YES completion:nil];
127 |
128 | }
129 |
130 | ...
131 |
132 | ```
133 |
134 |
135 | 
136 |
137 |
138 | License
139 | -------------------------------------------------------
140 | The MIT License (MIT)
141 |
142 | Copyright (c) 2016 Sugar and Candy
143 |
144 | Permission is hereby granted, free of charge, to any person obtaining a copy
145 | of this software and associated documentation files (the "Software"), to deal
146 | in the Software without restriction, including without limitation the rights
147 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
148 | copies of the Software, and to permit persons to whom the Software is
149 | furnished to do so, subject to the following conditions:
150 |
151 | The above copyright notice and this permission notice shall be included in all
152 | copies or substantial portions of the Software.
153 |
154 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
155 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
156 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
157 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
158 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
159 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
160 | SOFTWARE.
161 |
--------------------------------------------------------------------------------
/SCPinViewController.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'SCPinViewController'
3 | s.version = '1.0.2'
4 | s.license = 'MIT'
5 | s.summary = 'Super customization pin code validator'
6 | s.homepage = 'https://github.com/SugarAndCandy/SCPinViewController.git'
7 | s.social_media_url = 'https://twitter.com/sugarandcandyru'
8 | s.authors = { 'Sugar and Candy' => 'hi@sugarandcandy.ru' }
9 | s.source = { :git => 'https://github.com/SugarAndCandy/SCPinViewController.git', :tag => s.version, :submodules => true }
10 | s.requires_arc = true
11 | s.platform = :ios, "8.0"
12 | s.ios.deployment_target = "8.0"
13 |
14 | s.public_header_files = 'SCPinViewController/**/SC*.h'
15 | s.source_files = 'SCPinViewController/**/SC*.{m,h}'
16 | #s.resource_bundles = {
17 | # 'SCPinViewController' => ['SCPinViewController/**/*.{png}']
18 | #}
19 | s.resources = ['SCPinViewController/**/*.{xib}','SCPinViewController/**/*.{png}']
20 |
21 | end
22 |
--------------------------------------------------------------------------------
/SCPinViewController.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 942AA3871D396DE8008E39F9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 942AA3861D396DE8008E39F9 /* main.m */; };
11 | 942AA38A1D396DE8008E39F9 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 942AA3891D396DE8008E39F9 /* AppDelegate.m */; };
12 | 942AA38D1D396DE8008E39F9 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 942AA38C1D396DE8008E39F9 /* ViewController.m */; };
13 | 942AA3901D396DE8008E39F9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 942AA38E1D396DE8008E39F9 /* Main.storyboard */; };
14 | 942AA3921D396DE8008E39F9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 942AA3911D396DE8008E39F9 /* Assets.xcassets */; };
15 | 942AA3951D396DE8008E39F9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 942AA3931D396DE8008E39F9 /* LaunchScreen.storyboard */; };
16 | 942AA3A01D396E1A008E39F9 /* SCPinViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 942AA39E1D396E1A008E39F9 /* SCPinViewController.m */; };
17 | 942AA3A11D396E1A008E39F9 /* SCPinViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 942AA39F1D396E1A008E39F9 /* SCPinViewController.xib */; };
18 | 942AA3A41D397141008E39F9 /* SCPinButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 942AA3A31D397141008E39F9 /* SCPinButton.m */; };
19 | 9471BF4A1E046F8E00DE5C51 /* sc_img_delete.png in Resources */ = {isa = PBXBuildFile; fileRef = 9471BF441E046F8E00DE5C51 /* sc_img_delete.png */; };
20 | 9471BF4B1E046F8E00DE5C51 /* sc_img_delete@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9471BF451E046F8E00DE5C51 /* sc_img_delete@2x.png */; };
21 | 9471BF4C1E046F8E00DE5C51 /* sc_img_delete@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9471BF461E046F8E00DE5C51 /* sc_img_delete@3x.png */; };
22 | 9471BF4D1E046F8E00DE5C51 /* sc_img_touchid.png in Resources */ = {isa = PBXBuildFile; fileRef = 9471BF471E046F8E00DE5C51 /* sc_img_touchid.png */; };
23 | 9471BF4E1E046F8E00DE5C51 /* sc_img_touchid@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9471BF481E046F8E00DE5C51 /* sc_img_touchid@2x.png */; };
24 | 9471BF4F1E046F8E00DE5C51 /* sc_img_touchid@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9471BF491E046F8E00DE5C51 /* sc_img_touchid@3x.png */; };
25 | 94AF9D621D3A2BC800C9B7E0 /* SCPinView.m in Sources */ = {isa = PBXBuildFile; fileRef = 94AF9D611D3A2BC800C9B7E0 /* SCPinView.m */; };
26 | 94AF9D871D3A94A400C9B7E0 /* SCPinAppearance.m in Sources */ = {isa = PBXBuildFile; fileRef = 94AF9D861D3A94A400C9B7E0 /* SCPinAppearance.m */; };
27 | /* End PBXBuildFile section */
28 |
29 | /* Begin PBXFileReference section */
30 | 942AA3821D396DE8008E39F9 /* SCPinViewController.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SCPinViewController.app; sourceTree = BUILT_PRODUCTS_DIR; };
31 | 942AA3861D396DE8008E39F9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
32 | 942AA3881D396DE8008E39F9 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
33 | 942AA3891D396DE8008E39F9 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
34 | 942AA38B1D396DE8008E39F9 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
35 | 942AA38C1D396DE8008E39F9 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
36 | 942AA38F1D396DE8008E39F9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
37 | 942AA3911D396DE8008E39F9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
38 | 942AA3941D396DE8008E39F9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
39 | 942AA3961D396DE8008E39F9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
40 | 942AA39D1D396E1A008E39F9 /* SCPinViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCPinViewController.h; sourceTree = ""; };
41 | 942AA39E1D396E1A008E39F9 /* SCPinViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCPinViewController.m; sourceTree = ""; };
42 | 942AA39F1D396E1A008E39F9 /* SCPinViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCPinViewController.xib; sourceTree = ""; };
43 | 942AA3A21D397141008E39F9 /* SCPinButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCPinButton.h; sourceTree = ""; };
44 | 942AA3A31D397141008E39F9 /* SCPinButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCPinButton.m; sourceTree = ""; };
45 | 9471BF441E046F8E00DE5C51 /* sc_img_delete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = sc_img_delete.png; sourceTree = ""; };
46 | 9471BF451E046F8E00DE5C51 /* sc_img_delete@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "sc_img_delete@2x.png"; sourceTree = ""; };
47 | 9471BF461E046F8E00DE5C51 /* sc_img_delete@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "sc_img_delete@3x.png"; sourceTree = ""; };
48 | 9471BF471E046F8E00DE5C51 /* sc_img_touchid.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = sc_img_touchid.png; sourceTree = ""; };
49 | 9471BF481E046F8E00DE5C51 /* sc_img_touchid@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "sc_img_touchid@2x.png"; sourceTree = ""; };
50 | 9471BF491E046F8E00DE5C51 /* sc_img_touchid@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "sc_img_touchid@3x.png"; sourceTree = ""; };
51 | 94AF9D601D3A2BC800C9B7E0 /* SCPinView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCPinView.h; sourceTree = ""; };
52 | 94AF9D611D3A2BC800C9B7E0 /* SCPinView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCPinView.m; sourceTree = ""; };
53 | 94AF9D851D3A94A400C9B7E0 /* SCPinAppearance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCPinAppearance.h; sourceTree = ""; };
54 | 94AF9D861D3A94A400C9B7E0 /* SCPinAppearance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCPinAppearance.m; sourceTree = ""; };
55 | /* End PBXFileReference section */
56 |
57 | /* Begin PBXFrameworksBuildPhase section */
58 | 942AA37F1D396DE8008E39F9 /* Frameworks */ = {
59 | isa = PBXFrameworksBuildPhase;
60 | buildActionMask = 2147483647;
61 | files = (
62 | );
63 | runOnlyForDeploymentPostprocessing = 0;
64 | };
65 | /* End PBXFrameworksBuildPhase section */
66 |
67 | /* Begin PBXGroup section */
68 | 942AA3791D396DE8008E39F9 = {
69 | isa = PBXGroup;
70 | children = (
71 | 942AA3831D396DE8008E39F9 /* Products */,
72 | 942AA3841D396DE8008E39F9 /* SCPinViewController */,
73 | );
74 | sourceTree = "";
75 | };
76 | 942AA3831D396DE8008E39F9 /* Products */ = {
77 | isa = PBXGroup;
78 | children = (
79 | 942AA3821D396DE8008E39F9 /* SCPinViewController.app */,
80 | );
81 | name = Products;
82 | sourceTree = "";
83 | };
84 | 942AA3841D396DE8008E39F9 /* SCPinViewController */ = {
85 | isa = PBXGroup;
86 | children = (
87 | 942AA39C1D396DFD008E39F9 /* SCPinViewController */,
88 | 942AA3851D396DE8008E39F9 /* Supporting Files */,
89 | 942AA3881D396DE8008E39F9 /* AppDelegate.h */,
90 | 942AA3891D396DE8008E39F9 /* AppDelegate.m */,
91 | 942AA3911D396DE8008E39F9 /* Assets.xcassets */,
92 | 942AA3961D396DE8008E39F9 /* Info.plist */,
93 | 942AA3931D396DE8008E39F9 /* LaunchScreen.storyboard */,
94 | 942AA38E1D396DE8008E39F9 /* Main.storyboard */,
95 | 942AA38B1D396DE8008E39F9 /* ViewController.h */,
96 | 942AA38C1D396DE8008E39F9 /* ViewController.m */,
97 | );
98 | path = SCPinViewController;
99 | sourceTree = "";
100 | };
101 | 942AA3851D396DE8008E39F9 /* Supporting Files */ = {
102 | isa = PBXGroup;
103 | children = (
104 | 942AA3861D396DE8008E39F9 /* main.m */,
105 | );
106 | path = "Supporting Files";
107 | sourceTree = "";
108 | };
109 | 942AA39C1D396DFD008E39F9 /* SCPinViewController */ = {
110 | isa = PBXGroup;
111 | children = (
112 | 94AF9D841D3A948200C9B7E0 /* Appearance */,
113 | 94AF9D5E1D3A2BA100C9B7E0 /* View */,
114 | 944E832E1D3987AA004A565E /* images */,
115 | 942AA39D1D396E1A008E39F9 /* SCPinViewController.h */,
116 | 942AA39E1D396E1A008E39F9 /* SCPinViewController.m */,
117 | 942AA39F1D396E1A008E39F9 /* SCPinViewController.xib */,
118 | );
119 | path = SCPinViewController;
120 | sourceTree = "";
121 | };
122 | 944E832E1D3987AA004A565E /* images */ = {
123 | isa = PBXGroup;
124 | children = (
125 | 9471BF441E046F8E00DE5C51 /* sc_img_delete.png */,
126 | 9471BF451E046F8E00DE5C51 /* sc_img_delete@2x.png */,
127 | 9471BF461E046F8E00DE5C51 /* sc_img_delete@3x.png */,
128 | 9471BF471E046F8E00DE5C51 /* sc_img_touchid.png */,
129 | 9471BF481E046F8E00DE5C51 /* sc_img_touchid@2x.png */,
130 | 9471BF491E046F8E00DE5C51 /* sc_img_touchid@3x.png */,
131 | );
132 | path = images;
133 | sourceTree = "";
134 | };
135 | 94AF9D5E1D3A2BA100C9B7E0 /* View */ = {
136 | isa = PBXGroup;
137 | children = (
138 | 94AF9D5F1D3A2BB300C9B7E0 /* PinButton */,
139 | 94AF9D631D3A2BCB00C9B7E0 /* PinView */,
140 | );
141 | path = View;
142 | sourceTree = "";
143 | };
144 | 94AF9D5F1D3A2BB300C9B7E0 /* PinButton */ = {
145 | isa = PBXGroup;
146 | children = (
147 | 942AA3A21D397141008E39F9 /* SCPinButton.h */,
148 | 942AA3A31D397141008E39F9 /* SCPinButton.m */,
149 | );
150 | path = PinButton;
151 | sourceTree = "";
152 | };
153 | 94AF9D631D3A2BCB00C9B7E0 /* PinView */ = {
154 | isa = PBXGroup;
155 | children = (
156 | 94AF9D601D3A2BC800C9B7E0 /* SCPinView.h */,
157 | 94AF9D611D3A2BC800C9B7E0 /* SCPinView.m */,
158 | );
159 | path = PinView;
160 | sourceTree = "";
161 | };
162 | 94AF9D841D3A948200C9B7E0 /* Appearance */ = {
163 | isa = PBXGroup;
164 | children = (
165 | 94AF9D851D3A94A400C9B7E0 /* SCPinAppearance.h */,
166 | 94AF9D861D3A94A400C9B7E0 /* SCPinAppearance.m */,
167 | );
168 | path = Appearance;
169 | sourceTree = "";
170 | };
171 | /* End PBXGroup section */
172 |
173 | /* Begin PBXNativeTarget section */
174 | 942AA3811D396DE8008E39F9 /* SCPinViewController */ = {
175 | isa = PBXNativeTarget;
176 | buildConfigurationList = 942AA3991D396DE8008E39F9 /* Build configuration list for PBXNativeTarget "SCPinViewController" */;
177 | buildPhases = (
178 | 942AA37E1D396DE8008E39F9 /* Sources */,
179 | 942AA37F1D396DE8008E39F9 /* Frameworks */,
180 | 942AA3801D396DE8008E39F9 /* Resources */,
181 | );
182 | buildRules = (
183 | );
184 | dependencies = (
185 | );
186 | name = SCPinViewController;
187 | productName = SCPinViewController;
188 | productReference = 942AA3821D396DE8008E39F9 /* SCPinViewController.app */;
189 | productType = "com.apple.product-type.application";
190 | };
191 | /* End PBXNativeTarget section */
192 |
193 | /* Begin PBXProject section */
194 | 942AA37A1D396DE8008E39F9 /* Project object */ = {
195 | isa = PBXProject;
196 | attributes = {
197 | LastUpgradeCheck = 0730;
198 | ORGANIZATIONNAME = "Sugar and Candy";
199 | TargetAttributes = {
200 | 942AA3811D396DE8008E39F9 = {
201 | CreatedOnToolsVersion = 7.3.1;
202 | DevelopmentTeam = 6PHPGAAG6M;
203 | ProvisioningStyle = Manual;
204 | };
205 | };
206 | };
207 | buildConfigurationList = 942AA37D1D396DE8008E39F9 /* Build configuration list for PBXProject "SCPinViewController" */;
208 | compatibilityVersion = "Xcode 3.2";
209 | developmentRegion = English;
210 | hasScannedForEncodings = 0;
211 | knownRegions = (
212 | en,
213 | Base,
214 | );
215 | mainGroup = 942AA3791D396DE8008E39F9;
216 | productRefGroup = 942AA3831D396DE8008E39F9 /* Products */;
217 | projectDirPath = "";
218 | projectRoot = "";
219 | targets = (
220 | 942AA3811D396DE8008E39F9 /* SCPinViewController */,
221 | );
222 | };
223 | /* End PBXProject section */
224 |
225 | /* Begin PBXResourcesBuildPhase section */
226 | 942AA3801D396DE8008E39F9 /* Resources */ = {
227 | isa = PBXResourcesBuildPhase;
228 | buildActionMask = 2147483647;
229 | files = (
230 | 9471BF4A1E046F8E00DE5C51 /* sc_img_delete.png in Resources */,
231 | 9471BF4D1E046F8E00DE5C51 /* sc_img_touchid.png in Resources */,
232 | 942AA3A11D396E1A008E39F9 /* SCPinViewController.xib in Resources */,
233 | 9471BF4E1E046F8E00DE5C51 /* sc_img_touchid@2x.png in Resources */,
234 | 942AA3951D396DE8008E39F9 /* LaunchScreen.storyboard in Resources */,
235 | 942AA3921D396DE8008E39F9 /* Assets.xcassets in Resources */,
236 | 9471BF4B1E046F8E00DE5C51 /* sc_img_delete@2x.png in Resources */,
237 | 9471BF4F1E046F8E00DE5C51 /* sc_img_touchid@3x.png in Resources */,
238 | 9471BF4C1E046F8E00DE5C51 /* sc_img_delete@3x.png in Resources */,
239 | 942AA3901D396DE8008E39F9 /* Main.storyboard in Resources */,
240 | );
241 | runOnlyForDeploymentPostprocessing = 0;
242 | };
243 | /* End PBXResourcesBuildPhase section */
244 |
245 | /* Begin PBXSourcesBuildPhase section */
246 | 942AA37E1D396DE8008E39F9 /* Sources */ = {
247 | isa = PBXSourcesBuildPhase;
248 | buildActionMask = 2147483647;
249 | files = (
250 | 942AA3A01D396E1A008E39F9 /* SCPinViewController.m in Sources */,
251 | 942AA38D1D396DE8008E39F9 /* ViewController.m in Sources */,
252 | 942AA38A1D396DE8008E39F9 /* AppDelegate.m in Sources */,
253 | 94AF9D871D3A94A400C9B7E0 /* SCPinAppearance.m in Sources */,
254 | 942AA3A41D397141008E39F9 /* SCPinButton.m in Sources */,
255 | 94AF9D621D3A2BC800C9B7E0 /* SCPinView.m in Sources */,
256 | 942AA3871D396DE8008E39F9 /* main.m in Sources */,
257 | );
258 | runOnlyForDeploymentPostprocessing = 0;
259 | };
260 | /* End PBXSourcesBuildPhase section */
261 |
262 | /* Begin PBXVariantGroup section */
263 | 942AA38E1D396DE8008E39F9 /* Main.storyboard */ = {
264 | isa = PBXVariantGroup;
265 | children = (
266 | 942AA38F1D396DE8008E39F9 /* Base */,
267 | );
268 | name = Main.storyboard;
269 | path = .;
270 | sourceTree = "";
271 | };
272 | 942AA3931D396DE8008E39F9 /* LaunchScreen.storyboard */ = {
273 | isa = PBXVariantGroup;
274 | children = (
275 | 942AA3941D396DE8008E39F9 /* Base */,
276 | );
277 | name = LaunchScreen.storyboard;
278 | path = .;
279 | sourceTree = "";
280 | };
281 | /* End PBXVariantGroup section */
282 |
283 | /* Begin XCBuildConfiguration section */
284 | 942AA3971D396DE8008E39F9 /* Debug */ = {
285 | isa = XCBuildConfiguration;
286 | buildSettings = {
287 | ALWAYS_SEARCH_USER_PATHS = NO;
288 | CLANG_ANALYZER_NONNULL = YES;
289 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
290 | CLANG_CXX_LIBRARY = "libc++";
291 | CLANG_ENABLE_MODULES = YES;
292 | CLANG_ENABLE_OBJC_ARC = YES;
293 | CLANG_WARN_BOOL_CONVERSION = YES;
294 | CLANG_WARN_CONSTANT_CONVERSION = YES;
295 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
296 | CLANG_WARN_EMPTY_BODY = YES;
297 | CLANG_WARN_ENUM_CONVERSION = YES;
298 | CLANG_WARN_INT_CONVERSION = YES;
299 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
300 | CLANG_WARN_UNREACHABLE_CODE = YES;
301 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
302 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
303 | COPY_PHASE_STRIP = NO;
304 | DEBUG_INFORMATION_FORMAT = dwarf;
305 | ENABLE_STRICT_OBJC_MSGSEND = YES;
306 | ENABLE_TESTABILITY = YES;
307 | GCC_C_LANGUAGE_STANDARD = gnu99;
308 | GCC_DYNAMIC_NO_PIC = NO;
309 | GCC_NO_COMMON_BLOCKS = YES;
310 | GCC_OPTIMIZATION_LEVEL = 0;
311 | GCC_PREPROCESSOR_DEFINITIONS = (
312 | "DEBUG=1",
313 | "$(inherited)",
314 | );
315 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
316 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
317 | GCC_WARN_UNDECLARED_SELECTOR = YES;
318 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
319 | GCC_WARN_UNUSED_FUNCTION = YES;
320 | GCC_WARN_UNUSED_VARIABLE = YES;
321 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
322 | MTL_ENABLE_DEBUG_INFO = YES;
323 | ONLY_ACTIVE_ARCH = YES;
324 | SDKROOT = iphoneos;
325 | };
326 | name = Debug;
327 | };
328 | 942AA3981D396DE8008E39F9 /* Release */ = {
329 | isa = XCBuildConfiguration;
330 | buildSettings = {
331 | ALWAYS_SEARCH_USER_PATHS = NO;
332 | CLANG_ANALYZER_NONNULL = YES;
333 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
334 | CLANG_CXX_LIBRARY = "libc++";
335 | CLANG_ENABLE_MODULES = YES;
336 | CLANG_ENABLE_OBJC_ARC = YES;
337 | CLANG_WARN_BOOL_CONVERSION = YES;
338 | CLANG_WARN_CONSTANT_CONVERSION = YES;
339 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
340 | CLANG_WARN_EMPTY_BODY = YES;
341 | CLANG_WARN_ENUM_CONVERSION = YES;
342 | CLANG_WARN_INT_CONVERSION = YES;
343 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
344 | CLANG_WARN_UNREACHABLE_CODE = YES;
345 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
346 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
347 | COPY_PHASE_STRIP = NO;
348 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
349 | ENABLE_NS_ASSERTIONS = NO;
350 | ENABLE_STRICT_OBJC_MSGSEND = YES;
351 | GCC_C_LANGUAGE_STANDARD = gnu99;
352 | GCC_NO_COMMON_BLOCKS = YES;
353 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
354 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
355 | GCC_WARN_UNDECLARED_SELECTOR = YES;
356 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
357 | GCC_WARN_UNUSED_FUNCTION = YES;
358 | GCC_WARN_UNUSED_VARIABLE = YES;
359 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
360 | MTL_ENABLE_DEBUG_INFO = NO;
361 | SDKROOT = iphoneos;
362 | VALIDATE_PRODUCT = YES;
363 | };
364 | name = Release;
365 | };
366 | 942AA39A1D396DE8008E39F9 /* Debug */ = {
367 | isa = XCBuildConfiguration;
368 | buildSettings = {
369 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
370 | CODE_SIGN_IDENTITY = "iPhone Developer";
371 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
372 | DEVELOPMENT_TEAM = 6PHPGAAG6M;
373 | INFOPLIST_FILE = SCPinViewController/Info.plist;
374 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
375 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
376 | PRODUCT_BUNDLE_IDENTIFIER = ru.sugarandcandy.SCPinViewController;
377 | PRODUCT_NAME = "$(TARGET_NAME)";
378 | PROVISIONING_PROFILE = "31637879-d753-41a1-a98d-dfbebd0f3f2a";
379 | PROVISIONING_PROFILE_SPECIFIER = "iOS Development";
380 | };
381 | name = Debug;
382 | };
383 | 942AA39B1D396DE8008E39F9 /* Release */ = {
384 | isa = XCBuildConfiguration;
385 | buildSettings = {
386 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
387 | CODE_SIGN_IDENTITY = "iPhone Developer";
388 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
389 | DEVELOPMENT_TEAM = 6PHPGAAG6M;
390 | INFOPLIST_FILE = SCPinViewController/Info.plist;
391 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
392 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
393 | PRODUCT_BUNDLE_IDENTIFIER = ru.sugarandcandy.SCPinViewController;
394 | PRODUCT_NAME = "$(TARGET_NAME)";
395 | PROVISIONING_PROFILE = "af0a1a07-375d-405c-a8e5-010f0719edb8";
396 | PROVISIONING_PROFILE_SPECIFIER = "XC Ad Hoc: *";
397 | };
398 | name = Release;
399 | };
400 | /* End XCBuildConfiguration section */
401 |
402 | /* Begin XCConfigurationList section */
403 | 942AA37D1D396DE8008E39F9 /* Build configuration list for PBXProject "SCPinViewController" */ = {
404 | isa = XCConfigurationList;
405 | buildConfigurations = (
406 | 942AA3971D396DE8008E39F9 /* Debug */,
407 | 942AA3981D396DE8008E39F9 /* Release */,
408 | );
409 | defaultConfigurationIsVisible = 0;
410 | defaultConfigurationName = Release;
411 | };
412 | 942AA3991D396DE8008E39F9 /* Build configuration list for PBXNativeTarget "SCPinViewController" */ = {
413 | isa = XCConfigurationList;
414 | buildConfigurations = (
415 | 942AA39A1D396DE8008E39F9 /* Debug */,
416 | 942AA39B1D396DE8008E39F9 /* Release */,
417 | );
418 | defaultConfigurationIsVisible = 0;
419 | defaultConfigurationName = Release;
420 | };
421 | /* End XCConfigurationList section */
422 | };
423 | rootObject = 942AA37A1D396DE8008E39F9 /* Project object */;
424 | }
425 |
--------------------------------------------------------------------------------
/SCPinViewController.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SCPinViewController.xcodeproj/project.xcworkspace/xcuserdata/maxim.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarAndCandy/SCPinViewController/0a9ff6bdc6917961a9f3347bf27f6249be73e696/SCPinViewController.xcodeproj/project.xcworkspace/xcuserdata/maxim.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/SCPinViewController.xcodeproj/xcuserdata/maxim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/SCPinViewController.xcodeproj/xcuserdata/maxim.xcuserdatad/xcschemes/SCPinViewController.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/SCPinViewController.xcodeproj/xcuserdata/maxim.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SCPinViewController.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 942AA3811D396DE8008E39F9
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SCPinViewController/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 15.07.16.
6 | // Copyright © 2016 Sugar and Candy. 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 |
--------------------------------------------------------------------------------
/SCPinViewController/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 15.07.16.
6 | // Copyright © 2016 Sugar and Candy. 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 |
--------------------------------------------------------------------------------
/SCPinViewController/Assets.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 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/SCPinViewController/Base.lproj/LaunchScreen.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 |
27 |
28 |
--------------------------------------------------------------------------------
/SCPinViewController/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 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/SCPinViewController/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
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 |
40 |
41 |
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/Appearance/SCPinAppearance.h:
--------------------------------------------------------------------------------
1 | //
2 | // SCPinAppearance.h
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 16.07.16.
6 | // Copyright © 2016 Sugar and Candy. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SCPinAppearance : NSObject
12 | + (instancetype)defaultAppearance;
13 |
14 | @property (nonatomic, strong) UIColor *numberButtonColor;
15 | @property (nonatomic, strong) UIColor *numberButtonTitleColor;
16 | @property (nonatomic, strong) UIColor *numberButtonStrokeColor;
17 | @property (nonatomic, assign) CGFloat numberButtonStrokeWitdh;
18 | @property (nonatomic, assign) BOOL numberButtonstrokeEnabled;
19 | @property (nonatomic, strong) UIFont *numberButtonFont;
20 |
21 | @property (nonatomic, strong) UIColor *deleteButtonColor;
22 |
23 | @property (nonatomic, strong) UIColor *pinFillColor;
24 | @property (nonatomic, strong) UIColor *pinHighlightedColor;
25 | @property (nonatomic, strong) UIColor *pinStrokeColor;
26 | @property (nonatomic, assign) CGFloat pinStrokeWidth;
27 | @property (nonatomic, assign) CGSize pinSize;
28 |
29 | @property (nonatomic, assign) BOOL touchIDButtonEnabled;
30 | @property (nonatomic, strong) UIColor *touchIDButtonColor;
31 |
32 | @property (nonatomic, strong) NSString *titleText;
33 | @property (nonatomic, strong) UIColor *titleTextColor;
34 | @property (nonatomic, strong) UIFont *titleTextFont;
35 |
36 | @property (nonatomic, strong) NSString *confirmText;
37 |
38 | @property (nonatomic, strong) NSString *supportText;
39 | @property (nonatomic, strong) UIColor *supportTextColor;
40 | @property (nonatomic, strong) UIFont *supportTextFont;
41 |
42 | @property (nonatomic, strong) NSString *touchIDText;
43 | @property (nonatomic, strong) NSString *touchIDVerification;
44 |
45 | @property (nonatomic, assign) BOOL cancelButtonEnabled;
46 | @property (nonatomic, strong) NSString *cancelButtonText;
47 | @property (nonatomic, strong) UIColor *cancelButtonTextColor;
48 | @property (nonatomic, strong) UIFont *cancelButtonTextFont;
49 |
50 | @end
51 |
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/Appearance/SCPinAppearance.m:
--------------------------------------------------------------------------------
1 | //
2 | // SCPinAppearance.m
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 16.07.16.
6 | // Copyright © 2016 Sugar and Candy. All rights reserved.
7 | //
8 |
9 | #import "SCPinAppearance.h"
10 |
11 | @implementation SCPinAppearance
12 |
13 |
14 | + (instancetype)defaultAppearance {
15 | SCPinAppearance *defaultAppearance = [[SCPinAppearance alloc]init];
16 | return defaultAppearance;
17 | }
18 |
19 | - (instancetype)init {
20 | self = [super init];
21 | if (self) {
22 | [self setupDefaultAppearance];
23 | }
24 | return self;
25 | }
26 |
27 | -(void)setupDefaultAppearance {
28 | UIColor *defaultColor = [UIColor colorWithRed:46.0f / 255.0f green:192.0f / 255.0f blue:197.0f / 255.0f alpha:1];
29 | UIFont *defaultFont = [UIFont fontWithName:@"HelveticaNeue-Thin" size:27.0f];
30 |
31 | self.numberButtonColor = defaultColor;
32 | self.numberButtonTitleColor = [UIColor blackColor];
33 | self.numberButtonStrokeColor = defaultColor;
34 | self.numberButtonStrokeWitdh = 0.8f;
35 | self.numberButtonstrokeEnabled = YES;
36 | self.numberButtonFont = defaultFont;
37 |
38 | self.deleteButtonColor = defaultColor;
39 |
40 | self.pinFillColor = [UIColor clearColor];
41 | self.pinHighlightedColor = defaultColor;
42 | self.pinStrokeColor = defaultColor;
43 | self.pinStrokeWidth = 0.8f;
44 | self.pinSize = CGSizeMake(14.0f, 14.0f);
45 |
46 | self.touchIDButtonEnabled = YES;
47 | self.touchIDButtonColor = defaultColor;
48 |
49 | self.titleText = @"Enter Pin";
50 | self.titleTextFont = defaultFont;
51 | self.titleTextColor = defaultColor;
52 |
53 | self.confirmText = @"Confirm Pin";
54 |
55 | self.supportText = nil;
56 | self.supportTextFont = defaultFont;
57 | self.supportTextColor = defaultColor;
58 |
59 | self.touchIDText = @"Put finger";
60 | self.touchIDVerification = @"Pincode TouchID";
61 |
62 | self.cancelButtonEnabled = YES;
63 | self.cancelButtonText = @"Cancel";
64 | self.cancelButtonTextFont = [UIFont fontWithName:@"HelveticaNeue-Thin" size:16.0f];;
65 | self.cancelButtonTextColor = defaultColor;
66 | }
67 |
68 |
69 | /*
70 | #pragma mark - Setters
71 |
72 | -(void)setNumberButtonColor:(UIColor *)numberButtonColor {
73 | _numberButtonColor = numberButtonColor;
74 | for (SCPinButton *button in self.numberButtons) {
75 | [button setTintColor:numberButtonColor];
76 | }
77 | }
78 |
79 | -(void)setNumberButtonTitleColor:(UIColor *)numberButtonTitleColor {
80 | _numberButtonTitleColor = numberButtonTitleColor;
81 | for (SCPinButton *button in self.numberButtons) {
82 | [button setTitleColor:numberButtonTitleColor forState:UIControlStateNormal];
83 | [button setTitleColor:numberButtonTitleColor forState:UIControlStateSelected];
84 | [button setTitleColor:numberButtonTitleColor forState:UIControlStateHighlighted];
85 | }
86 | }
87 |
88 | -(void)setNumberButtonStrokeColor:(UIColor *)numberButtonStrokeColor {
89 | _numberButtonStrokeColor = numberButtonStrokeColor;
90 | for (SCPinButton *button in self.numberButtons) {
91 | button.strokeColor = numberButtonStrokeColor;
92 | }
93 | }
94 |
95 | -(void)setNumberButtonStrokeWitdh:(CGFloat)numberButtonStrokeWitdh {
96 | _numberButtonStrokeWitdh = numberButtonStrokeWitdh;
97 | for (SCPinButton *button in self.numberButtons) {
98 | button.strokeWidth = numberButtonStrokeWitdh;
99 | }
100 | }
101 |
102 | -(void)setNumberButtonstrokeEnabled:(BOOL)numberButtonstrokeEnabled {
103 | _numberButtonstrokeEnabled = numberButtonstrokeEnabled;
104 | for (SCPinButton *button in self.numberButtons) {
105 | button.strokeColor = numberButtonstrokeEnabled ? [self numberButtonStrokeColor] : [UIColor clearColor];
106 | }
107 | }
108 |
109 | -(void)setNumberButtonFont:(UIFont *)numberButtonFont {
110 | _numberButtonFont = numberButtonFont;
111 | for (SCPinButton *button in self.numberButtons) {
112 | [button.titleLabel setFont:numberButtonFont];
113 | [button setNeedsDisplay];
114 | }
115 | }
116 |
117 | -(void)setDeleteButtonColor:(UIColor *)deleteButtonColor {
118 | _deleteButtonColor = deleteButtonColor;
119 | [self.deleteButton setTintColor:deleteButtonColor];
120 | //[self.deleteButton.titleLabel setTintColor:deleteButtonColor];
121 |
122 | }
123 |
124 | -(void)setDeleteButtonText:(NSString *)deleteButtonText {
125 | _deleteButtonText = deleteButtonText;
126 | //[self.deleteButton setTitle:deleteButtonText forState:UIControlStateNormal];
127 | //[self.deleteButton setTitle:deleteButtonText forState:UIControlStateSelected];
128 | //[self.deleteButton setTitle:deleteButtonText forState:UIControlStateHighlighted];
129 | }
130 |
131 | -(void)setDeleteButtonFont:(UIFont *)deleteButtonFont {
132 | _deleteButtonFont = deleteButtonFont;
133 | [self.deleteButton.titleLabel setFont:deleteButtonFont];
134 | }
135 |
136 | -(void)setPinFillColor:(UIColor *)pinFillColor {
137 | _pinFillColor = pinFillColor;
138 | }
139 |
140 | -(void)setPinHighlightedColor:(UIColor *)pinHighlightedColor {
141 | _pinHighlightedColor = pinHighlightedColor;
142 | }
143 |
144 | -(void)setPinStrokeColor:(UIColor *)pinStrokeColor {
145 | _pinStrokeColor = pinStrokeColor;
146 | }
147 |
148 | -(void)setPinStrokeWidth:(CGFloat)pinStrokeWidth {
149 | _pinStrokeWidth = pinStrokeWidth;
150 | }
151 |
152 | -(void)setPinSize:(CGSize)pinSize {
153 | _pinSize = pinSize;
154 | self.viewForPinsLayoutConstraint.constant = pinSize.height;
155 | }
156 |
157 | -(void)setTouchIDButtonEnabled:(BOOL)touchIDButtonEnabled {
158 | _touchIDButtonEnabled = touchIDButtonEnabled;
159 | }
160 |
161 | -(void)setTouchIDButtonColor:(UIColor *)touchIDButtonColor {
162 | _touchIDButtonColor = touchIDButtonColor;
163 | [self.touchIDButton setTintColor:touchIDButtonColor];
164 | }
165 |
166 | -(void)setTitleText:(NSString *)titleText {
167 | _titleText = titleText;
168 | self.titleLabel.text = titleText;
169 | }
170 |
171 | -(void)setTitleTextFont:(UIFont *)titleTextFont {
172 | _titleTextFont = titleTextFont;
173 | self.titleLabel.font = titleTextFont;
174 | }
175 |
176 | -(void)setTitleTextColor:(UIColor *)titleTextColor {
177 | _titleTextColor = titleTextColor;
178 | self.titleLabel.textColor = titleTextColor;
179 |
180 | }
181 | -(void)setSupportText:(NSString *)supportText {
182 | _titleText = supportText;
183 | self.supportLabel.text = supportText;
184 | }
185 |
186 | -(void)setSupportTextFont:(UIFont *)supportTextFont {
187 | _titleTextFont = supportTextFont;
188 | self.supportLabel.font = supportTextFont;
189 | }
190 |
191 | -(void)setSupportTextColor:(UIColor *)supportTextColor {
192 | _titleTextColor = supportTextColor;
193 | self.supportLabel.textColor = supportTextColor;
194 |
195 | } */
196 |
197 |
198 | @end
199 |
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/SCPinViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SCPinViewController.h
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 15.07.16.
6 | // Copyright © 2016 Sugar and Candy. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol SCPinViewControllerValidateDelegate;
12 | @protocol SCPinViewControllerCreateDelegate;
13 | @protocol SCPinViewControllerDataSource;
14 | @class SCPinAppearance;
15 | typedef NS_ENUM(NSInteger, SCPinViewControllerScope) {
16 | SCPinViewControllerScopeValidate,
17 | SCPinViewControllerScopeCreate,
18 | SCPinViewControllerScopeConfirm
19 | };
20 |
21 | @interface SCPinViewController : UIViewController
22 | - (instancetype)initWithScope:(SCPinViewControllerScope)scope;
23 |
24 | + (SCPinAppearance *)appearance;
25 | + (void)setNewAppearance:(SCPinAppearance *)newAppearance;
26 |
27 |
28 | @property (nonatomic, assign, readonly) SCPinViewControllerScope scope;
29 |
30 | @property (nonatomic, weak) id createDelegate;
31 | @property (nonatomic, weak) id validateDelegate;
32 | @property (nonatomic, weak) id dataSource;
33 |
34 | @end
35 |
36 | @protocol SCPinViewControllerValidateDelegate
37 | @required
38 | /**
39 | * when user set wrong pin code calling this delegate method
40 | */
41 | -(void)pinViewControllerDidSetWrongPin:(SCPinViewController *)pinViewController;
42 | /**
43 | * when user set correct pin code calling this delegate method
44 | */
45 | -(void)pinViewControllerDidSetСorrectPin:(SCPinViewController *)pinViewController;
46 |
47 | @optional
48 | /**
49 | * when user cancel calling this delegate method
50 | */
51 | -(void)pinViewControllerDidCancel:(SCPinViewController *)pinViewController;
52 |
53 |
54 | @end
55 |
56 | @protocol SCPinViewControllerCreateDelegate
57 | @required
58 | /**
59 | * when user set new pin code calling this delegate method
60 | */
61 | -(void)pinViewController:(SCPinViewController *)pinViewController didSetNewPin:(NSString *)pin;
62 | /**
63 | * length for pin
64 | */
65 | -(NSInteger)lengthForPin;
66 |
67 | @optional
68 | /**
69 | * when user cancel calling this delegate method
70 | */
71 | -(void)pinViewControllerDidCancel:(SCPinViewController *)pinViewController;
72 |
73 | @end
74 |
75 | @protocol SCPinViewControllerDataSource
76 | @required
77 | /**
78 | * Pin code for controller. Supports from 2 to 8 characters
79 | */
80 | -(NSString *)codeForPinViewController:(SCPinViewController *)pinViewController;
81 |
82 | @optional
83 | -(BOOL)hideTouchIDButtonIfFingersAreNotEnrolled;
84 | -(BOOL)showTouchIDVerificationImmediately;
85 | @end
86 |
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/SCPinViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SCPinViewController.m
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 15.07.16.
6 | // Copyright © 2016 Sugar and Candy. All rights reserved.
7 | //
8 |
9 |
10 | #import "SCPinViewController.h"
11 | #import "SCPinButton.h"
12 | #import "SCPinView.h"
13 | #import "SCPinAppearance.h"
14 | #import
15 | #import
16 |
17 | static SCPinAppearance *appearance;
18 |
19 |
20 | @interface SCPinViewController ()
21 | @property (nonatomic, strong) SCPinAppearance *appearance;
22 |
23 | @property (weak, nonatomic) IBOutlet UIButton *cancelButton;
24 | @property (weak, nonatomic) IBOutlet UIButton *touchIDButton;
25 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel;
26 | @property (weak, nonatomic) IBOutlet UILabel *supportLabel;
27 | @property (strong, nonatomic) IBOutletCollection(SCPinButton) NSArray *numberButtons;
28 | @property (weak, nonatomic) IBOutlet UIButton *deleteButton;
29 | @property (weak, nonatomic) IBOutlet UIView *viewForPins;
30 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *viewForPinsLayoutConstraint;
31 |
32 | @property (nonatomic, strong) NSString *currentPin;
33 | @property (nonatomic, strong) NSString *pinToConfirm;
34 | @property (nonatomic, assign) BOOL enable;
35 | @property (nonatomic, assign) BOOL touchIDPassedValidation;
36 | @end
37 |
38 | @implementation SCPinViewController
39 |
40 | #pragma mark - Public
41 |
42 | + (SCPinAppearance *)appearance {
43 | return appearance;
44 | }
45 |
46 | + (void)setNewAppearance:(SCPinAppearance *)newAppearance {
47 | appearance = newAppearance;
48 | }
49 |
50 | #pragma mark - initialization
51 |
52 | - (instancetype)initWithScope:(SCPinViewControllerScope)scope {
53 | NSBundle *budle = [NSBundle bundleForClass:[SCPinViewController class]];
54 | NSLog(@"%@", budle);
55 | self = [super initWithNibName:NSStringFromClass([SCPinViewController class]) bundle:budle];
56 | if (self) {
57 | _scope = scope;
58 | if (!appearance) {
59 | appearance = [SCPinAppearance defaultAppearance];
60 | }
61 | _appearance = appearance;
62 |
63 | }
64 | return self;
65 | }
66 |
67 | #pragma mark - View life cycle
68 |
69 | - (void)viewDidLoad {
70 | [super viewDidLoad];
71 | [self configureView];
72 |
73 | [self setupInitialState];
74 | [self clearCurrentPin];
75 | [self createPinView];
76 |
77 | }
78 |
79 | -(void)setupInitialState {
80 | self.enable = YES;
81 | self.touchIDPassedValidation = NO;
82 |
83 | if ([self.dataSource respondsToSelector:@selector(hideTouchIDButtonIfFingersAreNotEnrolled)]) {
84 | BOOL hideTouchIDButton = [self.dataSource hideTouchIDButtonIfFingersAreNotEnrolled];
85 | NSError *error = nil;
86 | LAContext *context = [[LAContext alloc] init];
87 | if (hideTouchIDButton) {
88 | if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
89 | [self.touchIDButton setHidden:NO];
90 | } else {
91 | [self.touchIDButton setHidden:YES];
92 | }
93 | } else {
94 | [self.touchIDButton setHidden:NO];
95 | }
96 | }
97 |
98 | if (!self.touchIDButton.isHidden) {
99 | self.touchIDButton.hidden = !appearance.touchIDButtonEnabled;
100 | }
101 | if ([self.dataSource respondsToSelector:@selector(showTouchIDVerificationImmediately)]) {
102 | BOOL immediately = [self.dataSource showTouchIDVerificationImmediately];
103 | if (immediately) {
104 | [self touchIDVerification];
105 | }
106 | }
107 | if (self.scope == SCPinViewControllerScopeCreate) {
108 | [self.touchIDButton setHidden:YES];
109 | }
110 |
111 |
112 | }
113 |
114 | -(void)configureView {
115 | for (SCPinButton *button in self.numberButtons) {
116 | [button setTintColor:_appearance.numberButtonColor];
117 | [button setTitleColor:_appearance.numberButtonTitleColor forState:UIControlStateNormal];
118 | [button setTitleColor:_appearance.numberButtonTitleColor forState:UIControlStateSelected];
119 | [button setTitleColor:_appearance.numberButtonTitleColor forState:UIControlStateHighlighted];
120 | button.strokeColor = _appearance.numberButtonStrokeColor;
121 | button.strokeWidth = _appearance.numberButtonStrokeWitdh;
122 | button.strokeColor = _appearance.numberButtonstrokeEnabled ? [_appearance numberButtonStrokeColor] : [UIColor clearColor];
123 | [button.titleLabel setFont:_appearance.numberButtonFont];
124 | [button setNeedsDisplay];
125 | }
126 |
127 | NSBundle *bundle = [NSBundle bundleForClass:[SCPinViewController class]];
128 |
129 | UIImage *deleteButtonImage = [UIImage imageNamed:@"sc_img_delete" inBundle:bundle compatibleWithTraitCollection:nil];
130 | [self.deleteButton setImage:deleteButtonImage forState:UIControlStateNormal];
131 |
132 | UIImage *touchIDButtonImage = [UIImage imageNamed:@"sc_img_touchid" inBundle:bundle compatibleWithTraitCollection:nil];
133 | [self.touchIDButton setImage:touchIDButtonImage forState:UIControlStateNormal];
134 |
135 | [self.deleteButton setTintColor:_appearance.deleteButtonColor];
136 | [self.touchIDButton setTintColor:_appearance.touchIDButtonColor];
137 |
138 | self.titleLabel.text = _appearance.titleText;
139 | self.titleLabel.font = _appearance.titleTextFont;
140 | self.titleLabel.textColor = _appearance.titleTextColor;
141 |
142 | self.supportLabel.text = _appearance.supportText;
143 | self.supportLabel.font = _appearance.supportTextFont;
144 | self.supportLabel.textColor = _appearance.supportTextColor;
145 |
146 | self.touchIDButton.hidden = !appearance.touchIDButtonEnabled;
147 |
148 | self.cancelButton.hidden = !appearance.cancelButtonEnabled;
149 | [self.cancelButton setTitle:_appearance.cancelButtonText forState:UIControlStateNormal];
150 | [self.cancelButton setTitleColor:_appearance.cancelButtonTextColor forState:UIControlStateNormal];
151 | self.cancelButton.titleLabel.font = _appearance.cancelButtonTextFont;
152 | }
153 |
154 |
155 | -(void)clearCurrentPin {
156 | self.currentPin = @"";
157 | }
158 |
159 | - (void)didReceiveMemoryWarning {
160 | [super didReceiveMemoryWarning];
161 | }
162 |
163 |
164 | -(void)viewWillLayoutSubviews {
165 | [super viewWillLayoutSubviews];
166 | [self createPinView];
167 |
168 | }
169 |
170 | -(void)viewDidLayoutSubviews {
171 | [super viewDidLayoutSubviews];
172 | }
173 |
174 | #pragma mark - Controller logic
175 |
176 | -(void)createPinView {
177 | NSInteger length = 0;
178 | NSInteger currentPinLength = [self.currentPin length];
179 | switch (self.scope) {
180 | case SCPinViewControllerScopeCreate:
181 | case SCPinViewControllerScopeConfirm:{
182 | length = [self.createDelegate lengthForPin];
183 | break;
184 | }
185 | case SCPinViewControllerScopeValidate: {
186 | NSString *pin = [self.dataSource codeForPinViewController:self];
187 | length = [pin length];
188 | break;
189 | }
190 | default:
191 | break;
192 | }
193 |
194 | CGFloat width = _appearance.pinSize.width;
195 | CGFloat distance = 15.0f;
196 |
197 | CGFloat pinWidth = (length * width) + ((length - 1) * distance);
198 | CGFloat center = CGRectGetWidth(self.viewForPins.frame) / 2;
199 | CGFloat x = center - (pinWidth / 2);
200 |
201 | for (UIView *view in self.viewForPins.subviews) {
202 | [view removeFromSuperview];
203 | }
204 |
205 | for (NSInteger index = 0; index index) {
250 | self.self.currentPin = [self.currentPin substringToIndex:index];
251 | }
252 | [self createPinView];
253 | }
254 |
255 | -(void)setCurrentPin:(NSString *)currentPin {
256 | _currentPin = currentPin;
257 |
258 | switch (self.scope) {
259 | case SCPinViewControllerScopeValidate:{
260 | BOOL equalLength = ([currentPin length] == [[self.dataSource codeForPinViewController:self] length]);
261 | if (equalLength) {
262 | [self validatePin:currentPin];
263 | }
264 | break;
265 | }
266 | case SCPinViewControllerScopeCreate: {
267 | if ([currentPin length] == [self.createDelegate lengthForPin]) {
268 | // [self.createDelegate pinViewController:self didSetNewPin:currentPin];
269 | _scope = SCPinViewControllerScopeConfirm;
270 | _titleLabel.text = _appearance.confirmText;
271 | _pinToConfirm = _currentPin;
272 | [self clearCurrentPin];
273 | [self createPinView];
274 | }
275 | break;
276 | }
277 | case SCPinViewControllerScopeConfirm: {
278 | if ([currentPin length] == [self.createDelegate lengthForPin]) {
279 |
280 | if ([_pinToConfirm isEqualToString:_currentPin]) {
281 | [self.createDelegate pinViewController:self didSetNewPin:currentPin];
282 | } else {
283 | // wrong confirmation pin
284 | [self wrongConfirmPin];
285 | }
286 |
287 |
288 | }
289 | break;
290 | }
291 | default:
292 | break;
293 | }
294 | }
295 |
296 | -(void)validatePin:(NSString *)pin {
297 | if ([[self.dataSource codeForPinViewController:self] isEqualToString:pin]) {
298 | [self correctPin];
299 |
300 | } else {
301 | [self wrongPin];
302 | }
303 | }
304 |
305 | -(void)correctPin {
306 | if ([self.validateDelegate respondsToSelector:@selector(pinViewControllerDidSetСorrectPin:)]) {
307 | [self.validateDelegate pinViewControllerDidSetСorrectPin:self];
308 | }
309 | }
310 |
311 | - (void)wrongPin {
312 | __weak SCPinViewController *weakSelf = self;
313 | self.enable = NO;
314 | NSTimeInterval delay = 0.25f;
315 | dispatch_time_t delayInSeconds = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
316 | dispatch_after(delayInSeconds, dispatch_get_main_queue(), ^(void){
317 | __strong SCPinViewController *strongSelf = weakSelf;
318 | AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
319 | CAAnimation * shake = [self makeShakeAnimation];
320 | [strongSelf.viewForPins.layer addAnimation:shake forKey:@"shake"];
321 | if ([strongSelf.validateDelegate respondsToSelector:@selector(pinViewControllerDidSetWrongPin:)]) {
322 | [strongSelf.validateDelegate pinViewControllerDidSetWrongPin:strongSelf];
323 | }
324 | [strongSelf clearCurrentPin];
325 | [strongSelf createPinView];
326 | strongSelf.enable = YES;
327 | });
328 |
329 | }
330 |
331 | - (void)wrongConfirmPin {
332 | __weak SCPinViewController *weakSelf = self;
333 | self.enable = NO;
334 | NSTimeInterval delay = 0.25f;
335 | dispatch_time_t delayInSeconds = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
336 | dispatch_after(delayInSeconds, dispatch_get_main_queue(), ^(void){
337 | __strong SCPinViewController *strongSelf = weakSelf;
338 | AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
339 | CAAnimation * shake = [self makeShakeAnimation];
340 | [strongSelf.viewForPins.layer addAnimation:shake forKey:@"shake"];
341 | [strongSelf clearCurrentPin];
342 | [strongSelf createPinView];
343 | strongSelf.enable = YES;
344 | });
345 |
346 | }
347 |
348 | - (CAAnimation *)makeShakeAnimation {
349 | CAKeyframeAnimation * shake = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"];
350 | [shake setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
351 | [shake setDuration:0.5f];
352 | [shake setValues:@[ @(-20), @(20), @(-15), @(15), @(-10), @(10), @(-5), @(5), @(0) ]];
353 | return shake;
354 | }
355 |
356 |
357 | - (void)touchIDVerification {
358 |
359 | NSError * error = nil;
360 | LAContext * context = [[LAContext alloc] init];
361 |
362 | if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
363 | __weak SCPinViewController *weakSelf = self;
364 | [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
365 | localizedReason:_appearance.touchIDVerification
366 | reply:^(BOOL success, NSError * authenticationError) {
367 | if (success) {
368 | __strong SCPinViewController *strongSelf = weakSelf;
369 |
370 | dispatch_async(dispatch_get_main_queue(), ^{
371 | strongSelf.touchIDPassedValidation = YES;
372 | [strongSelf createPinView];
373 | if ([strongSelf.validateDelegate respondsToSelector:@selector(pinViewControllerDidSetСorrectPin:)]) {
374 | [strongSelf.validateDelegate pinViewControllerDidSetСorrectPin:strongSelf];
375 | }
376 | });
377 | } else {
378 | NSLog(@"Authentication Error: %@", authenticationError);
379 | }
380 | }];
381 | } else {
382 | NSLog(@"Policy Error: %@", [error localizedDescription]);
383 | }
384 |
385 | }
386 |
387 | #pragma mark - Actions
388 |
389 | - (IBAction)tapPinButtonAction:(SCPinButton *)sender {
390 | if (!self.enable) {
391 | return;
392 | }
393 | NSInteger number = [sender tag];
394 | [self appendingPincode:[@(number) description]];
395 | AudioServicesPlaySystemSound(0x450);
396 |
397 | }
398 |
399 | - (IBAction)touchIDButtonAction:(UIButton *)sender {
400 | [self touchIDVerification];
401 | }
402 |
403 | - (IBAction)deleteButtonAction:(UIButton *)sender {
404 | [self removeLastPincode];
405 | AudioServicesPlaySystemSound(0x450);
406 |
407 | }
408 |
409 | - (IBAction)cancelButtonAction:(id)sender {
410 |
411 | if ((self.scope == SCPinViewControllerScopeCreate || self.scope == SCPinViewControllerScopeConfirm) && [self.createDelegate respondsToSelector:@selector(pinViewControllerDidCancel:)]) {
412 | [self.createDelegate pinViewControllerDidCancel:self];
413 | }
414 |
415 | if (self.scope == SCPinViewControllerScopeValidate && [self.validateDelegate respondsToSelector:@selector(pinViewControllerDidCancel:)]) {
416 | [self.validateDelegate pinViewControllerDidCancel:self];
417 | }
418 | }
419 |
420 | @end
421 |
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/SCPinViewController.xib:
--------------------------------------------------------------------------------
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 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/View/PinButton/SCPinButton.h:
--------------------------------------------------------------------------------
1 | //
2 | // SCPinButton.h
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 15.07.16.
6 | // Copyright © 2016 Sugar and Candy. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SCPinButton : UIButton
12 | @property (nonatomic, strong) UIColor *strokeColor;
13 | @property (nonatomic, assign) CGFloat strokeWidth;
14 |
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/View/PinButton/SCPinButton.m:
--------------------------------------------------------------------------------
1 | //
2 | // SCPinButton.m
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 15.07.16.
6 | // Copyright © 2016 Sugar and Candy. All rights reserved.
7 | //
8 |
9 | #import "SCPinButton.h"
10 |
11 |
12 | @implementation SCPinButton
13 |
14 | - (void)setHighlighted:(BOOL)highlighted {
15 | if (super.highlighted != highlighted) {
16 | super.highlighted = highlighted;
17 |
18 | [self setNeedsDisplay];
19 | }
20 | }
21 |
22 | -(void)setStrokeColor:(UIColor *)strokeColor {
23 | _strokeColor = strokeColor;
24 | [self setNeedsDisplay];
25 | }
26 |
27 | -(void)setStrokeWidth:(CGFloat)strokeWidth {
28 | _strokeWidth = strokeWidth;
29 | [self setNeedsDisplay];
30 | }
31 |
32 | - (void)drawRect:(CGRect)rect {
33 | // Drawing code
34 | [super drawRect:rect];
35 | /*
36 | @property (nonatomic, strong) UIColor *strokeColor;
37 | @property (nonatomic, assign) CGFloat strokeWidth;
38 |
39 | */
40 | CGFloat height = CGRectGetHeight(rect);
41 | CGRect inset = CGRectInset(CGRectMake(0, 0, height, height), 1, 1);
42 |
43 | CGContextRef context = UIGraphicsGetCurrentContext();
44 |
45 | CGColorRef colorRef = [self tintColor].CGColor;
46 | CGColorRef strokecolorRef = [self strokeColor].CGColor;
47 | UIControlState state = [self state];
48 |
49 | CGContextSetLineWidth(context, self.strokeWidth);
50 | if (state == UIControlStateHighlighted) {
51 | CGContextSetFillColorWithColor(context, colorRef);
52 | CGContextFillEllipseInRect (context, inset);
53 | CGContextFillPath(context);
54 | }
55 | else {
56 | CGContextSetStrokeColorWithColor(context, strokecolorRef);
57 | CGContextAddEllipseInRect(context, inset);
58 | CGContextStrokePath(context);
59 | }
60 | }
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/View/PinView/SCPinView.h:
--------------------------------------------------------------------------------
1 | //
2 | // SCPinView.h
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 16.07.16.
6 | // Copyright © 2016 Sugar and Candy. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SCPinView : UIView
12 | @property (nonatomic, strong) UIColor *fillColor;
13 | @property (nonatomic, strong) UIColor *highlightedColor;
14 | @property (nonatomic, strong) UIColor *strokeColor;
15 | @property (nonatomic, assign) CGFloat strokeWidth;
16 | @property (nonatomic, assign) BOOL highlighted;
17 | @end
18 |
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/View/PinView/SCPinView.m:
--------------------------------------------------------------------------------
1 | //
2 | // SCPinView.m
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 16.07.16.
6 | // Copyright © 2016 Sugar and Candy. All rights reserved.
7 | //
8 |
9 | #import "SCPinView.h"
10 |
11 | @implementation SCPinView
12 | -(instancetype)init {
13 | self = [super init];
14 | if (self) {
15 | [self applyDefautlAttributes];
16 | }
17 | return self;
18 | }
19 |
20 | -(instancetype)initWithCoder:(NSCoder *)aDecoder {
21 | self = [super initWithCoder:aDecoder];
22 | if (self) {
23 | [self applyDefautlAttributes];
24 | }
25 | return self;
26 | }
27 |
28 | -(instancetype)initWithFrame:(CGRect)frame {
29 | self = [super initWithFrame:frame];
30 | if (self) {
31 | [self applyDefautlAttributes];
32 | }
33 | return self;
34 | }
35 |
36 | -(void)applyDefautlAttributes {
37 | self.backgroundColor = [UIColor clearColor];
38 | self.fillColor = [UIColor redColor];
39 | self.strokeColor = [UIColor blueColor];
40 | self.strokeWidth = 0.5f;
41 | self.highlighted = YES;
42 | self.highlightedColor = [UIColor greenColor];
43 | }
44 |
45 | - (void)drawRect:(CGRect)rect {
46 |
47 | CGRect bezierPathRect = CGRectMake(self.strokeWidth, self.strokeWidth, CGRectGetWidth(rect) - (self.strokeWidth * 2), CGRectGetHeight(rect) - (self.strokeWidth * 2));
48 | UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: bezierPathRect];
49 | if (self.highlighted) {
50 | [self.highlightedColor setFill];
51 | } else {
52 | [self.fillColor setFill];
53 | }
54 | [ovalPath fill];
55 | [self.strokeColor setStroke];
56 | ovalPath.lineWidth = self.strokeWidth;
57 | [ovalPath stroke];
58 | }
59 |
60 |
61 | @end
62 |
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/images/sc_img_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarAndCandy/SCPinViewController/0a9ff6bdc6917961a9f3347bf27f6249be73e696/SCPinViewController/SCPinViewController/images/sc_img_delete.png
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/images/sc_img_delete@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarAndCandy/SCPinViewController/0a9ff6bdc6917961a9f3347bf27f6249be73e696/SCPinViewController/SCPinViewController/images/sc_img_delete@2x.png
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/images/sc_img_delete@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarAndCandy/SCPinViewController/0a9ff6bdc6917961a9f3347bf27f6249be73e696/SCPinViewController/SCPinViewController/images/sc_img_delete@3x.png
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/images/sc_img_touchid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarAndCandy/SCPinViewController/0a9ff6bdc6917961a9f3347bf27f6249be73e696/SCPinViewController/SCPinViewController/images/sc_img_touchid.png
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/images/sc_img_touchid@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarAndCandy/SCPinViewController/0a9ff6bdc6917961a9f3347bf27f6249be73e696/SCPinViewController/SCPinViewController/images/sc_img_touchid@2x.png
--------------------------------------------------------------------------------
/SCPinViewController/SCPinViewController/images/sc_img_touchid@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarAndCandy/SCPinViewController/0a9ff6bdc6917961a9f3347bf27f6249be73e696/SCPinViewController/SCPinViewController/images/sc_img_touchid@3x.png
--------------------------------------------------------------------------------
/SCPinViewController/Supporting Files/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 15.07.16.
6 | // Copyright © 2016 Sugar and Candy. 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 |
--------------------------------------------------------------------------------
/SCPinViewController/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 15.07.16.
6 | // Copyright © 2016 Sugar and Candy. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/SCPinViewController/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // SCPinViewController
4 | //
5 | // Created by Maxim Kolesnik on 15.07.16.
6 | // Copyright © 2016 Sugar and Candy. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "SCPinViewController.h"
11 | #import "SCPinAppearance.h"
12 |
13 | NSString * const kViewControllerPin = @"kViewControllerPin";
14 |
15 | @interface ViewController ()
16 |
17 | @end
18 |
19 | @implementation ViewController
20 |
21 | - (void)viewDidLoad {
22 | [super viewDidLoad];
23 | }
24 |
25 | - (IBAction)showPinAction:(UIButton *)sender {
26 | SCPinViewController *vc;
27 |
28 | SCPinAppearance *appearance = [SCPinAppearance defaultAppearance];
29 | appearance.numberButtonstrokeEnabled = NO;
30 | appearance.titleText = @"Enter PIN";
31 |
32 | appearance.cancelButtonText = @"Close";
33 |
34 | [SCPinViewController setNewAppearance:appearance];
35 | vc = [[SCPinViewController alloc] initWithScope:SCPinViewControllerScopeValidate];
36 |
37 | vc.dataSource = self;
38 | vc.validateDelegate = self;
39 | [self presentViewController:vc animated:YES completion:nil];
40 | }
41 |
42 | - (IBAction)createPinAction:(UIButton *)sender {
43 | SCPinAppearance *appearance = [SCPinAppearance defaultAppearance];
44 | SCPinViewController *vc;
45 |
46 | appearance.titleText = @"Create PIN";
47 | [SCPinViewController setNewAppearance:appearance];
48 | vc = [[SCPinViewController alloc] initWithScope:SCPinViewControllerScopeCreate];
49 |
50 | vc.createDelegate = self;
51 | UINavigationController *navigation = [[UINavigationController alloc]initWithRootViewController:vc];
52 | UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(cancel)];
53 | [vc.navigationItem setLeftBarButtonItems:@[item]];
54 | [self presentViewController:navigation animated:YES completion:nil];
55 | }
56 |
57 | -(void)cancel {
58 | [self dismissViewControllerAnimated:YES completion:nil];
59 | }
60 |
61 | - (void)didReceiveMemoryWarning {
62 | [super didReceiveMemoryWarning];
63 | }
64 |
65 | -(void)pinViewController:(SCPinViewController *)pinViewController didSetNewPin:(NSString *)pin {
66 | NSLog(@"pinViewController: %@",pinViewController);
67 | [[NSUserDefaults standardUserDefaults] setObject:pin forKey:kViewControllerPin];
68 | [[NSUserDefaults standardUserDefaults] synchronize];
69 | [self dismissViewControllerAnimated:YES completion:nil];
70 | }
71 |
72 | -(NSInteger)lengthForPin {
73 | return 4;
74 | }
75 |
76 | -(NSString *)codeForPinViewController:(SCPinViewController *)pinViewController {
77 | NSString *pin = [[NSUserDefaults standardUserDefaults] objectForKey:kViewControllerPin];
78 | return pin;
79 | }
80 |
81 | -(BOOL)hideTouchIDButtonIfFingersAreNotEnrolled {
82 | return YES;
83 | }
84 |
85 | -(BOOL)showTouchIDVerificationImmediately {
86 | return NO;
87 | }
88 |
89 | -(void)pinViewControllerDidSetWrongPin:(SCPinViewController *)pinViewController {
90 |
91 | }
92 |
93 | -(void)pinViewControllerDidSetСorrectPin:(SCPinViewController *)pinViewController{
94 | [self dismissViewControllerAnimated:YES completion:nil];
95 |
96 | }
97 |
98 | - (void)pinViewControllerDidCancel:(SCPinViewController *)pinViewController {
99 | [self dismissViewControllerAnimated:YES completion:nil];
100 | }
101 |
102 | @end
103 |
--------------------------------------------------------------------------------
/Screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SugarAndCandy/SCPinViewController/0a9ff6bdc6917961a9f3347bf27f6249be73e696/Screen.png
--------------------------------------------------------------------------------