[Swift实际操作]九、完整实例,7登录页面:创建自定义视图及相关组件Swift实际操作

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

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

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

本文将开始创建登录页面,首先创建该页面所需的一些自定义组件:

做为登录按钮的自定义视图对象。

在【RegLogin】组的名称上点击鼠标右键,打开右键菜单。

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

Name:RegButton

Subclass:ShadowView

Language:Swift

 1 import UIKit
 2 
 3 class RegButton: ShadowView {
 4     //给类添加一个按钮类型的属性
 5     var bt : UIButton!
 6     //首先重写父类的初始化方法
 7     override init(frame: CGRect) {
 8         super.init(frame: frame)
 9         //设置字第个一视图的背景颜色
10         self.backgroundColor = UIColor.white
11         //设置字第个一视图的圆角半径
12         self.cornerRadius = 4.0
13         //设置字第个一视图的投影半径
14         self.shadowRadius = 2.0
15         //设置投影的偏移
16         self.shadowOffset = CGSize(width: 0, height: 1)
17         //设置阴影的颜色
18         self.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 126.0/255)
19         //初始化一个指定显示区域的按钮对象
20         let btFrame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)
21         //设置按钮的字体
22         bt = UIButton(frame: btFrame)
23         bt.titleLabel?.font = UIFont(name: "PingFang SC", size: 14)!
24         //设置按钮在正常状态的标题颜色
25         bt.setTitleColor(.white, for: .normal)
26         //设置按钮在失效状态的标题颜色
27         bt.setTitleColor(UIColor(red: 230.0/255, green: 230.0/255, blue: 230.0/255, alpha: 1.0), for: .disabled)
28         //设置按钮的标题
29         bt.setTitle("", for: .normal)
30         //设置按钮的背景颜色
31         bt.backgroundColor = .clear
32         //将按钮添加到自定义视图中
33         self.addSubview(bt)
34         //调用自定义视图对象的实例方法,
35         //设置当前视图出于失效状态
36         self.deActive()
37     }
38     //添加一个方法,用来设置当前的自定义视图的状态为激活状态,
39     //从而允许用户点击自动定义视图中的按钮对象
40     func active()
41     {
42         //首先使按钮出于正常状态
43         self.bt.isEnabled = true
44         //按钮正常状态下的背景颜色
45         self.backgroundColor = UIColor(red: 255.0/255, green: 89.0/255, blue: 95.0/255, alpha: 1.0)
46         //正常状态下的标题颜色
47         self.bt.titleLabel?.textColor = UIColor.white
48     }
49     //添加一个方法,用来设置当前的自定义视图的状态为激活状态,
50     //从而不允许用户点击自动定义视图中的按钮对象
51     func deActive()
52     {
53         
54         //首先使按钮出于失效状态
55         self.bt.isEnabled = false
56         //按钮失效状态下的背景颜色
57         self.backgroundColor = UIColor.white
58     }
59     //最后添加一个必须实现的初始化方法
60     required init?(coder aDecoder: NSCoder) {
61         fatalError("init(coder:) has not been implemented")
62     }
63 }

接着创建一个针对图像类的扩展。

【New File】->【Swift File】创建新文件【ExtensionUIImage.swift】

接着开始编写代码,完成对图像类的扩展。

 1 import UIKit
 2 //创建一个图像类的扩展
 3 extension UIImage
 4 {
 5     //添加一个扩展方法,该方法被用于修改文本框右侧的删除图标的颜色
 6     func blendColor(_ color: UIColor, blendMode: CGBlendMode) -> UIImage
 7     {
 8         //获得当前图片的显示区域
 9         let rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
10         //获得当前的图形上下文。
11         //其中第一个参数表示画布的大小
12         //第二个参数表示是否透明图片
13         //第三个参数表示图片的比例
14         UIGraphicsBeginImageContextWithOptions(size, false, scale)
15         //首先将指定的颜色设置为填充色。
16         color.setFill()
17         //然后将填充色填充在指定的区域内。
18         UIRectFill(rect)
19         //最后将指定区域的内容,绘制在当前的图形上下文。
20         draw(in: rect, blendMode: blendMode, alpha: 1.0)
21         //从当前的图形上下文中,获得填充后的图片,并关闭图形上下文。
22         let blendedImage = UIGraphicsGetImageFromCurrentImageContext()
23         UIGraphicsEndImageContext()
24         //最后在方法的末尾,返回图形上下文中获得的图片。
25         return blendedImage!
26     }
27 }

使用键盘上的快捷键【Command】+【N】,继续创建一个类文件。

接着创建一个针对图像类的扩展。

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

Name:UserInfo

Subclass:NSObjecct

Language:Swift

该类将用来在本地存储用户信息

  1 import UIKit
  2 import Foundation
  3 //使类遵循编码协议,它可以将类的实例转化为序列化的数据进行存储。
  4 class UserInfo : NSObject, NSCoding {
  5     //添加属性:表示用户的考试类型
  6     var myKeMu : PaperType = .ACT
  7     //添加属性:用户是否为海外用户
  8     var isHaiWai : Bool = false
  9     //手机号码
 10     var phone : String = ""
 11     //邮箱
 12     var email : String = ""
 13     //密码
 14     var password : String = ""
 15     //昵称
 16     var nickname : String = ""
 17     //验证码
 18     var verifyCode : String = ""
 19     //头像图片
 20     var avatar : UIImage = UIImage()
 21     //是否参加过考试
 22     var isTested : Bool = false
 23     //头像路径
 24     var head_img : String = ""
 25     //年级
 26     var grade : Int = 1
 27     //生日
 28     var birthday = ""
 29     //用户的唯一标志符
 30     var id = 0
 31     //性别
 32     var sex = 0
 33     //老师
 34     var teacher = ""
 35     //用户考试类型
 36     var exam_type = 0
 37     //状态
 38     var status = 0
 39     //当前总考分
 40     var current_score : Int = 0     //相当于服务器中的current_score
 41     //英语
 42     var english : Int = 0           //相当于服务器中的english
 43     //数学
 44     var math : Int = 0              //相当于服务器中的math
 45     //阅读
 46     var reading : Int = 0           //相当于服务器中的reading
 47     //科学
 48     var science : Int = 0           //相当于服务器中的science
 49     //写作
 50     var writing : Int = 0           //相当于服务器中的writing
 51     
 52     var expect_english : Int = 0
 53     var expect_reading : Int = 0
 54     var expect_science : Int = 0
 55     var expect_score : Int = 0
 56     var expect_writing : Int = 0
 57     var expect_math : Int = 0
 58     
 59     //实现协议中的方法
 60     //用来将用户信息进行编码
 61     func encode(with aCoder: NSCoder)
 62     {
 63         //首先对用户的手机、邮箱、密码、昵称、和是否参考过考试等属性进行编码
 64         //并设置对应的键名
 65         aCoder.encode(self.phone, forKey: "phone")
 66         aCoder.encode(self.email, forKey: "email")
 67         aCoder.encode(self.password, forKey: "password")
 68         aCoder.encode(self.nickname, forKey: "nickname")
 69         aCoder.encode(self.isTested, forKey: "isTested")
 70         //接着对另外八个属性进行编码
 71         aCoder.encode(self.head_img, forKey: "head_img")
 72         aCoder.encode(self.grade, forKey: "grade")
 73         aCoder.encode(self.birthday, forKey: "birthday")
 74         aCoder.encode(self.id, forKey: "id")
 75         aCoder.encode(self.sex, forKey: "sex")
 76         aCoder.encode(self.teacher, forKey: "teacher")
 77         aCoder.encode(self.exam_type, forKey: "exam_type")
 78         aCoder.encode(self.status, forKey: "status")
 79         //最后对剩余的六个属性进行编码
 80         aCoder.encode(self.current_score, forKey: "current_score")
 81         aCoder.encode(self.english, forKey: "english")
 82         aCoder.encode(self.math, forKey: "math")
 83         aCoder.encode(self.reading, forKey: "reading")
 84         aCoder.encode(self.science, forKey: "science")
 85         aCoder.encode(self.writing, forKey: "writing")
 86         
 87         aCoder.encode(self.expect_english, forKey: "expect_english")
 88         aCoder.encode(self.expect_reading, forKey: "expect_reading")
 89         aCoder.encode(self.expect_science, forKey: "expect_science")
 90         aCoder.encode(self.expect_score, forKey: "expect_score")
 91         aCoder.encode(self.expect_writing, forKey: "expect_writing")
 92         aCoder.encode(self.expect_math, forKey: "expect_math")
 93     }
 94     //实现协议中的方法,用来将将过编码的用户信息进行解码
 95     required init(coder aDecoder: NSCoder)
 96     {
 97         super.init()
 98         //首先对用户的手机、邮箱、密码、昵称、和是否参考过考试等属性进行解码
 99         self.phone = aDecoder.decodeObject(forKey: "phone") as! String
100         self.email = aDecoder.decodeObject(forKey: "email") as! String
101         self.password = aDecoder.decodeObject(forKey: "password") as! String
102         self.nickname = aDecoder.decodeObject(forKey: "nickname") as! String
103         self.nickname = aDecoder.decodeObject(forKey: "nickname") as! String
104          //接着对另外八个属性进行解码
105         self.head_img = aDecoder.decodeObject(forKey: "head_img") as! String
106         self.grade = aDecoder.decodeInteger(forKey: "grade")
107         self.birthday = aDecoder.decodeObject(forKey: "birthday") as! String
108         self.id = aDecoder.decodeInteger(forKey: "id")
109         self.sex = aDecoder.decodeInteger(forKey: "sex")
110         self.teacher = aDecoder.decodeObject(forKey: "teacher") as! String
111         self.exam_type = aDecoder.decodeInteger(forKey: "exam_type")
112         self.status = aDecoder.decodeInteger(forKey: "status")
113         //然后对剩余的六个属性进行解码
114         self.current_score = aDecoder.decodeInteger(forKey: "current_score")
115         self.english = aDecoder.decodeInteger(forKey: "english")
116         self.math = aDecoder.decodeInteger(forKey: "math")
117         self.reading = aDecoder.decodeInteger(forKey: "reading")
118         self.science = aDecoder.decodeInteger(forKey: "science")
119         self.writing = aDecoder.decodeInteger(forKey: "writing")
120         
121         self.expect_english = aDecoder.decodeInteger(forKey: "expect_english")
122         self.expect_reading = aDecoder.decodeInteger(forKey: "expect_reading")
123         self.expect_science = aDecoder.decodeInteger(forKey: "expect_science")
124         self.expect_score = aDecoder.decodeInteger(forKey: "expect_score")
125         self.expect_writing = aDecoder.decodeInteger(forKey: "expect_writing")
126         self.expect_math = aDecoder.decodeInteger(forKey: "expect_math")
127         
128         
129     }
130     //最后添加一个初始化方法,完成用户信息类的创建
131     override init()
132     {
133         
134     }
135     
136     func setScore(num:Int, score:Int)
137     {
138         switch num {
139             case 0:
140                 self.current_score = score
141             case 1:
142                 self.english = score
143             case 2:
144                 self.math = score
145             case 3:
146                 self.reading = score
147             case 4:
148                 self.science = score
149             default: break
150             
151         }
152     }
153     
154     func setSATScore(num:Int, score:Int)
155     {
156         switch num {
157         case 0:
158             self.reading = score
159         case 1:
160             self.math = score
161         case 2:
162             self.writing = score
163         default: break
164             
165         }
166     }
167 }

使用键盘上的快捷键【Command】+【N】,继续创建一个类文件。

【New File】->【Swift File】创建新文件【DataUtil.swift】

接着开始编写代码,完成对图像类的扩展。

  1 import Foundation
  2 import UIKit
  3 import Alamofire
  4 import Toaster
  5 import PKHUD
  6 import Qiniu
  7 
  8 class DataUtil
  9 {
 10     class func hasLogined() -> Bool
 11     {
 12         return UserDefaults.standard.bool(forKey: "hasLogin")
 13     }
 14     class func setLogined()
 15     {
 16         UserDefaults.standard.set(true, forKey: "hasLogin")
 17         UserDefaults.standard.synchronize()
 18     }
 19     class func setLoginOut()
 20     {
 21         UserDefaults.standard.set(false, forKey: "hasLogin")
 22         UserDefaults.standard.synchronize()
 23     }
 24     
 25     class func hasShowIntro() -> Bool
 26     {
 27         return UserDefaults.standard.bool(forKey: "hasShowIntro")
 28     }
 29     class func setShowInfo()
 30     {
 31         UserDefaults.standard.set(true, forKey: "hasShowIntro")
 32         UserDefaults.standard.synchronize()
 33     }
 34     
 35     class func getTabBarHeight(vc:UIViewController) -> CGFloat
 36     {
 37         return (vc.tabBarController?.tabBar.frame.size.height)!
 38     }
 39     
 40     class func setCrtUser(userInfo : UserInfo)
 41     {
 42         let data = NSMutableData()
 43         let archive = NSKeyedArchiver(forWritingWith: data)
 44         archive.encode(userInfo, forKey: "crtUserInfo")
 45         archive.finishEncoding()
 46         
 47         let filePath = NSHomeDirectory() + "/Documents/crtUserInfo.data"
 48         data.write(toFile: filePath, atomically: true)
 49     }
 50     //添加一个类方法,用来获得归档的用户信息
 51     class func getCrtUser() -> UserInfo
 52     {
 53         //首先获得用户归档文件的存储路径
 54         let filePath = NSHomeDirectory() + "/Documents/crtUserInfo.data"
 55         //然后加载该路径下的文件,并初始化一个键值解码器
 56         let fileData = NSMutableData(contentsOfFile: filePath)
 57         let unarchiver = NSKeyedUnarchiver(forReadingWith: fileData! as Data)
 58         //根据键名,对指定的归档进行解码
 59         let savedUser = unarchiver.decodeObject(forKey: "crtUserInfo") as! UserInfo
 60         unarchiver.finishDecoding()
 61         //最后返回解码后的用户信息对象
 62         return savedUser
 63     }
 64     //接着添加一个类方法,用来设置用户使用游客身份访问应用程序
 65     class func setVisitorLogin(value:Bool)
 66     {
 67         UserDefaults.standard.set(value, forKey: "IsVisitorLogin")
 68         UserDefaults.standard.synchronize()
 69     }
 70     //添加一个类方法,用来判断用户是否为游客身份
 71     class func isVisitorLogin() -> Bool
 72     {
 73         return UserDefaults.standard.bool(forKey: "IsVisitorLogin")
 74     }
 75     //添加一个类方法,用来将头像数据存储到沙盒目录
 76     class func saveAvarta(data:Data)
 77     {
 78         let targetPath:String = NSHomeDirectory() + "/Documents/avarta.png"
 79         try? data.write(to: URL(fileURLWithPath: targetPath))
 80     }
 81      //添加一个类方法,用来读取沙盒中保存的头像
 82     class func getAvarta() -> UIImage?
 83     {
 84         let targetPath:String = NSHomeDirectory() + "/Documents/avarta.png"
 85         
 86         let image = UIImage(contentsOfFile: targetPath)
 87         return image
 88     }
 89     //添加一个类方法,用来给图像视图设置用户的头像
 90     class func setAvartaForImageView(imageView : UIImageView)
 91     {
 92         //首先读取之前保存的用户头像
 93         let localImage = getAvarta()
 94         //判断当读取的图像不为空时,设置图像视图
 95         if(localImage == nil)
 96         {
 97             //如果没有获得用户头像,
 98             let user = getCrtUser()
 99             //则从保存的用户信息中,获取用户头像的远程路径
100             let imageUrl = user.head_img
101             //如果远程路径为空,则设置用户头像为默认图片
102             if(imageUrl != "")
103             {
104                 //如果远程路径不为空,
105                 let url = URL(string: imageUrl)
106                 //则初始化一个指定网址的网络请求
107                 let request = URLRequest(url: url!)
108                 //使用异步的方式,请求远程服务器上的图片资源
109                 NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.main, completionHandler: {(response:URLResponse?, data:Data?,error:Error?)->Void in
110                     //然后返回主线程,对图像视图进行更新
111                     DispatchQueue.main.async(execute: { () -> Void in
112                         let image = UIImage(data: data!)
113                         imageView.image = image
114                     })
115                 })
116             }
117             else
118             {
119                 //则设置用户头像为默认图片
120                 imageView.image = UIImage(named: "defaultAvarta")
121             }
122         }
123         else
124         {
125             imageView.image = localImage
126         }
127     }
128     //添加一个方法,用来将头像上传至七牛服务器
129     class func uploadAvartaToQiNiu(baseUrl: String, avarta: UIImage)
130     {
131         //初始化一个字符串常量,表示网络请求的接口地址
132         let url = "\(baseUrl)iOS/uploadAvarta.json"
133         //然后开始显示加载动画,并设置弹出框的字体样式。
134         HUD.show(.progress)
135         ToastView.appearance().font = UIFont(name: "PingFang SC", size: 14)!
136         //使用第三方类库,访问指定的网址,并返回Json格式的结果
137         Alamofire.request(url, method: .get).responseJSON
138             { 
139                 //如果返回成功的结果
140                 //则获得返回结果中的编码和详情字段
141                 response in
142                 print(response)
143                 if let json = response.result.value as? [String: Any]
144                 {
145                     print("JSON: \(json)")
146                     let code = json["code"] as? Int ?? 0
147                     let detail = json["detail"] as? [String: Any]
148                     //设置弹出窗口到屏幕底部的距离
149                     ToastView.appearance().bottomOffsetPortrait = 150
150                     //如果返回值为0,则表示网络请求成功,
151                     if(code == 0)
152                     {
153                          //获得服务器返回的将要上传至七牛的图片名称和上传口令。
154                         let upload_name = detail?["upload_name"] as? String ?? ""
155                         //获得服务器返回的将要上传至七牛的上传口令。
156                         let upload_token = detail?["upload_token"] as? String ?? ""
157                         print(upload_token)
158                         //初始化一个七牛上传管理器
159                         let upManager = QNUploadManager()
160                         //并将图片转换为二进制
161                         let data = UIImagePNGRepresentation(avarta)
162                         //调用七牛上传管理器的上传方法,开始图片的上传。
163                         upManager?.put(data, key: upload_name, token: upload_token, complete:{ (info, key, resp) -> Void in
164                             
165                             print(info)
166                             print("----------")
167                             print(resp)
168                             //如果七牛返回的状态码为200,则表示上传成功。
169                             if (info?.statusCode == 200 && resp != nil)
170                             {
171                                 //接着将用户信息进行更新,
172                                 //首先获得已经保存在沙盒中的当前的用户
173                                 let userInfo = DataUtil.getCrtUser()
174                                 //定义一个网络地址,用来获得服务器上的用户信息
175                                 let url = "\(baseUrl)iOS/headImage.json"
176                                 //并设置访问时传递的参数,以更新远程服务器上用户的头像信息
177                                 let parameters = ["head_img": "https://www.cnblogs.com/strengthen/\(upload_name)"]
178                                 //使用第三方类库,访问指定的网址,并返回Json格式的结果
179                                 Alamofire.request(url, method: .get, parameters: parameters).responseJSON
180                                     { 
181                                         response in
182                                         HUD.hide(animated: true)
183                                         //对返回的结果进行判断和处理
184                                         if let json = response.result.value as? [String: Any]
185                                         {
186                                             print("JSON: \(json)")
187                                             //获得服务器返回的状态码
188                                             let code = json["code"] as? Int ?? 0
189                                             //如果状态码为0,则表示请求成功,否则弹出失败提示窗口
190                                             if(code == 0)
191                                             {
192                                                 //显示弹出窗口,提示图片上传成功。
193                                                 ToastView.appearance().bottomOffsetPortrait = 80
194                                                 Toast(text: "Image was successfully uploaded!").show()
195                                                 //然后获得服务器返回的头像路径,
196                                                 //并更新用户信息中的头像路径。
197                                                 let detail = json["detail"] as? [String: Any]
198                                                 userInfo.head_img = detail?["head_img"] as? String ?? ""
199                                                 //最后更新存储在本地的用户信息。
200                                                 DataUtil.setCrtUser(userInfo: userInfo)
201                                                 DataUtil.saveAvarta(data: data!)
202                                             }
203                                             else
204                                             {
205                                                 let detail = json["detail"] as? String ?? ""
206                                                 ToastView.appearance().bottomOffsetPortrait = 80
207                                                 Toast(text: detail).show()
208                                                 
209                                             }
210                                         }
211                                         else
212                                         {
213                                             ToastView.appearance().bottomOffsetPortrait = 80
214                                             Toast(text: "Upload failed!").show()
215                                         }
216                                 }
217                             }
218                             else
219                             {
220                                 HUD.hide(animated: true)
221                             }
222                             
223                         }, option: nil)
224                     }
225                     else
226                     {
227                         //r如果返回编码的值不为零。则提示编码失败
228                         ToastView.appearance().bottomOffsetPortrait = 80
229                         //则打开提示窗口,提示上传失败,
230                         Toast(text: "Upload failed!").show()
231                         //并隐藏加载动画
232                         HUD.hide(animated: true)
233                     }
234                 }
235                 else
236                 {
237                      //如果上传失败,
238                     ToastView.appearance().bottomOffsetPortrait = 80
239                      //则打开提示窗口,提示上传失败,
240                     Toast(text: "Upload failed!").show()
241                       //并隐藏加载动画
242                     HUD.hide(animated: true)
243                 }
244         }
245     }
246 }
247 
248 func getKemuBySection(section: String) -> String
249 {
250     if(section == "english")
251     {
252         return "英语"
253     }
254     else if(section == "math")
255     {
256         return "数学"
257     }
258     else if(section == "reading")
259     {
260         return "阅读"
261     }
262     else if(section == "science")
263     {
264         return "科学"
265     }
266     else if(section == "language")
267     {
268         return "语言"
269     }
270     else if(section == "mathc")
271     {
272         return "数学计算器"
273     }
274     else
275     {
276         return section
277     }
278 }
279 
280 func getLevel(level: String) -> String
281 {
282     if(level == "E")
283     {
284         return "简单"
285     }
286     else if(level == "M")
287     {
288         return "中等"
289     }
290     else
291     {
292         return "困难"
293     }
294 }