swift设备管理制作过程(xib自动布局)

swift设备管理制作过程(xib自动布局)

该工程git仓库 https://gitee.com/zjf1998/DeviceManage-swift

目录

1. swift工程搭建   2

1.1新建DeviceManager-swift项目    2

1.2 installpod  3

1.3 整理项目目录         5

2. 封装AFNetworking  5

2.1 创建网络工具类    5

2.2创建Model解析类 7

3. 实现登录模块功能  8

3.1 创建用户信息模型UserInfoModel类 8

3.2 创建登录xib和Controller    11

4. 实现首页模块功能  16

4.1 实现TabBar功能   16

4.2实现HomeViewController,并使用xib适配       17

5. 实现咨询模块功能  24

1.新建InfoViewController    24

2.添加上下左右4个约束0,使得这个tableView布满整个屏幕 24

3.新建一个viewController,同时添加一个webView,同样使其布满整个屏幕    25

4.最后在infoViewController中添加tableViewCell点击事件  25

6. 实现购物车模块功能      26

1.创建ShoppingCartViewController并勾选xib文件         26

2.在xib文件中拖入一个tableView和一个view      27

3.适配iphoneX界面      28

4.创建UITableViewCell 30

7. HandyJSON的使用    33

8. SwiftyJSON的使用    34

 

 

 

 

 

 

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

       依次说明一下这几个第三方库的作用:

  1. AFNetworking:第三方网络连接库,在本项目中并不会拿AFNetworking直接访问网络,而是将AFNetworking封装以后使用。目的是为了封装BaseURL、封装解析类型、网络请求日志等等,具体封装过程会在之后章节介绍。
  2. SVProgressHUD:loadiong时显示的小圆圈,在耗时的网络请求中会使用SVProgressHUD显示loading界面。
  3. SDCycleScrollView:循环轮播图。如果自己用UIScrollView来实现轮播图,会出现滚动到最后一张图片的时候无法滚动到第一张图,形成循环轮播。想要自己实现也是可以的,但是比较复杂,这里直接使用第三方框架。
  4. SDAutoLayout:自动布局框架。和Masonry类似,SDAutoLayout主要用来布局Cell。底层使用AutoLayout引擎。
  5. MJRefresh:TableView的下拉刷新和上拉加载组件。用在需要手动下拉刷新,获取需要分页的TableView。
  6. DOPDropDownMenu-Enhanced:使用在TableView上,用来显示筛选或排序类型的组件,点击可以显示下拉选项。
  7. JPush:极光推送。一个第三方推送SDK,用来接收推送消息。可以对设备设置别名,分组,从而实现分组或对个别用户进消息推送。
  8. AMapLocation:高德地图定位组件,用于获取设备当前位置,可以根据需求设置定位精度,对设备进行单次定位或是持续定位,获取高德坐标。
  9. SDWebImage:这个类库提供一个UIImageView类别以支持加载来自网络的远程图片。具有缓存管理、异步下载、同一个URL下载次数控制和优化等特征。
  10. SnapKit:一个经典的Swift版的第三方库,专门用于项目的自动布局
  11. HandyJSON:采用Swift反射+内存赋值的方式来构造Model实例,保持原汁原味的Swift类定义。
  12. SwiftyJSON:SwiftyJSON是个使用Swift语言编写的开源库,可以让我们很方便地处理JSON数据(解析数据、生成数据)。

       保存Podfile后,在Podfile当前目录下执行pod install命令,安装第三方库。

       如图所示安装成功后,pod会把第三方库和工程合并到一个workspace中,所以现在需要从DeviceManage-swift.xcworkspace文件打开工程。打开后可以看到集成的第三方库:

       需要使用第三方库的时候,只需要导入头文件,直接使用就可以了。项目直接把第三方库交给了CocoaPod管理了。

1.3 整理项目目录

       一般iOS项目会使用MVC的设计模式进行开发,MVC的设计模式可以降低开发的耦合性、提高组件的重用性、提高可维护性,最重要的是可以使开发者条理清晰。Apple的UIKit等系统库都大量使用了MVC设计模式,所以本项目也将会在MVC的设计模式上进行开发。

       首先需要整理项目目录,便于之后的开发和文件的分类。项目按照功能分类,在各个功能下分为三个模块:Model、Controller、View,在DeviceManager目录下创建好空文件夹,如下所示:

       这里需要说明一下:

  1. 项目分为五个功能模块:首页、登录、购物车、咨询、我的。因为本项目使用MVC设计模式,所以每个功能模块下又分成M、V、C三个模块。
  2. 在Model文件夹中存放数据模型类,用来解析并转换JSON字符串为JSON对象;在View文件夹中存放视图类,用来显示UI;在Controller文件夹中存放控制器类,Controller是Model和View通信的桥梁,Controller通过请求获取数据并使用Model将JSON字符串转化为JSON对象传给View,用户对于View的操作,View会通知Controller,Controller处理以后改变Model。
  3. Framework(SDSY/Framework)文件夹中存放不支持CocoaPod的第三方库来手动管理。
  4. Tools(SDSY/Classes/Tools)文件夹中存放一些公共的工具类,比如:自定义网络框架、Category、全局定义、基础类(BaseClass)等等。

2. 封装AFNetworking

2.1 创建网络工具类

       项目一般都会封装一层业务层的网络框架,这样可以统一请求网络请求接口,记录或打印网络日志,封装请求格式和解析格式等等。本项目底层网络框架采用AFNetworking,对AFNetworking进行了如下的业务封装:

  1. 单例方式获取网络工具类
  2. 封装AFNetworking两种请求方法(GET/POST)为一种,实现唯一的请求方法获取数据
  3. 封装请求的BaseURL
  4. 封装支持的解析类型(acceptableContentTypes)
  5. 封装可以手动关闭打开的打印网络请求日志,包括请求内容(请求类型、请求地址、请求内容)、请求获得的JSON、请求失败时的错误信息
  6. 封装返回的内容(服务器返回数据,请求错误等)

       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/"

对以上宏定义进行解释:

  1. BASE_URL:定义请求的基地址。

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类封装了如下功能:

  1. 作为Model类最基本的用途,存储数据(学生ID,登录凭证,凭证过期时间等)
  2. 单例,在一个App的声明周期内存储用户登录信息
  3. 获取当前用户登录状态
  4. 退出登录
  5. 持久化储存用户登录信息,用于第二次打开App的自动登录

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

                // 如果要解析 jsonArraySwiftyJSON 更加丝滑, 参考 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, 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启动后的rootViewControllerLoginViewController,待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视图,

  1. 在右边的控制栏中设置好这两个个视图的在父视图中的位置

  1. 点击右下角,添加约束,轮播图的高度为200,所以让Device View距顶部的高度200,Device View的高度为170,其他的约束贴合边框即可。

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. 设置顶部轮播图,Height_NavBar代表了导航栏的高度

//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标题

        setTitle(title, for: .normal)

        // 设置title颜色

        setTitleColor(UIColor.black, for: .normal)

        // 设置title文字大小

        titleLabel?.font = UIFont.systemFont(ofSize: 12)

        // 设置Btn icon

        setImage(image, for: .normal)

    }

   

    required init?(coder aDecoder: NSCoder) {

        fatalError("init(coder:) has not been implemented")

    }

   

    override func layoutSubviews() {

        super.layoutSubviews()

        // 1.调整image位置

        if(self.imageView != nil){

            var imageFrame:CGRect = self.imageView!.frame

            imageFrame.origin.x = (self.frame.size.width - imageFrame.size.width) * 0.5

            imageFrame.origin.y = 5

            self.imageView!.frame = imageFrame

        }

       

        // 2.调整title位置

        if(self.titleLabel != nil && self.imageView != nil){

            self.titleLabel!.sizeToFit()

            var titleFrame:CGRect = self.titleLabel!.frame

            titleFrame.origin.x = (self.frame.size.width - titleFrame.size.width) * 0.5

            titleFrame.origin.y = self.imageView!.frame.maxY + 5

            self.titleLabel!.frame = titleFrame

        }

    }

}

 

5.新建deviceTableViewCell

 

添加一系列控件用于制作tableViewCell

编写回调方法didSet

var model : DeviceModel? {

        didSet{

            // nil值校验

            guard model != nil else {

                return

            }

            // 设备图片

            if self.model?.DeviceName == "打印机" {

                self.dev_image.image = UIImage.init(named: "printer")

            }

            else if self.model?.DeviceName == "耳机" {

                self.dev_image.image = UIImage.init(named: "earphone")

            }

            else if self.model?.DeviceName == "鼠标" {

                self.dev_image.image = UIImage.init(named: "mouse")

            }

            else if self.model?.DeviceName == "笔记本电脑" {

                self.dev_image.image = UIImage.init(named: "computer")

            }

            else if self.model?.DeviceName == "U" {

                self.dev_image.image = UIImage.init(named: "udisk")

            }

            else if self.model?.DeviceName == "头盔" {

                self.dev_image.image = UIImage.init(named: "helmet")

            }

           

            // 设备名

            self.dev_name.text = self.model?.DeviceName

            self.dev_name.setNeedsLayout()

           

            // 设备价格

            self.dev_price.text = self.model?.DevicePrice

            self.dev_name.setNeedsLayout()

        }

    }

6.编写购物车按钮的点击事件

   //添加商品到购物车

    @IBAction func addShopingcart(_ sender: Any) {

        let parameters:NSDictionary = ["addDeviceID":self.model?.DeviceID as Any,"addBuyNum":"1",

                                       "addUserID":UserInfoModel.shareInstance.UserID as Any]

        let manager:AFHTTPSessionManager = AFHTTPSessionManager.init()

        manager.responseSerializer = AFHTTPResponseSerializer.init()

        manager.post(BASE_URL+"addShopingcart", parameters: parameters, progress: { (Progress) in

        }, success: { (task:URLSessionDataTask, responseObject:Any?) in

            if(self.model?.DeviceID != nil){

                let str:String = "设备编号\(self.model!.DeviceID)加入购物车成功"

                SVProgressHUD.showInfo(withStatus: str)

                SVProgressHUD.dismiss(withDelay: 1)

            }

        })

    }

7.运行结果

 

可以看到已经成功适配了iphoneX。

5. 实现咨询模块功能

1.新建InfoViewController

2.添加上下左右4个约束0,使得这个tableView布满整个屏幕

3.新建一个viewController,同时添加一个webView,同样使其布满整个屏幕

 

4.最后在infoViewController中添加tableViewCell点击事件

 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        tableView.deselectRow(at: indexPath, animated: true)

        // 取出cell

        guard let cell:InfoTableViewCell = tableView.cellForRow(at: indexPath) as? InfoTableViewCell else{

            return

        }

       

         // 弹出咨询详情页

        let infoDetailViewController:InfoDetailViewController = InfoDetailViewController()

        guard let content =  cell.model?.InformationContent! else {

            return

        }

        infoDetailViewController.html = content

        self.navigationController?.pushViewController(infoDetailViewController, animated: true)

    }

   

 

           

6. 实现购物车模块功能

1.创建ShoppingCartViewController并勾选xib文件

 

2.在xib文件中拖入一个tableView和一个view

首先给tableView添加约束,让table距底部有100的距离。

然后让右键点击底部的的view拖到上面那个tableView,添加VerticalSpacing约束

添加后效果如下

3.适配iphoneX界面

约束使用代码控制,使用xib拖线的方式

 

//高度51 + 底部导航栏

_tableViewBottom.constant = Height_TabBar + 51;

   

这样一来,无论是普通的TabBar(高度49),还是iphoneX系列的TableBar(83)都可以适配,效果图:

4.创建UITableViewCell

1.在xib文件中添加如下控件即可,因为是tableViewCell,控件大小相对固定,无需添加约束

 

2.设置 didSet方法,设置每个tableViewCell控件的内容

var model:ShopListModel? {

        didSet{

            // nil值校验

            guard model != nil else {

                return

            }

            // 商品名

            self.shop_name.text  = self.model?.Device?.DeviceName

            self.shop_name.setNeedsLayout()

            // 商品价格

            self.shop_price.text = self.model?.Device?.DevicePrice

            self.shop_price.setNeedsLayout()

            // 设备图片

            if self.model?.Device?.DeviceName == "打印机" {

                self.shop_image.image = UIImage.init(named: "printer")

            }

            else if self.model?.Device?.DeviceName == "耳机" {

                self.shop_image.image = UIImage.init(named: "earphone")

            }

            else if self.model?.Device?.DeviceName == "鼠标" {

                self.shop_image.image = UIImage.init(named: "mouse")

            }

            else if self.model?.Device?.DeviceName == "笔记本电脑" {

                self.shop_image.image = UIImage.init(named: "computer")

            }

            else if self.model?.Device?.DeviceName == "U" {

                self.shop_image.image = UIImage.init(named: "udisk")

            }

            else if self.model?.Device?.DeviceName == "头盔" {

                self.shop_image.image = UIImage.init(named: "helmet")

            }

         

            //商品数量

            self.shop_num.text = String(self.model!.BuyNum!)

            self.shop_num.setNeedsLayout()

            //设置该商品是否被选中图像

            if(self.model!.isChoice == false){

                shop_choice.setImage(UIImage.init(named: "choice"), for: .normal)

            }else{

                shop_choice.setImage(UIImage.init(named: "choiced"), for: .normal)

            }

        }

}

3.以xib拖线的方式添加按钮点击事件

//增加购物车中商品数量

    @IBAction func addShop(_ sender: Any) {

        guard  model != nil else {

            return

        }

       

        if(model?.isChoice == false){

            return

        }

        var curBuynum:Int = (model?.BuyNum)!

        curBuynum = curBuynum+1

        self.model!.BuyNum = curBuynum

        print("设备编号\(self.model!.Device?.DeviceID ?? -1)购物车数量加1")

        //刷新视图

        self.shop_num.text = String(self.model!.BuyNum!)

        self.updateMoneySum()

    }

    //减少购物车中商品

    @IBAction func reduceShop(_ sender: Any) {

        guard  model != nil else {

            return

        }

       

        if(model?.isChoice == false){

            return

        }

        var curBuynum:Int = (model?.BuyNum)!

        if(curBuynum > 0){

            curBuynum = curBuynum-1

        }

        self.model!.BuyNum = curBuynum

        print("设备编号\(self.model!.Device?.DeviceID ?? -1)购物车数量减1")

        //刷新视图

        self.shop_num.text = String(self.model!.BuyNum!)

        self.updateMoneySum()

    }

   

    //选中或取消选中该商品

    @IBAction func shopChoice(_ sender: Any) {

        if(self.model!.isChoice == false){

            self.model!.isChoice = true;

            self.shop_choice.setImage(UIImage.init(named: "choiced"), for: .normal)

        }

        else{

            self.model!.isChoice = false;

            self.shop_choice.setImage(UIImage.init(named: "choice"), for: .normal)

        }

        updateMoneySum()

    }

4.在自定义cell里 获取其控制器viewController,获取控制器viewController并计算总价格

extension ShopTableViewCell{

    //获取控制器viewController并计算总价格

    func updateMoneySum() {

        let shoppingCartViewController:ShoppingCartViewController = self.viewController() as! ShoppingCartViewController

        var moneySum:Int = 0;

        for i in stride(from: shoppingCartViewController.shopArray.count-1, to: -1, by: -1) {

            let model:ShopListModel = shoppingCartViewController.shopArray[i] as! ShopListModel

            if(model.isChoice == true){

   

                moneySum += model.BuyNum! * Int((model.Device?.DevicePrice)!)!

            }

        }

       

        shoppingCartViewController.money.text = String(moneySum)

    }

   

    //在自定义cell里 获取其控制器viewController

    func viewController() -> UIViewController? {

       

        for view in sequence(first: self.superview, next: { $0?.superview }) {

            if let responder = view?.next {

                if responder.isKind(of: UIViewController.self){

                    return responder as? UIViewController

                }

            }

        }

        return nil

    }

}

5.在tableView中指定按xib进行加载

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        var cell :ShopTableViewCell? = tableView.dequeueReusableCell(withIdentifier: "CellID") as? ShopTableViewCell

        if(cell == nil){

            cell = Bundle.main.loadNibNamed("ShopTableViewCell", owner: nil, options: nil)?.first as? ShopTableViewCell

        }

        if let model = shopArray[indexPath.row] as? ShopListModel {

            cell?.model = model

        }

       

        return cell!

    }

7. HandyJSON的使用

HandyJSON另辟蹊径,采用Swift反射+内存赋值的方式来构造Model实例,保持原汁原味的Swift类定义。

1、pod引入

pod 'HandyJSON'

2、使用时,先头部import

import HandyJSON

1、HandyJSON支持 JSON直接转Model,定义class时,有两点注意:

必须遵循HandyJSON协议

需要实现空的initializer (当然Struct结构体 可以不需要init(),下文有说明)

2.为了方便使用,创建BaseModel继承HandyJSON

import UIKit

import HandyJSON

 

class BaseModel: HandyJSON {

    required init() {

       

    }

}

3.使用示例

import UIKit

 

class ShopModel: BaseModel {

    var DeviceClassId:Int?                  //设备分类id

    var DeviceID:Int?                       //设备id

    var DeviceName:String?                  //设备名称

    var DevicePrice:String?                 //设备价格

   

    required init() {

       

    }

}

8. SwiftyJSON的使用

SwiftyJSON是个使用Swift语言编写的开源库,可以让我们很方便地处理JSON数据(解析数据、生成数据)。下面这个网站详细说明了如何使用:

http://www.hangge.com/blog/cache/detail_968.html

使用示例

// 使用 SwiftyJSON 解析json -- 这里解析的是 jsonObject

            // 如果要解析 jsonArraySwiftyJSON 更加丝滑, 参考 http://www.hangge.com/blog/cache/detail_968.html

            let json = JSON(result as Any)

           

            guard let array = json["result"].array else{

                ZJFLog(message: "获取数据出错")

                // 正常结束刷新

                self.tableView.mj_header.endRefreshing()

                return

            }

           

            for dataDict in array{

                let response = dataDict.dictionaryObject

                // 字典转模型

                guard let model:ShopListModel  =  ShopListModel.deserialize(from: response) else{

                    ZJFLog(message: "字典转模型出错")

                    continue

                }

                self.shopArray.add(model)

            }

 

 

 

 

 

 

 

 

你可能感兴趣的:(swift开发)