├── .gitignore
├── LICENSE
├── README.md
├── YBInputControl
├── UIView+YBInputControl.h
└── UIView+YBInputControl.m
├── YBInputControlDemo.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── YBInputControlDemo
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
└── AppIcon.appiconset
│ └── Contents.json
├── Base.lproj
├── LaunchScreen.storyboard
└── Main.storyboard
├── Info.plist
├── TestTableViewVC.h
├── TestTableViewVC.m
├── ViewController.h
├── ViewController.m
└── main.m
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | .DS_Store
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 | profile
14 | *.moved-aside
15 | DerivedData
16 | .idea/
17 | *.hmap
18 |
19 | #CocoaPods
20 | Pods
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 indulgeIn
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # YBInputControl
2 |
3 | iOS文本输入控制-框架:轻松实现文本输入字符类型控制、字符长度控制,侵入性小。
4 | ———— 于 2018-3-14 大规模重构
5 |
6 |
7 | # 使用须知
8 |
9 |
10 | 文本输入解决方案和框架原理讲解请看简书:https://www.jianshu.com/p/0e527df5c1ef
11 |
12 | 首先将`UIView+YBInputControl`的`.h.m`文件拖入工程,在需要使用的地方导入`UIView+YBInputControl.h`
13 |
14 |
15 |
16 | # 具体用法
17 |
18 |
19 | 该框架分别在`UITextField`和`UITextView`的分类中拓展了一个属性:
20 |
21 |
@property (nonatomic, strong, nullable) YBInputControlProfile *yb_inputCP;
22 |
23 |
24 | 使用方法就是为该属性赋值。这里通过`YBInputControlProfile`类进行文本控制,控制的具体配置都在该类的属性中。
25 |
26 |
27 | ## UITextField 使用
28 |
29 | ### 常规方法使用:
30 |
31 | YBInputControlProfile *profile = [YBInputControlProfile new];
32 | profile.maxLength = 10;
33 | profile.textControlType = YBTextControlType_excludeInvisible;
34 | [profile addTargetOfTextChange:self action:@selector(textChange:)];
35 | textfield.yb_inputCP = profile;
36 |
37 |
38 | ### 链式语法使用:
39 |
40 | textfield.yb_inputCP = YBInputControlProfile.creat.set_maxLength(10).set_textControlType(YBTextControlType_letter).set_textChanged(^(id obj){
41 | NSLog(@"%@", [obj valueForKey:@"text"]);
42 | });
43 |
44 |
45 | 如你所见,文本变化的回调提供了block闭包形式和添加监听者+SEL的方式。
46 |
47 |
48 | ### 取消功能:
49 |
50 | textfield.yb_inputCP = nil;
51 |
52 |
53 |
54 | ### 设置自己的正则表达式:
55 |
56 | 如果你想使用自己的正则表达式可以使用`YBInputControlProfile`类的这个属性:
57 |
58 | profile.regularStr = @"^[a-z]*$";
59 |
60 |
61 | 若你在设置`textControlType`之后或者唯一设置了`regularStr`,你的正则表达式将会生效,但是你需要注意以下几个问题(当然,若使用`textControlType`配置类型,框架内部会处理下列问题):
62 |
63 | + 注意1:如果不是输入描述性文本的情况下,建议把联想输入关闭(联想输入在输入之前无法监听,无法精确控制输入字符)`textfield.autocorrectionType = UITextAutocorrectionTypeNo;`
64 |
65 | + 注意2:如果不用输入中文,请设置键盘让用户无法切换到中文输入状态(由于中文输入状态自带联想)`textfield.keyboardType = UIKeyboardTypeASCIICapable;`
66 |
67 |
68 | ### 关于侵入性的说明:
69 |
70 | 你仍然可以监听`UITextField`的`delegate`,框架已经做了特殊处理,你可以在任何地方使用:
71 |
72 | textfield.delegate = self;
73 |
74 |
75 | 注意:若实现了如下方法,将覆盖本框架的输入实时限制功能(其他方法可以像往常一样使用):
76 |
77 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
78 | //如果你仍然想要框架的实时判断输入功能,需要调用一个共有函数
79 | return yb_shouldChangeCharactersIn(textField, range, string);
80 | }
81 |
82 |
83 |
84 | ## UITextView 使用
85 |
86 | 若不额外设置`UITextView`的`delegate`属性时,使用和`UITextFiled`一样,不需往下看了。
87 |
88 | 若你配置了`UITextView`的`yb_inputCP`属性过后,仍然想要配置`UITextView`的`delegate`。由于`UITextView`的继承特性等一系列复杂的原因,暂时无法减少对其的侵入性。对于`UITextView`来说,当`delegate`是非自身的情况下,`yb_inputCP`属性和`delegate`属性是互斥的,意味着后配置的会覆盖先配置的,因为配置`yb_inputCP`属性时内部是将`UITextView`的`delegate`设置为自身。
89 |
90 | 若你先配置了`yb_inputCP`,后配置:`textView.delegate = otherTarget;`那么该框架的功能将会失效,若你仍然想使其有效,必须实现如下操作:
91 |
92 | - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
93 | return yb_shouldChangeCharactersIn(textView, range, text);
94 | }
95 | - (void)textViewDidChange:(UITextView *)textView {
96 | yb_textDidChange(textView);
97 | }
98 |
99 |
100 | 若你先配置了`textView.delegate = otherTarget;`,后配置`yb_inputCP`,那`otherTarget`对代理方法的回调将全部失效。
101 |
--------------------------------------------------------------------------------
/YBInputControl/UIView+YBInputControl.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+YBInputControl.h
3 | // YBInputLimitDemo
4 | //
5 | // Created by 杨少 on 2018/3/12.
6 | // Copyright © 2018年 yangbo. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | BOOL yb_shouldChangeCharactersIn(id _Nullable target, NSRange range, NSString * _Nullable string);
14 | void yb_textDidChange(id _Nullable target);
15 |
16 | typedef NS_ENUM(NSInteger, YBTextControlType) {
17 | YBTextControlType_none, //无限制
18 |
19 | YBTextControlType_number, //数字
20 | YBTextControlType_letter, //字母(包含大小写)
21 | YBTextControlType_letterSmall, //小写字母
22 | YBTextControlType_letterBig, //大写字母
23 | YBTextControlType_number_letterSmall, //数字+小写字母
24 | YBTextControlType_number_letterBig, //数字+大写字母
25 | YBTextControlType_number_letter, //数字+字母
26 |
27 | YBTextControlType_excludeInvisible, //去除不可见字符(包括空格、制表符、换页符等)
28 | YBTextControlType_price, //价格(小数点后最多输入两位)
29 | };
30 |
31 |
32 | @interface YBInputControlProfile : NSObject
33 |
34 | /**
35 | 限制输入长度,NSUIntegerMax表示不限制(默认不限制)
36 | */
37 | @property (nonatomic, assign) NSUInteger maxLength;
38 |
39 | /**
40 | 限制输入的文本类型(单选,在内部其实是配置了regularStr属性)
41 | */
42 | @property (nonatomic, assign) YBTextControlType textControlType;
43 |
44 | /**
45 | 限制输入的正则表达式字符串
46 | */
47 | @property (nonatomic, copy, nullable) NSString *regularStr;
48 |
49 | /**
50 | 文本变化回调(observer为UITextFiled或UITextView)
51 | */
52 | @property (nonatomic, copy, nullable) void(^textChanged)(id observe);
53 |
54 | /**
55 | 添加文本变化监听
56 | @param target 方法接收者
57 | @param action 方法(方法参数为UITextFiled或UITextView)
58 | */
59 | - (void)addTargetOfTextChange:(id)target action:(SEL)action;
60 |
61 |
62 |
63 | /**
64 | 链式配置方法(对应属性配置)
65 | */
66 | + (YBInputControlProfile *)creat;
67 | - (YBInputControlProfile *(^)(YBTextControlType type))set_textControlType;
68 | - (YBInputControlProfile *(^)(NSString *regularStr))set_regularStr;
69 | - (YBInputControlProfile *(^)(NSUInteger maxLength))set_maxLength;
70 | - (YBInputControlProfile *(^)(void (^textChanged)(id observe)))set_textChanged;
71 | - (YBInputControlProfile *(^)(id target, SEL action))set_targetOfTextChange;
72 |
73 |
74 |
75 | //键盘索引和键盘类型,当设置了 textControlType 内部会自动配置,当然你也可以自己配置
76 | @property(nonatomic) UITextAutocorrectionType autocorrectionType;
77 | @property(nonatomic) UIKeyboardType keyboardType;
78 |
79 | //取消输入前回调的长度判断
80 | @property (nonatomic, assign, readonly) BOOL cancelTextLengthControlBefore;
81 | //文本变化方法体
82 | @property (nonatomic, strong, nullable, readonly) NSInvocation *textChangeInvocation;
83 |
84 | @end
85 |
86 |
87 | @interface UITextField (YBInputControl)
88 |
89 | @property (nonatomic, strong, nullable) YBInputControlProfile *yb_inputCP;
90 |
91 | @end
92 |
93 |
94 | @interface UITextView (YBInputControl)
95 |
96 | @property (nonatomic, strong, nullable) YBInputControlProfile *yb_inputCP;
97 |
98 | @end
99 |
100 |
101 | NS_ASSUME_NONNULL_END
102 |
103 |
104 |
--------------------------------------------------------------------------------
/YBInputControl/UIView+YBInputControl.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+YBInputControl.m
3 | // YBInputLimitDemo
4 | //
5 | // Created by 杨少 on 2018/3/12.
6 | // Copyright © 2018年 yangbo. All rights reserved.
7 | //
8 |
9 | #import "UIView+YBInputControl.h"
10 | #import
11 |
12 | static const void *key_Profile = &key_Profile;
13 | static const void *key_tempDelegate = &key_tempDelegate;
14 |
15 | static BOOL judgeRegular(NSString *contentStr, NSString *regularStr) {
16 | NSError *error;
17 | NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:regularStr options:0 error:&error];
18 | if (error) {
19 | return YES;
20 | }
21 | NSArray *results = [regex matchesInString:contentStr options:0 range:NSMakeRange(0, contentStr.length)];
22 | return results.count > 0;
23 | }
24 | BOOL yb_shouldChangeCharactersIn(id target, NSRange range, NSString *string) {
25 | if (!target) {
26 | return YES;
27 | }
28 | YBInputControlProfile *profile = objc_getAssociatedObject(target, key_Profile);
29 | if (!profile) {
30 | return YES;
31 | }
32 | //计算若输入成功的字符串
33 | NSString *nowStr = [target valueForKey:@"text"];
34 | NSMutableString *resultStr = [NSMutableString stringWithString:nowStr];
35 | if (string.length == 0) {
36 | [resultStr deleteCharactersInRange:range];
37 | } else {
38 | if (range.length == 0) {
39 | [resultStr insertString:string atIndex:range.location];
40 | } else {
41 | [resultStr replaceCharactersInRange:range withString:string];
42 | }
43 | }
44 | //长度判断
45 | if (profile.maxLength != NSUIntegerMax) {
46 | if (!profile.cancelTextLengthControlBefore && resultStr.length > profile.maxLength) {
47 | return NO;
48 | }
49 | }
50 | //正则表达式匹配
51 | if (resultStr.length > 0) {
52 | if (!profile.regularStr || profile.regularStr.length <= 0) {
53 | return YES;
54 | }
55 | return judgeRegular(resultStr, profile.regularStr);
56 | }
57 | return YES;
58 | }
59 | void yb_textDidChange(id target) {
60 | if (!target) {
61 | return;
62 | }
63 | YBInputControlProfile *profile = objc_getAssociatedObject(target, key_Profile);
64 | if (!profile) {
65 | return;
66 | }
67 | //内容适配
68 | if (profile.maxLength != NSUIntegerMax && [target valueForKey:@"markedTextRange"] == nil) {
69 | NSString *resultText = [target valueForKey:@"text"];
70 | if ([target isKindOfClass:UITextView.class]) {
71 | //如果是 UITextView, 不需要过滤空格,未超过最大限制也不要调用 setText: 方法(setText:会导致光标自动移动到尾部)
72 | if (resultText.length > profile.maxLength) {
73 | [target setValue:[resultText substringToIndex:profile.maxLength] forKey:@"text"];
74 | }
75 | } else if ([target isKindOfClass:UITextField.class]) {
76 | //先内容过滤
77 | if (profile.textControlType == YBTextControlType_excludeInvisible) {
78 | resultText = [[target valueForKey:@"text"] stringByReplacingOccurrencesOfString:@" " withString:@""];
79 | }
80 | //再判断长度
81 | if (resultText.length > profile.maxLength) {
82 | [target setValue:[resultText substringToIndex:profile.maxLength] forKey:@"text"];
83 | } else {
84 | [target setValue:resultText forKey:@"text"];
85 | }
86 | }
87 | }
88 | //回调
89 | if (profile.textChangeInvocation) {
90 | [profile.textChangeInvocation setArgument:&target atIndex:2];
91 | [profile.textChangeInvocation invoke];
92 | }
93 | if (profile.textChanged) {
94 | profile.textChanged(target);
95 | }
96 | }
97 |
98 |
99 |
100 | @interface YBInputControlProfile ()
101 | @property (nonatomic, assign) BOOL cancelTextLengthControlBefore;
102 | @property (nonatomic, strong, nullable) NSInvocation *textChangeInvocation;
103 | @end
104 | @implementation YBInputControlProfile
105 | - (instancetype)init
106 | {
107 | self = [super init];
108 | if (self) {
109 | self.maxLength = NSUIntegerMax;
110 | }
111 | return self;
112 | }
113 | - (void)addTargetOfTextChange:(id)target action:(SEL)action {
114 | NSInvocation *invocation = nil;
115 | if (target && action) {
116 | invocation = [NSInvocation invocationWithMethodSignature:[target methodSignatureForSelector:action]];
117 | invocation.target = target;
118 | invocation.selector = action;
119 | }
120 | self.textChangeInvocation = invocation;
121 | }
122 | - (void)setTextControlType:(YBTextControlType)textControlType {
123 | _textControlType = textControlType;
124 |
125 | switch (textControlType) {
126 | case YBTextControlType_none: {
127 | self.regularStr = @"";
128 | self.keyboardType = UIKeyboardTypeDefault;
129 | self.autocorrectionType = UITextAutocorrectionTypeDefault;
130 | self.cancelTextLengthControlBefore = YES;
131 | }
132 | break;
133 | case YBTextControlType_number: {
134 | self.regularStr = @"^[0-9]*$";
135 | self.keyboardType = UIKeyboardTypeNumberPad;
136 | self.autocorrectionType = UITextAutocorrectionTypeNo;
137 | self.cancelTextLengthControlBefore = NO;
138 | }
139 | break;
140 | case YBTextControlType_letter: {
141 | self.regularStr = @"^[a-zA-Z]*$";
142 | self.keyboardType = UIKeyboardTypeASCIICapable;
143 | self.autocorrectionType = UITextAutocorrectionTypeNo;
144 | self.cancelTextLengthControlBefore = NO;
145 | }
146 | break;
147 | case YBTextControlType_letterSmall: {
148 | self.regularStr = @"^[a-z]*$";
149 | self.keyboardType = UIKeyboardTypeASCIICapable;
150 | self.autocorrectionType = UITextAutocorrectionTypeNo;
151 | self.cancelTextLengthControlBefore = NO;
152 | }
153 | break;
154 | case YBTextControlType_letterBig: {
155 | self.regularStr = @"^[A-Z]*$";
156 | self.keyboardType = UIKeyboardTypeASCIICapable;
157 | self.autocorrectionType = UITextAutocorrectionTypeNo;
158 | self.cancelTextLengthControlBefore = NO;
159 | }
160 | break;
161 | case YBTextControlType_number_letter: {
162 | self.regularStr = @"^[0-9a-zA-Z]*$";
163 | self.keyboardType = UIKeyboardTypeASCIICapable;
164 | self.autocorrectionType = UITextAutocorrectionTypeNo;
165 | self.cancelTextLengthControlBefore = NO;
166 | }
167 | break;
168 | case YBTextControlType_number_letterSmall: {
169 | self.regularStr = @"^[0-9a-z]*$";
170 | self.keyboardType = UIKeyboardTypeASCIICapable;
171 | self.autocorrectionType = UITextAutocorrectionTypeNo;
172 | self.cancelTextLengthControlBefore = NO;
173 | }
174 | break;
175 | case YBTextControlType_number_letterBig: {
176 | self.regularStr = @"^[0-9A-Z]*$";
177 | self.keyboardType = UIKeyboardTypeASCIICapable;
178 | self.autocorrectionType = UITextAutocorrectionTypeNo;
179 | self.cancelTextLengthControlBefore = NO;
180 | }
181 | break;
182 | case YBTextControlType_price: {
183 | NSString *tempStr = self.maxLength == NSUIntegerMax?@"":[NSString stringWithFormat:@"%ld", (unsigned long)self.maxLength];
184 | self.regularStr = [NSString stringWithFormat:@"^(([1-9]\\d{0,%@})|0)(\\.\\d{0,2})?$", tempStr];
185 | self.keyboardType = UIKeyboardTypeDecimalPad;
186 | self.autocorrectionType = UITextAutocorrectionTypeNo;
187 | self.cancelTextLengthControlBefore = NO;
188 | }
189 | break;
190 | case YBTextControlType_excludeInvisible: {
191 | self.regularStr = @"";
192 | self.keyboardType = UIKeyboardTypeDefault;
193 | self.autocorrectionType = UITextAutocorrectionTypeDefault;
194 | self.cancelTextLengthControlBefore = YES;
195 | }
196 | break;
197 | default:
198 | break;
199 | }
200 | }
201 | + (YBInputControlProfile *)creat {
202 | YBInputControlProfile *profile = [YBInputControlProfile new];
203 | return profile;
204 | }
205 | - (YBInputControlProfile * _Nonnull (^)(YBTextControlType))set_textControlType {
206 | return ^YBInputControlProfile* (YBTextControlType type) {
207 | self.textControlType = type;
208 | return self;
209 | };
210 | }
211 | - (YBInputControlProfile * _Nonnull (^)(NSString * _Nonnull))set_regularStr {
212 | return ^YBInputControlProfile* (NSString *regularStr) {
213 | self.regularStr = regularStr;
214 | return self;
215 | };
216 | }
217 | - (YBInputControlProfile * _Nonnull (^)(NSUInteger))set_maxLength {
218 | return ^YBInputControlProfile* (NSUInteger maxLength) {
219 | self.maxLength = maxLength;
220 | return self;
221 | };
222 | }
223 | - (YBInputControlProfile * _Nonnull (^)(void (^ _Nonnull)(id _Nonnull)))set_textChanged {
224 | return ^YBInputControlProfile *(void (^block)(id observe)) {
225 | if (block) {
226 | self.textChanged = ^(id observe) {
227 | block(observe);
228 | };
229 | }
230 | return self;
231 | };
232 | }
233 | - (YBInputControlProfile * _Nonnull (^)(id _Nonnull, SEL _Nonnull))set_targetOfTextChange {
234 | return ^YBInputControlProfile *(id target, SEL action) {
235 | [self addTargetOfTextChange:target action:action];
236 | return self;
237 | };
238 | }
239 | @end
240 |
241 |
242 | @interface YBInputControlTempDelegate : NSObject
243 | @property (nonatomic, weak) id delegate_inside;
244 | @property (nonatomic, weak) id delegate_outside;
245 | @property (nonatomic, strong) Protocol *protocol;
246 | @end
247 | @implementation YBInputControlTempDelegate
248 | - (BOOL)respondsToSelector:(SEL)aSelector {
249 | struct objc_method_description des = protocol_getMethodDescription(self.protocol, aSelector, NO, YES);
250 | if (des.types == NULL) {
251 | return [super respondsToSelector:aSelector];
252 | }
253 | if ([self.delegate_inside respondsToSelector:aSelector] || [self.delegate_outside respondsToSelector:aSelector]) {
254 | return YES;
255 | }
256 | return [super respondsToSelector:aSelector];
257 | }
258 | - (void)forwardInvocation:(NSInvocation *)anInvocation {
259 | SEL sel = anInvocation.selector;
260 | BOOL isResponds = NO;
261 | if ([self.delegate_inside respondsToSelector:sel]) {
262 | isResponds = YES;
263 | [anInvocation invokeWithTarget:self.delegate_inside];
264 | }
265 | if ([self.delegate_outside respondsToSelector:sel]) {
266 | isResponds = YES;
267 | [anInvocation invokeWithTarget:self.delegate_outside];
268 | }
269 | if (!isResponds) {
270 | [self doesNotRecognizeSelector:sel];
271 | }
272 | }
273 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
274 | NSMethodSignature *sig_inside = [self.delegate_inside methodSignatureForSelector:aSelector];
275 | NSMethodSignature *sig_outside = [self.delegate_outside methodSignatureForSelector:aSelector];
276 | NSMethodSignature *result_sig = sig_inside?:sig_outside?:nil;
277 | return result_sig;
278 | }
279 | - (Protocol *)protocol {
280 | if (!_protocol) {
281 | if ([self.delegate_inside isKindOfClass:UITextField.self]) {
282 | _protocol = objc_getProtocol("UITextFieldDelegate");
283 | }
284 | }
285 | return _protocol;
286 | }
287 | @end
288 |
289 |
290 | @implementation UITextField (YBInputControl)
291 | #pragma mark insert logic to selector--setDelegate:
292 | + (void)load {
293 | if ([NSStringFromClass(self) isEqualToString:@"UITextField"]) {
294 | Method m1 = class_getInstanceMethod(self, @selector(setDelegate:));
295 | Method m2 = class_getInstanceMethod(self, @selector(customSetDelegate:));
296 | if (m1 && m2) {
297 | method_exchangeImplementations(m1, m2);
298 | }
299 | }
300 | }
301 | - (void)customSetDelegate:(id)delegate {
302 | @synchronized(self) {
303 | if (objc_getAssociatedObject(self, key_Profile)) {
304 | YBInputControlTempDelegate *tempDelegate = [YBInputControlTempDelegate new];
305 | tempDelegate.delegate_inside = self;
306 | if (self.delegate && delegate == self) {
307 | tempDelegate.delegate_outside = self.delegate;
308 | } else {
309 | tempDelegate.delegate_outside = delegate;
310 | }
311 | [self customSetDelegate:tempDelegate];
312 | objc_setAssociatedObject(self, key_tempDelegate, tempDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
313 | } else {
314 | [self customSetDelegate:delegate];
315 | }
316 | }
317 | }
318 |
319 | #pragma mark getter setter
320 | - (void)setYb_inputCP:(YBInputControlProfile *)yb_inputCP {
321 | @synchronized(self) {
322 | if (yb_inputCP && [yb_inputCP isKindOfClass:YBInputControlProfile.self]) {
323 | objc_setAssociatedObject(self, key_Profile, yb_inputCP, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
324 |
325 | self.delegate = self;
326 | self.keyboardType = yb_inputCP.keyboardType;
327 | self.autocorrectionType = yb_inputCP.autocorrectionType;
328 | yb_inputCP.textChangeInvocation || yb_inputCP.textChanged ? [self addTarget:self action:@selector(textFieldDidChange:) forControlEvents : UIControlEventEditingChanged]:nil;
329 | } else {
330 | objc_setAssociatedObject(self, key_Profile, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
331 | }
332 | }
333 | }
334 | - (YBInputControlProfile *)yb_inputCP {
335 | return objc_getAssociatedObject(self, key_Profile);
336 | }
337 |
338 | #pragma mark UITextFieldDelegate
339 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
340 | UITextRange *selectedRange = [textField markedTextRange];
341 | UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
342 | if (position) return YES;
343 | return yb_shouldChangeCharactersIn(textField, range, string);
344 | }
345 | - (void)textFieldDidChange:(UITextField *)textField {
346 | UITextRange *selectedRange = [textField markedTextRange];
347 | UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
348 | if (position) return;
349 | yb_textDidChange(textField);
350 | }
351 |
352 | @end
353 |
354 |
355 | @implementation UITextView (YBInputControl)
356 |
357 | #pragma mark getter setter
358 | - (void)setYb_inputCP:(YBInputControlProfile *)yb_inputCP {
359 | @synchronized(self) {
360 | if (yb_inputCP && [yb_inputCP isKindOfClass:YBInputControlProfile.self]) {
361 | objc_setAssociatedObject(self, key_Profile, yb_inputCP, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
362 |
363 | self.delegate = self;
364 | self.keyboardType = yb_inputCP.keyboardType;
365 | self.autocorrectionType = yb_inputCP.autocorrectionType;
366 | } else {
367 | objc_setAssociatedObject(self, key_Profile, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
368 | }
369 | }
370 | }
371 | - (YBInputControlProfile *)yb_inputCP {
372 | return objc_getAssociatedObject(self, key_Profile);
373 | }
374 |
375 | #pragma mark UITextViewDelegate
376 | - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
377 | UITextRange *selectedRange = [textView markedTextRange];
378 | UITextPosition *position = [textView positionFromPosition:selectedRange.start offset:0];
379 | if (position) return YES;
380 | return yb_shouldChangeCharactersIn(textView, range, text);
381 | }
382 | - (void)textViewDidChange:(UITextView *)textView {
383 | UITextRange *selectedRange = [textView markedTextRange];
384 | UITextPosition *position = [textView positionFromPosition:selectedRange.start offset:0];
385 | if (position) return;
386 | yb_textDidChange(textView);
387 | }
388 |
389 | @end
390 |
391 |
--------------------------------------------------------------------------------
/YBInputControlDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 7057FA4B2057D29800C7766F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7057FA4A2057D29800C7766F /* AppDelegate.m */; };
11 | 7057FA4E2057D29800C7766F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7057FA4D2057D29800C7766F /* ViewController.m */; };
12 | 7057FA512057D29800C7766F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7057FA4F2057D29800C7766F /* Main.storyboard */; };
13 | 7057FA532057D29800C7766F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7057FA522057D29800C7766F /* Assets.xcassets */; };
14 | 7057FA562057D29800C7766F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7057FA542057D29800C7766F /* LaunchScreen.storyboard */; };
15 | 7057FA592057D29800C7766F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7057FA582057D29800C7766F /* main.m */; };
16 | 708702852058BB1C0095767D /* UIView+YBInputControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 708702842058BB1C0095767D /* UIView+YBInputControl.m */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | 7057FA462057D29800C7766F /* YBInputControlDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YBInputControlDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
21 | 7057FA492057D29800C7766F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
22 | 7057FA4A2057D29800C7766F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
23 | 7057FA4C2057D29800C7766F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
24 | 7057FA4D2057D29800C7766F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
25 | 7057FA502057D29800C7766F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
26 | 7057FA522057D29800C7766F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
27 | 7057FA552057D29800C7766F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
28 | 7057FA572057D29800C7766F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
29 | 7057FA582057D29800C7766F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
30 | 708702832058BB1C0095767D /* UIView+YBInputControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+YBInputControl.h"; sourceTree = ""; };
31 | 708702842058BB1C0095767D /* UIView+YBInputControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+YBInputControl.m"; sourceTree = ""; };
32 | /* End PBXFileReference section */
33 |
34 | /* Begin PBXFrameworksBuildPhase section */
35 | 7057FA432057D29800C7766F /* Frameworks */ = {
36 | isa = PBXFrameworksBuildPhase;
37 | buildActionMask = 2147483647;
38 | files = (
39 | );
40 | runOnlyForDeploymentPostprocessing = 0;
41 | };
42 | /* End PBXFrameworksBuildPhase section */
43 |
44 | /* Begin PBXGroup section */
45 | 7057FA3D2057D29800C7766F = {
46 | isa = PBXGroup;
47 | children = (
48 | 7057FA482057D29800C7766F /* YBInputControlDemo */,
49 | 7057FA472057D29800C7766F /* Products */,
50 | );
51 | sourceTree = "";
52 | };
53 | 7057FA472057D29800C7766F /* Products */ = {
54 | isa = PBXGroup;
55 | children = (
56 | 7057FA462057D29800C7766F /* YBInputControlDemo.app */,
57 | );
58 | name = Products;
59 | sourceTree = "";
60 | };
61 | 7057FA482057D29800C7766F /* YBInputControlDemo */ = {
62 | isa = PBXGroup;
63 | children = (
64 | 708702822058BB1C0095767D /* YBInputControl */,
65 | 7057FA492057D29800C7766F /* AppDelegate.h */,
66 | 7057FA4A2057D29800C7766F /* AppDelegate.m */,
67 | 7057FA4C2057D29800C7766F /* ViewController.h */,
68 | 7057FA4D2057D29800C7766F /* ViewController.m */,
69 | 7057FA4F2057D29800C7766F /* Main.storyboard */,
70 | 7057FA522057D29800C7766F /* Assets.xcassets */,
71 | 7057FA542057D29800C7766F /* LaunchScreen.storyboard */,
72 | 7057FA572057D29800C7766F /* Info.plist */,
73 | 7057FA582057D29800C7766F /* main.m */,
74 | );
75 | path = YBInputControlDemo;
76 | sourceTree = "";
77 | };
78 | 708702822058BB1C0095767D /* YBInputControl */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 708702832058BB1C0095767D /* UIView+YBInputControl.h */,
82 | 708702842058BB1C0095767D /* UIView+YBInputControl.m */,
83 | );
84 | path = YBInputControl;
85 | sourceTree = SOURCE_ROOT;
86 | };
87 | /* End PBXGroup section */
88 |
89 | /* Begin PBXNativeTarget section */
90 | 7057FA452057D29800C7766F /* YBInputControlDemo */ = {
91 | isa = PBXNativeTarget;
92 | buildConfigurationList = 7057FA5C2057D29800C7766F /* Build configuration list for PBXNativeTarget "YBInputControlDemo" */;
93 | buildPhases = (
94 | 7057FA422057D29800C7766F /* Sources */,
95 | 7057FA432057D29800C7766F /* Frameworks */,
96 | 7057FA442057D29800C7766F /* Resources */,
97 | );
98 | buildRules = (
99 | );
100 | dependencies = (
101 | );
102 | name = YBInputControlDemo;
103 | productName = YBInputControlDemo;
104 | productReference = 7057FA462057D29800C7766F /* YBInputControlDemo.app */;
105 | productType = "com.apple.product-type.application";
106 | };
107 | /* End PBXNativeTarget section */
108 |
109 | /* Begin PBXProject section */
110 | 7057FA3E2057D29800C7766F /* Project object */ = {
111 | isa = PBXProject;
112 | attributes = {
113 | LastUpgradeCheck = 0920;
114 | ORGANIZATIONNAME = "杨波";
115 | TargetAttributes = {
116 | 7057FA452057D29800C7766F = {
117 | CreatedOnToolsVersion = 9.2;
118 | ProvisioningStyle = Automatic;
119 | };
120 | };
121 | };
122 | buildConfigurationList = 7057FA412057D29800C7766F /* Build configuration list for PBXProject "YBInputControlDemo" */;
123 | compatibilityVersion = "Xcode 8.0";
124 | developmentRegion = en;
125 | hasScannedForEncodings = 0;
126 | knownRegions = (
127 | en,
128 | Base,
129 | );
130 | mainGroup = 7057FA3D2057D29800C7766F;
131 | productRefGroup = 7057FA472057D29800C7766F /* Products */;
132 | projectDirPath = "";
133 | projectRoot = "";
134 | targets = (
135 | 7057FA452057D29800C7766F /* YBInputControlDemo */,
136 | );
137 | };
138 | /* End PBXProject section */
139 |
140 | /* Begin PBXResourcesBuildPhase section */
141 | 7057FA442057D29800C7766F /* Resources */ = {
142 | isa = PBXResourcesBuildPhase;
143 | buildActionMask = 2147483647;
144 | files = (
145 | 7057FA562057D29800C7766F /* LaunchScreen.storyboard in Resources */,
146 | 7057FA532057D29800C7766F /* Assets.xcassets in Resources */,
147 | 7057FA512057D29800C7766F /* Main.storyboard in Resources */,
148 | );
149 | runOnlyForDeploymentPostprocessing = 0;
150 | };
151 | /* End PBXResourcesBuildPhase section */
152 |
153 | /* Begin PBXSourcesBuildPhase section */
154 | 7057FA422057D29800C7766F /* Sources */ = {
155 | isa = PBXSourcesBuildPhase;
156 | buildActionMask = 2147483647;
157 | files = (
158 | 7057FA4E2057D29800C7766F /* ViewController.m in Sources */,
159 | 7057FA592057D29800C7766F /* main.m in Sources */,
160 | 708702852058BB1C0095767D /* UIView+YBInputControl.m in Sources */,
161 | 7057FA4B2057D29800C7766F /* AppDelegate.m in Sources */,
162 | );
163 | runOnlyForDeploymentPostprocessing = 0;
164 | };
165 | /* End PBXSourcesBuildPhase section */
166 |
167 | /* Begin PBXVariantGroup section */
168 | 7057FA4F2057D29800C7766F /* Main.storyboard */ = {
169 | isa = PBXVariantGroup;
170 | children = (
171 | 7057FA502057D29800C7766F /* Base */,
172 | );
173 | name = Main.storyboard;
174 | sourceTree = "";
175 | };
176 | 7057FA542057D29800C7766F /* LaunchScreen.storyboard */ = {
177 | isa = PBXVariantGroup;
178 | children = (
179 | 7057FA552057D29800C7766F /* Base */,
180 | );
181 | name = LaunchScreen.storyboard;
182 | sourceTree = "";
183 | };
184 | /* End PBXVariantGroup section */
185 |
186 | /* Begin XCBuildConfiguration section */
187 | 7057FA5A2057D29800C7766F /* Debug */ = {
188 | isa = XCBuildConfiguration;
189 | buildSettings = {
190 | ALWAYS_SEARCH_USER_PATHS = NO;
191 | CLANG_ANALYZER_NONNULL = YES;
192 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
193 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
194 | CLANG_CXX_LIBRARY = "libc++";
195 | CLANG_ENABLE_MODULES = YES;
196 | CLANG_ENABLE_OBJC_ARC = YES;
197 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
198 | CLANG_WARN_BOOL_CONVERSION = YES;
199 | CLANG_WARN_COMMA = YES;
200 | CLANG_WARN_CONSTANT_CONVERSION = YES;
201 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
202 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
203 | CLANG_WARN_EMPTY_BODY = YES;
204 | CLANG_WARN_ENUM_CONVERSION = YES;
205 | CLANG_WARN_INFINITE_RECURSION = YES;
206 | CLANG_WARN_INT_CONVERSION = YES;
207 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
208 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
209 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
210 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
211 | CLANG_WARN_STRICT_PROTOTYPES = YES;
212 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
213 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
214 | CLANG_WARN_UNREACHABLE_CODE = YES;
215 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
216 | CODE_SIGN_IDENTITY = "iPhone Developer";
217 | COPY_PHASE_STRIP = NO;
218 | DEBUG_INFORMATION_FORMAT = dwarf;
219 | ENABLE_STRICT_OBJC_MSGSEND = YES;
220 | ENABLE_TESTABILITY = YES;
221 | GCC_C_LANGUAGE_STANDARD = gnu11;
222 | GCC_DYNAMIC_NO_PIC = NO;
223 | GCC_NO_COMMON_BLOCKS = YES;
224 | GCC_OPTIMIZATION_LEVEL = 0;
225 | GCC_PREPROCESSOR_DEFINITIONS = (
226 | "DEBUG=1",
227 | "$(inherited)",
228 | );
229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
231 | GCC_WARN_UNDECLARED_SELECTOR = YES;
232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
233 | GCC_WARN_UNUSED_FUNCTION = YES;
234 | GCC_WARN_UNUSED_VARIABLE = YES;
235 | IPHONEOS_DEPLOYMENT_TARGET = 11.2;
236 | MTL_ENABLE_DEBUG_INFO = YES;
237 | ONLY_ACTIVE_ARCH = YES;
238 | SDKROOT = iphoneos;
239 | };
240 | name = Debug;
241 | };
242 | 7057FA5B2057D29800C7766F /* Release */ = {
243 | isa = XCBuildConfiguration;
244 | buildSettings = {
245 | ALWAYS_SEARCH_USER_PATHS = NO;
246 | CLANG_ANALYZER_NONNULL = YES;
247 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
249 | CLANG_CXX_LIBRARY = "libc++";
250 | CLANG_ENABLE_MODULES = YES;
251 | CLANG_ENABLE_OBJC_ARC = YES;
252 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
253 | CLANG_WARN_BOOL_CONVERSION = YES;
254 | CLANG_WARN_COMMA = YES;
255 | CLANG_WARN_CONSTANT_CONVERSION = YES;
256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
257 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
258 | CLANG_WARN_EMPTY_BODY = YES;
259 | CLANG_WARN_ENUM_CONVERSION = YES;
260 | CLANG_WARN_INFINITE_RECURSION = YES;
261 | CLANG_WARN_INT_CONVERSION = YES;
262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
263 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
264 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
265 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
266 | CLANG_WARN_STRICT_PROTOTYPES = YES;
267 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
268 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
269 | CLANG_WARN_UNREACHABLE_CODE = YES;
270 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
271 | CODE_SIGN_IDENTITY = "iPhone Developer";
272 | COPY_PHASE_STRIP = NO;
273 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
274 | ENABLE_NS_ASSERTIONS = NO;
275 | ENABLE_STRICT_OBJC_MSGSEND = YES;
276 | GCC_C_LANGUAGE_STANDARD = gnu11;
277 | GCC_NO_COMMON_BLOCKS = YES;
278 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
279 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
280 | GCC_WARN_UNDECLARED_SELECTOR = YES;
281 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
282 | GCC_WARN_UNUSED_FUNCTION = YES;
283 | GCC_WARN_UNUSED_VARIABLE = YES;
284 | IPHONEOS_DEPLOYMENT_TARGET = 11.2;
285 | MTL_ENABLE_DEBUG_INFO = NO;
286 | SDKROOT = iphoneos;
287 | VALIDATE_PRODUCT = YES;
288 | };
289 | name = Release;
290 | };
291 | 7057FA5D2057D29800C7766F /* Debug */ = {
292 | isa = XCBuildConfiguration;
293 | buildSettings = {
294 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
295 | CODE_SIGN_STYLE = Automatic;
296 | DEVELOPMENT_TEAM = UU8H9EQ986;
297 | INFOPLIST_FILE = YBInputControlDemo/Info.plist;
298 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
299 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
300 | PRODUCT_BUNDLE_IDENTIFIER = com.yb.YBInputControlDemo;
301 | PRODUCT_NAME = "$(TARGET_NAME)";
302 | TARGETED_DEVICE_FAMILY = "1,2";
303 | };
304 | name = Debug;
305 | };
306 | 7057FA5E2057D29800C7766F /* Release */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
310 | CODE_SIGN_STYLE = Automatic;
311 | DEVELOPMENT_TEAM = UU8H9EQ986;
312 | INFOPLIST_FILE = YBInputControlDemo/Info.plist;
313 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
314 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
315 | PRODUCT_BUNDLE_IDENTIFIER = com.yb.YBInputControlDemo;
316 | PRODUCT_NAME = "$(TARGET_NAME)";
317 | TARGETED_DEVICE_FAMILY = "1,2";
318 | };
319 | name = Release;
320 | };
321 | /* End XCBuildConfiguration section */
322 |
323 | /* Begin XCConfigurationList section */
324 | 7057FA412057D29800C7766F /* Build configuration list for PBXProject "YBInputControlDemo" */ = {
325 | isa = XCConfigurationList;
326 | buildConfigurations = (
327 | 7057FA5A2057D29800C7766F /* Debug */,
328 | 7057FA5B2057D29800C7766F /* Release */,
329 | );
330 | defaultConfigurationIsVisible = 0;
331 | defaultConfigurationName = Release;
332 | };
333 | 7057FA5C2057D29800C7766F /* Build configuration list for PBXNativeTarget "YBInputControlDemo" */ = {
334 | isa = XCConfigurationList;
335 | buildConfigurations = (
336 | 7057FA5D2057D29800C7766F /* Debug */,
337 | 7057FA5E2057D29800C7766F /* Release */,
338 | );
339 | defaultConfigurationIsVisible = 0;
340 | defaultConfigurationName = Release;
341 | };
342 | /* End XCConfigurationList section */
343 | };
344 | rootObject = 7057FA3E2057D29800C7766F /* Project object */;
345 | }
346 |
--------------------------------------------------------------------------------
/YBInputControlDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/YBInputControlDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/YBInputControlDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // YBInputControlDemo
4 | //
5 | // Created by 杨少 on 2018/3/13.
6 | // Copyright © 2018年 杨波. 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 |
--------------------------------------------------------------------------------
/YBInputControlDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // YBInputControlDemo
4 | //
5 | // Created by 杨少 on 2018/3/13.
6 | // Copyright © 2018年 杨波. 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 |
24 | - (void)applicationWillResignActive:(UIApplication *)application {
25 | // 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.
26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
27 | }
28 |
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
31 | // 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.
32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | }
34 |
35 |
36 | - (void)applicationWillEnterForeground:(UIApplication *)application {
37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
38 | }
39 |
40 |
41 | - (void)applicationDidBecomeActive:(UIApplication *)application {
42 | // 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.
43 | }
44 |
45 |
46 | - (void)applicationWillTerminate:(UIApplication *)application {
47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | }
49 |
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/YBInputControlDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | }
88 | ],
89 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/YBInputControlDemo/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 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/YBInputControlDemo/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 |
--------------------------------------------------------------------------------
/YBInputControlDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/YBInputControlDemo/TestTableViewVC.h:
--------------------------------------------------------------------------------
1 | //
2 | // TestTableViewVC.h
3 | // YBInputControlDemo
4 | //
5 | // Created by 杨少 on 2018/5/14.
6 | // Copyright © 2018年 杨波. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TestTableViewVC : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/YBInputControlDemo/TestTableViewVC.m:
--------------------------------------------------------------------------------
1 | //
2 | // TestTableViewVC.m
3 | // YBInputControlDemo
4 | //
5 | // Created by 杨少 on 2018/5/14.
6 | // Copyright © 2018年 杨波. All rights reserved.
7 | //
8 |
9 | #import "TestTableViewVC.h"
10 |
11 | @interface TestTableViewVC ()
12 |
13 | @property (nonatomic, strong) UITableView *tableView;
14 |
15 | @end
16 |
17 | @implementation TestTableViewVC
18 |
19 | - (void)viewDidLoad {
20 | [super viewDidLoad];
21 | [self.view addSubview:self.tableView];
22 | }
23 |
24 | #pragma mark UITableViewDataSource
25 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
26 |
27 | }
28 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
29 | return 44;
30 | }
31 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
32 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"spaceCell"];
33 | if (!cell) {
34 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"spaceCell"];
35 | cell.selectionStyle = UITableViewCellSelectionStyleNone;
36 |
37 | }
38 | return cell;
39 | }
40 |
41 | #pragma mark UITableViewDelegate
42 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
43 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
44 |
45 | }
46 |
47 | #pragma mark getter
48 | - (UITableView *)tableView {
49 | if (!_tableView) {
50 | _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) style:UITableViewStylePlain];
51 | _tableView.delegate = self;
52 | _tableView.dataSource = self;
53 | }
54 | return _tableView;
55 | }
56 |
57 |
58 | @end
59 |
--------------------------------------------------------------------------------
/YBInputControlDemo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // YBInputControlDemo
4 | //
5 | // Created by 杨少 on 2018/3/13.
6 | // Copyright © 2018年 杨波. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/YBInputControlDemo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // YBInputControlDemo
4 | //
5 | // Created by 杨少 on 2018/3/13.
6 | // Copyright © 2018年 杨波. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "UIView+YBInputControl.h"
11 |
12 | @interface ViewController ()
13 |
14 | @end
15 |
16 | @implementation ViewController
17 |
18 | - (void)viewDidLoad {
19 | [super viewDidLoad];
20 |
21 | //以下注意项,框架会自动处理
22 | //* 注意1:如果不是输入描述性文本的情况下,建议把联想输入关闭(联想输入在输入之前无法监听,无法精确控制输入字符)
23 | // .autocorrectionType = UITextAutocorrectionTypeNo;
24 | //* 注意2:如果不用输入中文,请设置键盘让用户无法切换到中文输入状态(由于中文输入状态自带联想)
25 | // .keyboardType = UIKeyboardTypeASCIICapable;
26 |
27 | #pragma mark UITextField 的使用
28 | UITextField *textfield = [UITextField new];
29 | textfield.backgroundColor = [UIColor orangeColor];
30 | textfield.textColor = [UIColor whiteColor];
31 | textfield.frame = CGRectMake(20, 100, [UIScreen mainScreen].bounds.size.width-40, 44);
32 | [self.view addSubview:textfield];
33 |
34 | textfield.delegate = self;
35 | //链式语法使用
36 | textfield.yb_inputCP = YBInputControlProfile.creat.set_maxLength(10).set_textControlType(YBTextControlType_letter).set_textChanged(^(id obj){
37 | NSLog(@"%@", [obj valueForKey:@"text"]);
38 | });
39 |
40 | //常规方法使用
41 | // YBInputControlProfile *profile = [YBInputControlProfile new];
42 | // profile.maxLength = 10;
43 | // profile.textControlType = YBTextControlType_letter;
44 | // [profile addTargetOfTextChange:self action:@selector(textChange:)];
45 | // textfield.yb_inputCP = profile;
46 | // profile.regularStr = @"^[a-z]*$";
47 |
48 | //取消功能
49 | //textfield.yb_inputCP = nil;
50 |
51 | //同样可以按照以往的习惯,设置代理
52 | // textfield.delegate = self;
53 |
54 | //** 特别注意
55 | //在给 textField 设置了非自身的 delegate,若实现了如下方法,将可能覆盖本框架的输入实时限制功能,对应可能会覆盖的函数是:- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; 解决方法是在其中调用框架方法(若没实现该方法就没什么影响)。
56 |
57 |
58 | #pragma mark UITextView 的使用
59 | UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(20, 200, [UIScreen mainScreen].bounds.size.width-40, 300)];
60 | textView.font = [UIFont systemFontOfSize:14];
61 | textView.backgroundColor = [UIColor orangeColor];
62 | textView.textColor = [UIColor whiteColor];
63 | [self.view addSubview:textView];
64 |
65 | textView.yb_inputCP = YBInputControlProfile.creat.set_textControlType(YBTextControlType_letter).set_maxLength(20).set_textChanged(^(id obj){
66 | NSLog(@"%@", [obj valueForKey:@"text"]);
67 | });
68 |
69 | textView.delegate = self;
70 | //** 特别注意
71 | //对 textView 设置 yb_inputCP 和 delegate 互斥,后配置的将取而代之
72 | //在配置了框架属性 yb_inputCP 过后,给 textView 设置了非自身的 delegate ,将直接覆盖本框架的功能,需要在 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text 函数 和 - (void)textViewDidChange:(UITextView *)textView 函数中调用框架方法才能有效。
73 | }
74 |
75 | #pragma mark UITextFieldDelegate
76 | //- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
77 | // //如果你仍然想要框架的实时判断输入功能,需要调用一个共有函数
78 | // return yb_shouldChangeCharactersIn(textField, range, string);
79 | //}
80 | - (void)textFieldDidBeginEditing:(UITextField *)textField {
81 | NSLog(@"%@", NSStringFromSelector(_cmd));
82 | }
83 |
84 | #pragma mark UITextViewDelegate
85 | //- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
86 | // return yb_shouldChangeCharactersIn(textView, range, text);
87 | //}
88 | //- (void)textViewDidChange:(UITextView *)textView {
89 | // yb_textDidChange(textView);
90 | //}
91 | - (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
92 | NSLog(@"%@", NSStringFromSelector(_cmd));
93 | return YES;
94 | }
95 |
96 |
97 | #pragma mark event
98 | - (void)textChange:(id)obj {
99 | NSLog(@"%@", [obj valueForKey:@"text"]);
100 | }
101 |
102 |
103 | @end
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/YBInputControlDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // YBInputControlDemo
4 | //
5 | // Created by 杨少 on 2018/3/13.
6 | // Copyright © 2018年 杨波. 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 |
--------------------------------------------------------------------------------