[Swift4.2實際操作]九、完整例項-(7)登入頁面:建立自定義檢視及相關元件
阿新 • • 發佈:2018-11-05
本文將開始建立登入頁面,首先建立該頁面所需的一些自定義元件:
做為登入按鈕的自定義檢視物件。
在【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) 23bt.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 }