为有牺牲多壮志,敢教日月换新天。

[Swift实际操作]九、完整实例-(8)登录页面:创建自定义表单Row以及控制器

热烈欢迎,请直接点击!!!

进入博主App Store主页,下载使用各个作品!!!

注:博主将坚持每月上线一个新app!!!

本文将为你演示,如何知错自定义的表单行,表单行包含一个输入框和错误提示标签。

用于登录、注册以及其他需要表单的页面。

【New File】->【Cocoa Touch Class】创建新文件【FormRow.swift】

Name:FormRow

Subclass:UIView

Language:Swift

接着开始编写代码,完成自定义表单行的创建。

  1 import UIKit
  2 //定义一个枚举类型,表示表单中的输入框的类型,共5种
  3 enum FieldType
  4 {
  5     //手机
  6     case Tel
  7     //邮箱
  8     case Email
  9     //密码
 10     case Password
 11     //验证码
 12     case VerifyCode
 13     //昵称
 14     case NickName
 15 }
 16 
 17 //定义一个结构体类型
 18 struct RegexHelper
 19 {
 20     //添加一个正则表达式常量
 21     let regex: NSRegularExpression?
 22     //并添加一个初始化方法
 23     init(_ pattern: String)
 24     {
 25         //接着通过一个异常捕捉语句,
 26         do
 27         {
 28             //初始化一个正则表达式
 29             //并设置进行判断时,不区分英文字母的大小写
 30             regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpression.Options.caseInsensitive)
 31         }
 32         catch
 33         {
 34             regex = nil
 35         }
 36     }
 37 
 38     //添加一个方法
 39     //使用正则表达式,对字符串进行匹配,并返回匹配的结果
 40     func match(_ input: String) -> Bool
 41     {
 42         //初始化一个范围常量
 43         let len = input.characters.count
 44         let range = NSMakeRange(0, len)
 45         //使用正则表达式,对字符串进行匹配,并返回匹配的结果
 46         if let matches = regex?.matches(in: input,
 47                                         options: .reportProgress,
 48                                         range: range)
 49         {
 50             return matches.count > 0
 51         }
 52         else
 53         {
 54             return false
 55         }
 56     }
 57 }
 58 
 59 //给当前的类添加一个文本框协议,用来实现对文本框输入时检测
 60 class FormRow: UIView, UITextFieldDelegate {
 61     //初始化一个布尔变量,用来标识文本框的内容是否符合规则
 62     var isPass : Bool = false
 63     //依次初始化4个组件:1、显示文本框的标题
 64     var labelTitle : UILabel!
 65     //依次初始化4个组件:2、显示错误提示信息
 66     var labelError : UILabel!
 67     //依次初始化4个组件:3、输入框
 68     var inputField : UITextField!
 69     //依次初始化4个组件:4、分割线
 70     var line : UIView!
 71     //初始化一个视图控制器属性,
 72     //该属性的类文件将在后面创建
 73     weak var controller : BaseLoginRegViewController!
 74     //第一个属性:表示文本框的默认类型
 75     var valueType : FieldType = FieldType.Tel //0:tel, 1:email, 2:password  3:verifycode  4:username
 76     //第二个属性:表示正确的验证码
 77     var correctVerifyCode = ""
 78     //第三个属性:表示错误提示标签的显示区域
 79     var frameError : CGRect!
 80 
 81     //重写视图类的初始化方法
 82     override init(frame: CGRect) {
 83         super.init(frame: frame)
 84         //初始化一个指定显示区域的标签对象
 85         let frameTitle = CGRect(x: 20, y: 0, width: frame.size.width-40, height: 20)
 86         labelTitle = UILabel(frame: frameTitle)
 87         //设置标签的文字颜色
 88         labelTitle.textColor = .white
 89         //设置标签的字体属性
 90         labelTitle.font = UIFont(name: "PingFang SC", size: 14)
 91         //并将标签添加到自定义视图
 92         self.addSubview(labelTitle)
 93         //初始化一个指定显示区域的文本输入框
 94         let frameInput = CGRect(x: 20, y: 20, width: frame.size.width-40, height: 40)
 95         inputField = UITextField(frame: frameInput)
 96         //设置输入框的代理
 97         inputField.delegate = self
 98         //设置输入框的字体颜色
 99         inputField.textColor = .white
100         //设置输入框的清除按钮的模式
101         inputField.clearButtonMode = .whileEditing
102         //设置输入框的字体属性
103         inputField.font = UIFont(name: "PingFang SC", size: 17)
104         //并将输入框添加到自定义视图
105         self.addSubview(inputField)
106         //初始化一个具有白色背景的视图对象,作为表单行之间的分割线
107         line = UIView(frame: CGRect(x: 20, y: 64, width: frame.size.width-40, height: 1))
108         line.backgroundColor = UIColor.white
109         self.addSubview(line)
110         //初始化一个标签对象,用来在用户输入错误时显示提示信息
111         frameError = CGRect(x: 20, y: 69, width: frame.size.width-40, height: 14)
112         labelError = UILabel(frame: frameError)
113         //设置标签的文字颜色
114         labelError.textColor = UIColor(red: 255.0/255, green: 89.0/255, blue: 95.0/255, alpha: 1.0)
115          //设置标签的字体属性
116         labelError.font = UIFont(name: "PingFang SC", size: 10)!
117         //默认情况下,错误标签处于隐藏状态
118         labelError.isHidden = true
119         self.addSubview(labelError)
120         //添加一个通知,用来检测输入框种的内容发生变化时的事件
121         NotificationCenter.default.addObserver(self,
122                                                selector: #selector(FormRow.enteringContent(_:)),
123                                                name: NSNotification.Name.UITextFieldTextDidChange,
124                                                object: nil)
125     }
126 
127     //添加一个实例方法,用来获得输入框中的文字
128     func getValue() -> String
129     {
130         return inputField.text!
131     }
132 
133     //添加一个方法,用来设置自定义视图中的输入框的类型
134     func setValueType(type : FieldType)
135     {
136         self.valueType = type
137         //当类型为电话号码时
138         if(type == .Tel)
139         {
140             //设置输入框的键盘类型
141             self.inputField.keyboardType = UIKeyboardType.phonePad
142              //设置输入框的标题文字
143             self.labelTitle.text = "电话号码"
144             
145         }
146         //当类型为邮箱时
147         else if(type == .Email)
148         {
149              //设置输入框的键盘类型
150             self.inputField.keyboardType = UIKeyboardType.emailAddress
151             //设置输入框的标题文字
152             self.labelTitle.text = "邮箱地址"
153         }
154         //当类型为密码时
155         else if(type == .Password)
156         {
157              //设置输入框的键盘类型
158             self.inputField.keyboardType = UIKeyboardType.URL
159              //设置密文输入
160             self.inputField.isSecureTextEntry = true
161              //设置输入框的标题文字
162             self.labelTitle.text = "密码"
163         }
164         //当类型为验证码时
165         else if(type == .VerifyCode)
166         {
167             //设置输入框的键盘类型
168             self.inputField.keyboardType = UIKeyboardType.numberPad
169             //设置输入框的标题文字
170             self.labelTitle.text = "验证码"
171         }
172         //当类型为昵称时
173         else if(type == .NickName)
174         {
175             //设置输入框的键盘类型
176             self.inputField.keyboardType = UIKeyboardType.namePhonePad
177             //设置输入框的标题文字
178             self.labelTitle.text = "用户昵称"
179         }
180     }
181 
182     //添加一个方法,用来判断输入框中的内容是否为空
183     func checkIsNotBlank()
184     {
185         if self.getValue() != ""
186         {
187             //当值不为空时,设置布尔属性的值为真
188             self.isPass = true
189             //接着调用控制器对象的校验表单功能
190             self.controller.checkForm()
191         }
192         else
193         {
194             //当值为空时,设置布尔属性的值为真,
195             self.isPass = false
196             //接着调用控制器对象的校验表单功能 
197             self.controller.checkForm()
198         }
199     }
200 
201     //添加一个方法,用来对输入框的内容进行检验,
202     //并返回布尔类型的结果
203     func checkValue() -> Bool
204     {
205         //处理类型为电话号码时的情况
206         if(self.valueType == .Tel)
207         {
208             //设置用来匹配电话号码的正则表达式字符串
209             let pattern = "^1[0-9]{10}$"
210             //并初始化一个正则匹配的结构体对象
211             let matcher = RegexHelper(pattern)
212             //对输入框中的内容进行匹配,
213             if !matcher.match(self.getValue())
214             {
215                 //并返回输入失败时的结果
216                 self.setError(info: "电话号码输入有误")
217                 return false
218             }
219         }
220         //处理类型为电子邮箱时的情况
221         else if(self.valueType == .Email)
222         {
223             //设置用来匹配电子邮箱的正则表达式字符串
224             let pattern = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$"
225             //并初始化一个正则匹配的结构体对象
226             let matcher = RegexHelper(pattern)
227             //对输入框中的内容进行匹配,
228             let value = self.getValue()
229             if !matcher.match(value)
230             {
231                 //并返回输入失败时的结果
232                 self.setError(info: "邮箱地址输入有误")
233                 return false
234             }
235         }
236          //处理类型为密码时的情况
237         else if(self.valueType == .Password)
238         {
239             //设置用来匹配密码的正则表达式字符串
240             let pattern = "^[a-z0-9_-]{6,16}$"
241             //并初始化一个正则匹配的结构体对象
242             let matcher = RegexHelper(pattern)
243              //对输入框中的内容进行匹配,
244             if !matcher.match(self.getValue())
245             {
246                  //并返回输入失败时的结果
247                 self.setError(info: "密码输入有误")
248                 return false
249             }
250         }
251         //处理类型为验证码时的情况   
252         else if(self.valueType == .VerifyCode)
253         {
254              //设置用来匹配验证码的正则表达式字符串
255             let pattern = "^[0-9]{6,6}$"
256             //并初始化一个正则匹配的结构体对象
257             let matcher = RegexHelper(pattern)
258             //对输入框中的内容进行匹配,
259             if !matcher.match(self.getValue())
260             {
261                 //并返回输入失败时的结果
262                 self.setError(info: "验证码输入有误")
263                 return false
264             }
265         }
266         //处理类型为昵称时的情况
267         else if(self.valueType == .NickName)
268         {
269             //设置用来匹配昵称的正则表达式字符串
270             let pattern = "^[0-9_-a-zA-Z\\u4E00-\\u9FA5]{2,100}$"
271             //并初始化一个正则匹配的结构体对象
272             let matcher = RegexHelper(pattern)
273             //对输入框中的内容进行匹配,
274             if !matcher.match(self.getValue())
275             {
276                 //并返回输入失败时的结果
277                 self.setError(info: "用户名称输入有误")
278                 return false
279             }
280         }
281         //当执行到方法的末尾时,表示匹配成功,
282         //此时设置自定义表单行的状态,
283         self.setSuccess()
284         //并返回值为真的结果
285         return true
286     }
287 
288     //添加一个方法,用来设置自定义表单行在匹配错误时的状态
289     func setError(info : String)
290     {
291         //设置错误标签的内容
292         self.isPass = false
293         self.labelError.text = info
294         //并显示错误标签
295         self.labelError.isHidden = false
296         //由于错误标签是通过动画来显示的,
297         //所以在此对错误标签的显示区域和透明度进行初始化
298         let rect = CGRect(x: frameError.origin.x, y: frameError.origin.y - 20, width: frameError.size.width, height: frameError.size.height)
299         self.labelError.frame = rect
300         self.labelError.layer.opacity = 0.0
301         //设置标题颜色
302         self.labelTitle.textColor = .white
303         //设置输入框文字颜色
304         self.inputField.textColor = .white
305         //设置分割线的背景颜色
306         self.line.backgroundColor = .white
307         //使用视图的类方法,开始执行一段动画
308         UIView.beginAnimations("switchToDaLu", context: nil)
309         //并设置动画的时长为0.6秒
310         UIView.setAnimationDuration(0.6)
311         //在动画块中,更改动画标签的透明度
312         self.labelError.layer.opacity = 1.0
313         //在动画块中,更改动画标签的显示区域
314         self.labelError.frame = self.frameError
315         //修改标题颜色
316         self.labelTitle.textColor = self.labelError.textColor
317         //修改输入框文字颜色
318         self.inputField.textColor = self.labelError.textColor
319         //修改分割线的背景颜色
320         self.line.backgroundColor = self.labelError.textColor
321         //以上外观的变化,都以动画的形式,在0.63秒之内完成。
322         UIView.commitAnimations()
323         //最后结束视图的动画块
324         self.controller.checkForm()
325     }
326 
327     //添加一个方法,用来设置自定义表单行在匹配成功时的状态
328     func setSuccess()
329     {
330         //在上一个方法种,是通过视图动画块的方式创建动画的,
331         //在当前方法种,你将通过闭包的方式,创建一个动画。
332         //同时在动画结束时,隐藏错误标签
333         UIView.animate(withDuration: 0.6, delay: 0.0, options: .curveLinear, animations: {
334             
335             self.isPass = true
336             //初始化一个范围对象,作为错误标签的起始位置
337             let rect = CGRect(x: self.frameError.origin.x, y: self.frameError.origin.y - 20, width: self.frameError.size.width, height: self.frameError.size.height)
338             //将错误标签移至起始位置,并设置不透明度为0
339             self.labelError.frame = rect
340             self.labelError.layer.opacity = 0.0
341             //修改标题颜色
342             self.labelTitle.textColor = .white
343             //设置输入框文字颜色
344             self.inputField.textColor = .white
345             //设置分割线的背景颜色
346             self.line.backgroundColor = .white
347             //对控制器种的所有表单行进行校验
348             self.controller.checkForm()
349             
350         }, completion: { finished in
351          //同时在动画结束时,隐藏错误标签
352             self.labelError.isHidden = true
353         })
354     }
355 
356     //添加一个协议方法,用来监听输入框开始编辑的事件
357     func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
358         
359         self.controller.focusedFieldType = self.valueType
360         
361         return true
362     }
363 
364     //继续添加一个来自协议的方法,用来监听输入框的内容发生变化时的事件
365     func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
366         //将输入框右侧的删除图标的颜色进行修改,将其颜色变亮。
367         let count = inputField.subviews.count
368         
369         if count >= 2
370         {
371             let bt = inputField.subviews[1] as! UIButton
372             bt.imageView?.image = bt.imageView?.image?.blendColor(.white, blendMode: .destinationIn)
373         }
374         //当类型为密码时,最多允许输入11个字符
375         var maxNum = 11
376         if(self.valueType == .Password)
377         {
378             maxNum = 16
379         }
380         //当类型为验证码时,最多允许输入6个字符
381         else if(self.valueType == .VerifyCode)
382         {
383             maxNum = 6
384         }
385         //当类型为昵称时,最多允许输入100个字符
386         else if(self.valueType == .NickName)
387         {
388             maxNum = 100
389         }
390         //当类型为邮箱时,最多允许输入100个字符
391         else if(self.valueType == .Email)
392         {
393             maxNum = 100
394         }
395         //获得输入框中已有的内容
396         let txt = textField.text
397         let str = "\(txt!)\(string)"
398         //判断当输入框种的已有内容的长度大于或等于最大值时,
399         //输入框不再接入字符的输入
400         if str.lengthOfBytes(using: .utf8) <= maxNum
401         {
402             return true
403         }
404         else
405         {
406             return false
407         }
408     }
409 
410     //添加一个方法,用来响应通知。
411     //当收到输入框中的内容发生变化的通知时,
412     //对输入框的内容是否为空进行校验
413     func enteringContent(_ notification: Notification?)
414     {
415         self.checkIsNotBlank()
416     }
417     //添加一个必须实现的初始化方法
418     required init?(coder aDecoder: NSCoder) {
419         fatalError("init(coder:) has not been implemented")
420     }
421     
422     //最后添加一个解析方法,
423     //当自定义表单行对象被从内存清除之前,移除刚刚创建的通知。
424     deinit {
425         NotificationCenter.default.removeObserver(self)
426     }
427 }

【New File】->【Cocoa Touch Class】创建新文件【BaseLoginRegViewController.swift】

Name:BaseLoginRegviewController

Subclass:BaseViewController

Language:Swift

接着开始编码,完成基类的创建

 1 import UIKit
 2 
 3 class BaseLoginRegViewController: BaseViewController {
 4     //首先添加一个属性,表示当前处于焦点状态时的表单行的类型。
 5     var focusedFieldType : FieldType = .Password
 6     override func viewDidLoad() {
 7         super.viewDidLoad()
 8         // Do any additional setup after loading the view.
 9         //隐藏顶部的导航栏
10         self.navigationController?.setNavigationBarHidden(true, animated: false)
11         //隐藏左上角的关闭按钮
12         self.dismissBt.isHidden = true
13         //给根视图添加一个手势,当根视图被点击时,隐藏已经打开的键盘
14         self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(BaseLoginRegViewController.hideKeyboard)))
15     }
16 
17     //添加一个方法,来响应根视图的手势事件
18     func hideKeyboard()
19     {
20         self.view.endEditing(true)
21     }
22 
23     //最后添加一个方法,用来对页面中的所有表单进行检验,
24     //该方法将在子类中进行重写
25     func checkForm()
26     {
27         
28     }
29 
30     override func didReceiveMemoryWarning() {
31         super.didReceiveMemoryWarning()
32         // Dispose of any resources that can be recreated.
33     }
34 }

 

posted @ 2018-11-08 09:13  为敢技术  阅读(626)  评论(0编辑  收藏  举报