在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
swift设备管理制作过程(xib自动布局) 该工程git仓库 https://gitee.com/zjf1998/DeviceManage-swift 目录 4.2实现HomeViewController,并使用xib适配 17 2.添加上下左右4个约束0,使得这个tableView布满整个屏幕 24 3.新建一个viewController,同时添加一个webView,同样使其布满整个屏幕 25 4.最后在infoViewController中添加tableViewCell点击事件 25 1.创建ShoppingCartViewController并勾选xib文件 26 2.在xib文件中拖入一个tableView和一个view 27
1. swift工程搭建1.1新建DeviceManager-swift项目首先需要使用Xcode创建DeviceManager-swift工程,选择iOS-Application-SingleViewApp,点击Next: 输入项目名称,组织名称,bundleid,和使用的语言。由于不要需要使用单元测试和CoreDate,所以去掉底部的三个勾,如图配置好以后,点击Finish:
1.2 installpod本项目使用CocoaPod管理第三方框架,所以需要将Pod引入项目。 使用命令行,切换到项目根目录,使用如下命令创建Pod: 此时在工程目录下Pod会创建一个文件,名为Podfile。需工程需要导入的第三方框架就添加在这个文件当中。使用vim编辑Podfile,加入DeviceManage-swift需要使用的第三方库,修改Podfile文件为: # Uncomment the next line to define a global platform for your project platform :ios, '9.0' # Uncomment the next line if you're using Swift or would like to use dynamic frameworks use_frameworks!
target 'DeviceManage-swift' do
# Pods for DeviceManage-swift pod 'AFNetworking' , '~> 3.1.0' pod 'SVProgressHUD' pod 'SDCycleScrollView', '~> 1.73' pod 'SDAutoLayout' pod 'MJRefresh' pod 'JPush' pod 'AMapLocation' pod 'SDWebImage' pod 'SnapKit' pod 'HandyJSON', '~> 4.2.0' pod 'SwiftyJSON' end 依次说明一下这几个第三方库的作用:
保存Podfile后,在Podfile当前目录下执行pod install命令,安装第三方库。 如图所示安装成功后,pod会把第三方库和工程合并到一个workspace中,所以现在需要从DeviceManage-swift.xcworkspace文件打开工程。打开后可以看到集成的第三方库: 需要使用第三方库的时候,只需要导入头文件,直接使用就可以了。项目直接把第三方库交给了CocoaPod管理了。 1.3 整理项目目录一般iOS项目会使用MVC的设计模式进行开发,MVC的设计模式可以降低开发的耦合性、提高组件的重用性、提高可维护性,最重要的是可以使开发者条理清晰。Apple的UIKit等系统库都大量使用了MVC设计模式,所以本项目也将会在MVC的设计模式上进行开发。 首先需要整理项目目录,便于之后的开发和文件的分类。项目按照功能分类,在各个功能下分为三个模块:Model、Controller、View,在DeviceManager目录下创建好空文件夹,如下所示: 这里需要说明一下:
2. 封装AFNetworking2.1 创建网络工具类项目一般都会封装一层业务层的网络框架,这样可以统一请求网络请求接口,记录或打印网络日志,封装请求格式和解析格式等等。本项目底层网络框架采用AFNetworking,对AFNetworking进行了如下的业务封装:
NetworkTool类就是本项目对AFNetworking的一层业务封装,具体代码如下: NetworkTool.swift(SDSY/SDSY/Classes/Tools(工具)/NetworkTool.swift): import Foundation import AFNetworking
// 定义枚举类型 enum HTTPRequestType : Int{ case GET = 0 case POST }
class NetworkTools: AFHTTPSessionManager { // 设计单例 let是线程安全的 static let shareInstance : NetworkTools = { let tools = NetworkTools() tools.responseSerializer.acceptableContentTypes?.insert("text/html") return tools }() }
// 封装请求方法 extension NetworkTools { func request(methodType : HTTPRequestType, urlString : String, parameters : [String : AnyObject], finished :@escaping (_ result : AnyObject?, _ error : Error?)-> ()) { // 1 成功回调 let successCallBack = {(task :URLSessionDataTask, result : Any) in finished(result as AnyObject?, nil) } // 2 失败回调 let failureCallBack = {(task : URLSessionDataTask?, error :Error) in finished(nil, error) }
if methodType == .GET { // get请求
get(urlString, parameters: parameters, progress: nil, success: successCallBack, failure: failureCallBack) }else { // post请求
post(urlString, parameters: parameters, progress: nil, success: successCallBack, failure: failureCallBack) } } } 注意到上面的几个宏定义,需要加入到Const.swift文件中: import UIKit
let BASE_URL = "http://192.168.0.108:8080/DeviceManage/" 对以上宏定义进行解释:
2.2创建Model解析类网络框架已经将请求服务器返回的JSON字符串转换为了NSDictionary,如果我们之间使用NSDictionary来存取数据会十分的不方便。比如:每次读取或者写入时都需要使用Key-Value的方式进行,而Value的类型是id类型,非常的不直观。如果使用对象作为数据模型就会使开发更方便,出错率更低。所以项目需要一个将JSON字符串转化为JSON对象的类。 本项目的数据模型解析类的构造如下:BasaModel类是所有数据模型的父类,BasaModel负责解析JSON字符串为子类的属性。具体实现如下,BaseModel.h(SDSY/SDSY/Classes/Tools(工具)/BaseModel.swift): import UIKit import HandyJSON
class BaseModel: HandyJSON { required init() {
} } 1HandyJSON支持 JSON直接转Model,定义class时,有两点注意: 必须遵循HandyJSON协议 需要实现空的initializer (当然Struct结构体 可以不需要init()) 3. 实现登录模块功能3.1 创建用户信息模型UserInfoModel类User登录使用的Model为UserInfoModel。该Model类封装了如下功能:
UserInfoModel作为一个全局单例,控制用户的登录状态与用户登录信息。 UserInfoModel.swift(SDSY/SDSY/Classes/Login(登录)/Model/UserInfoModel.swift): import Foundation import SwiftyJSON
enum LoginType:Int { case AutoLogin = 1 // 自动登录 case GeneralLogin = 2 // 普通账号密码登录 case NoToken = 3 // 不存在token case PastToken = 4 // token过期 }
class UserInfoModel:NSObject,NSCoding{ // 单例 - 避免重复从沙盒加载归档文件 static var shareInstance = UserInfoModel()
var UserID:Int? var UserName:String? var UserPassword:String?
//获取数据 override init() { super.init()
let fileData = NSMutableData(contentsOfFile: self.savePath()) if fileData != nil { let unarchiver = NSKeyedUnarchiver(forReadingWith: fileData! as Data) let model = unarchiver.decodeObject(forKey: "userKey") as! UserInfoModel UserID = model.UserID UserName = model.UserName UserPassword = model.UserPassword
unarchiver.finishDecoding() } }
//MARK:-自定义构造函数 init(dic:[String:JSON]) { super.init()
UserID = dic["UserID"]?.intValue UserName = dic["UserName"]?.stringValue UserPassword = dic["UserPassword"]?.stringValue }
//归档的方法 func encode(with aCoder: NSCoder) { aCoder.encode(UserID, forKey: "UserID") aCoder.encode(UserName, forKey: "UserName") aCoder.encode(UserPassword, forKey: "UserPassword") }
//解档的方法 required init?(coder aDecoder: NSCoder) { super.init()
UserID = aDecoder.decodeObject(forKey: "UserID") as! Int? UserName = aDecoder.decodeObject(forKey: "UserName") as! String? UserPassword = aDecoder.decodeObject(forKey: "UserPassword") as! String? }
//检查是否已登录 func checkLogin() -> Bool{ // 1. 是否存在token if(UserPassword == nil || UserPassword?.count == 0){ self.loginLog(type: LoginType.NoToken); return false; } // 2. 自动登录成功 self.loginLog(type: LoginType.AutoLogin); return true; }
//输出日志信息 func loginLog(type:LoginType){ var loginType:String = ""
if(type == LoginType.AutoLogin){ // 自动登录 loginType = "自动登录"; }else if(type == LoginType.GeneralLogin) { // 普通 loginType = "账号密码登录"; }else if (type == LoginType.NoToken) { // 不存在token loginType = "不存在token,自动登录失败"; }else if (type == LoginType.PastToken) { // 不存在token loginType = "不存在token,自动登录失败"; } print("-------------------------------------------------") print("-------------------登录服务器信息-------------------") print("-------------------------------------------------") print("登录类型: %@",loginType) if self.UserID != nil { print("expiration: \(self.UserID!)") } if self.UserName != nil { print("stu_id: \(self.UserName!)") } if self.UserPassword != nil { print("token: \(self.UserPassword!)") } print("保存路径: %@",self.savePath()) print("-------------------------------------------------") print("-------------------------------------------------") }
// 保存数据 func saveToFile(model:UserInfoModel){ let data = NSMutableData() let archive = NSKeyedArchiver(forWritingWith: data) archive.encode(model, forKey: "userKey") archive.finishEncoding() data.write(toFile: self.savePath(), atomically: true) }
//获取保存路径 func savePath() ->String { // 1、获得沙盒的根路径 let home = NSHomeDirectory() as NSString // 2、获得Documents路径 let docPath = home.appendingPathComponent("Documents") as NSString // 3、获取文本文件路径 return docPath.appendingPathComponent("UserInfo.plist") as String }
//删除信息 func logout(){ let manager:FileManager = FileManager.default // 1. 是否文件 if(!manager.fileExists(atPath: self.savePath())){ print("没有找到token文件") return }
// 2.删除token信息 do{ try manager.removeItem(atPath: self.savePath()) } catch{ print("登出失败") }
// 3.释放模型 UserInfoModel.shareInstance = UserInfoModel.init() ; } } 3.2 创建登录xib和Controller需要实现的登录页面如下,用户输入学号和密码后,点击登录。APP调用登录接口,服务器根据登录学号与密码,返回相关登录信息,APP将登录信息解析为UserInfoModel模型: 本页面使用xib构建登录页面,Controller用来控制UI细节、网络请求和Model解析,首先创建xib: import UIKit import SwiftyJSON import SVProgressHUD
class LoginViewController: UIViewController,UITextFieldDelegate,UIScrollViewDelegate { // 学号id @IBOutlet weak var idTextField: UITextField! // 密码 @IBOutlet weak var passwdTextField: UITextField! // 登录按钮 @IBOutlet weak var loginBtn: UIButton! // label背景 @IBOutlet weak var labelBackView: UIView!
override func viewDidLoad() { super.viewDidLoad()
// 检查是否已经登录,如果已经登录,则进入主界面 if (UserInfoModel.shareInstance.checkLogin() == true){ // 已登录 self.showMainViewController(); return; } // 调整登录按钮 self.loginBtn.layer.masksToBounds = true self.loginBtn.layer.cornerRadius = 10
// 调整背景View self.labelBackView.layer.masksToBounds = true self.labelBackView.layer.cornerRadius = 10 self.labelBackView.layer.borderWidth = 0.5 self.labelBackView.layer.borderColor = UIColor.gray.cgColor
// textField self.idTextField.delegate = self as UITextFieldDelegate; self.passwdTextField.delegate = self as UITextFieldDelegate;
//测试账号自动填充 self.idTextField.text = "tom"; self.passwdTextField.text = "654321"; }
@IBAction func loginBtnTapped(_ sender: Any) { self.login() }
//登入 func login(){ SVProgressHUD.show()
let params = ["username":self.idTextField.text, "password": self.passwdTextField.text] // 请求参数 let loginUrl = BASE_URL+"loginValidate" // 请求地址
// 使用 AFNetworking 发送POST请求 NetworkTools.shareInstance.request(methodType: .POST, urlString: loginUrl, parameters: params as [String : AnyObject]) { (result : AnyObject?, error : Error?) in
if error != nil { print(error!) SVProgressHUD.dismiss() SVProgressHUD.showError(withStatus: "web接口服务连接失败,请确保主机ip地址是否正确,然后打开tomcat服务器") return } print(result!)
// 使用 SwiftyJSON 解析json -- 这里解析的是 jsonObject // 如果要解析 jsonArray, SwiftyJSON 更加丝滑, 参考 http://www.hangge.com/blog/cache/detail_968.html let json = JSON(result as Any) if let dataDict = json["result"][0].dictionary { // 字典转模型 let user : UserInfoModel = UserInfoModel.init(dic: (dataDict as [String : JSON] )) // 设置用户模型 UserInfoModel.shareInstance.saveToFile(model: user)
self.showMainViewController(); }else{ SVProgressHUD.dismiss() SVProgressHUD.showError(withStatus: "web接口服务连接失败,请确保主机ip地址是否正确,然后打开tomcat服务器") } } }
//跳转到主界面 func showMainViewController(){ let appdelegate = UIApplication.shared.delegate as! AppDelegate appdelegate.window?.rootViewController = MainViewController() }
//UITextFieldDelegate协议 func textFieldShouldReturn(_ textField: UITextField) -> Bool { if (textField.tag == 1) { // 学号textField,点击next,密码textField聚焦 self.passwdTextField.becomeFirstResponder(); }else { // 密码textField,点击return,直接登录 self.login(); self.idTextField.resignFirstResponder(); self.passwdTextField.resignFirstResponder(); } return true; }
//系统回调 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { self.idTextField.resignFirstResponder(); self.passwdTextField.resignFirstResponder(); } } 这里有三点需要注意: 1. 需要导入json解析框架SwiftyJSON import SwiftyJSON 2.MainViewController的父类是UITabBarController,用来管理显示在主屏上的四个模块(首页、资讯、购物车、设置),在之后的章节会详细介绍。目前为了运行起来,可以先创建一个MainViewController类,继承于UITabBarController在/DeviceManage/DeviceManage/Classes目录下。 在App启动时,需要检查用户的登录状态,如果本地有用户登录的Token,并且Token未过期,代表当前用户的登录状态有效,展示主页;如果未能找到本地的用户信息,或者Token已失效,展示登录页面。本项目,将登录状态的检测与跳转的页面交给了LoginViewController类实现。在上面的LoginViewController.m的- (void)viewDidLoad的方法中有判断。所以,APP启动后的rootViewController为LoginViewController,待LoginViewController检查Token后,跳转对应的页面。 删除ViewController类,并在AppDelegate.swift文件中修改- (BOOL)application: didFinishLaunchingWithOptions:方法为: func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { //设置提示框最长显示时间 SVProgressHUD.setMaximumDismissTimeInterval(1.0) //设置导航条点击颜色为橘色 UINavigationBar.appearance().tintColor = UIColor.orange //设置底部tabBar颜色为橘色 UITabBar.appearance().tintColor = UIColor.orange //设置导航栏标题颜色 UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.orange] //初始化窗体,设置第一个启动的Controller window = UIWindow.init(frame: UIScreen.main.bounds) window?.rootViewController = LoginViewController() window?.makeKeyAndVisible() return true } 将rootViewController指向了LoginViewController。运行代码可以看到我们实现的界面了,并且能看见控制台如下输出: 4. 实现首页模块功能4.1 实现TabBar功能在这里需要实现,点击底部的TabBarItem切换四个模块的主要页面(首页、资讯、搜索、设置)。首先创建四个空的类,在之后会实现四个模块的代码: func setupViewControllers(){ // 1.设置首页导航控制器 let homeViewController:HomeViewController = HomeViewController() let homeNavi:UINavigationController = UINavigationController.init(rootViewController: homeViewController) self.addChild(homeNavi)
// 2.设置咨询导航控制器 let infoViewController:InfoViewController = InfoViewController() let infoNavi:UINavigationController = UINavigationController.init(rootViewController: infoViewController) self.addChild(infoNavi)
// 3.设置购物车导航控制器 let shoppingCartViewController:ShoppingCartViewController = ShoppingCartViewController() let shopNavi:UINavigationController = UINavigationController.init(rootViewController: shoppingCartViewController) self.addChild(shopNavi)
// 4.设置我的导航控制器 let mineViewController:MineViewController = MineViewController() let mineNavi:UINavigationController = UINavigationController.init(rootViewController: mineViewController) self.addChild(mineNavi) } 4.2实现HomeViewController,并使用xib适配创建HomeViewController,同时勾选also create xib file
来到HomeViewController.xib,创建一个uiview视图,和一个tableView视图,
3.添加宏判断,适配iphoneX系列 //屏幕尺寸定义和iPhoneX机型判断代码 let SCREEN_WIDTH = UIScreen.main.bounds.size.width let SCREEN_HEIGHT = UIScreen.main.bounds.size.height
//判断是否是ipad let isiPad = (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad) //判断iPhone4系列 let kiPhone4 = ((UIScreen.main.currentMode?.size.equalTo(CGSize.init(width: 640, height: 960)))!) && isiPad //判断iPhone5系列 let kiPhone5 = ((UIScreen.main.currentMode?.size.equalTo(CGSize.init(width: 640, height: 1136)))!) && isiPad //判断iPhone6系列 let kiPhone6 = ((UIScreen.main.currentMode?.size.equalTo(CGSize.init(width: 750, height: 1334)))!) && isiPad //判断iphone6+系列 let kiPhone6Plus = ((UIScreen.main.currentMode?.size.equalTo(CGSize.init(width: 1242, height: 2208)))!) && isiPad //判断iPhoneX let IS_IPHONE_X = ((UIScreen.main.currentMode?.size.equalTo(CGSize.init(width: 1125, height: 2436)))!) && isiPad //判断iPHoneXr let IS_IPHONE_Xr = ((UIScreen.main.currentMode?.size.equalTo(CGSize.init(width: 828, height: 1792)))!) && isiPad //判断iPhoneXs let IS_IPHONE_Xs = ((UIScreen.main.currentMode?.size.equalTo(CGSize.init(width: 1125, height: 2436)))!) && isiPad //判断iPhoneXs Max let IS_IPHONE_Xs_Max = ((UIScreen.main.currentMode?.size.equalTo(CGSize.init(width: 1242, height: 2688)))!) && isiPad
//iPhoneX系列 let Height_StatusBar = ((IS_IPHONE_X == true || IS_IPHONE_Xr == true || IS_IPHONE_Xs == true || IS_IPHONE_Xs_Max == true) ? 44.0 : 20.0) let Height_NavBar = ((IS_IPHONE_X == true || IS_IPHONE_Xr == true || IS_IPHONE_Xs == true || IS_IPHONE_Xs_Max == true) ? 88.0 : 64.0) let Height_TabBar = ((IS_IPHONE_X == true || IS_IPHONE_Xr == true || IS_IPHONE_Xs == true || IS_IPHONE_Xs_Max == true) ? 83.0 : 49.0)
//1.设置顶部轮播图 self.bannerView = SDCycleScrollView.init(frame: CGRect.init(x: 0.0, y: Height_NavBar, width: Double(SCREEN_WIDTH), height: 200.0), delegate: self, placeholderImage: UIImage.init(named: "Error")) 4.创建设备分类按钮ClassifyButton类,借鉴与尚德书院活动分类 ClassifyButton用于显示设备分类(办公设备、生活实践等等),拥有一个居上的icon和一个在下的文字描述: import UIKit
class ClassifyButton: UIButton {
// MARK:- 自定义初始化方法 init(frame : CGRect,title : String,image: UIImage) { super.init(frame: frame) // 设置Btn标题
全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论