Swift 项目搭建

  • 功能需求
    • 由于采用了多视图控制器的设计方式,因此需要通过代码的方式向主控制器中添加子控制器

一、初始化部署

  • 0、效果图
Swift 项目搭建_第1张图片
初始化部署.png
  • 1、初始化window的根控制器
import UIKit

/*
1. command + shift + j ->快速定位到层级结构
2.按下回车 -->改文件夹的名称
3.command + c -->拷贝文件名称
4.command + n -->新建文件
5.command + v -->粘贴文件名称
6.回车
7.重复以上操作
*/

// T的含义: 外界传入什么就是什么
func JPLog(message: T, file: NSString = __FILE__, method: String = __FUNCTION__, line: Int = __LINE__)
{
    #if DEBUG
        print("\(method)[\(line)]: \(message)")
    #endif
}

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        
        // 1.创建window
        window = UIWindow(frame: UIScreen.mainScreen().bounds)
        window?.backgroundColor = UIColor.whiteColor()
        // 2.创建根控制器
        window?.rootViewController = MainViewController()
        // 3.显示界面
        window?.makeKeyAndVisible()
        return true
    }
}
  • MainViewController(window的根控制器)
    • 根据子控制器添加
import UIKit

class MainViewController: UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        addChildViewController(HomeTableViewController(), title: "首页", imageName: "tabbar_home")
        
        addChildViewController(MessageTableViewController(), title: "消息", imageName: "tabbar_message_center")
        
        addChildViewController(DiscoverTableViewController(), title: "发现", imageName: "tabbar_discover")
        
        addChildViewController(ProfileTableViewController(), title: "我", imageName: "tabbar_profile")
        
    }
    
    // MARK: - 内部控制方法
    func addChildViewController(childController: UIViewController, title: String, imageName: String) {
        // 1.添加子控制器
        // 不写该句,图片为蓝色默认,如果是在iOS8以前, 只有文字有效果, 而图片没有效果,而ios8设tintColor图片文字颜色都一样了,如果ios8前是设图片的渲染模型才能解决
        tabBar.tintColor = UIColor.orangeColor()
        
        // 1.1创建一个子控制器
        let homeVC = HomeTableViewController()
        // 1.2设置子控制器的相关属性
        homeVC.tabBarItem.image = UIImage(named: imageName)
        homeVC.tabBarItem.selectedImage = UIImage(named: imageName
            + "_highlighted")
//        homeVC.tabBarItem.title = title
//        homeVC.navigationItem.title = title
        // 系统会由内向外的设置标题
        homeVC.title = title
        
        // 1.3给子控制器包装一个导航控制器
        let nav = UINavigationController(rootViewController: homeVC)
        
        // 1.4添加
        addChildViewController(nav)
    }
}

二、根据类名字符串(加命名空间)来创建对应的控制器,重新设计MainViewController

  • 注意:
  • 1.在Swift中, 如果想通过字符串创建一个类, 那么必须加上命名空间
  • 2.动态获取的命名空间是不包含.的, 所以需要我们自己手动拼接
  • 3.动态获取命名空间
Swift 项目搭建_第2张图片
命名空间查看.png
let nameSpace = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"]
         ```
+ 实现方式一:利用可选绑定,判断命名空间存在,就创建

```objc
import UIKit

class MainViewController: UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        addChildViewController("HomeTableViewController", title: "首页", imageName: "tabbar_home")
        
        addChildViewController("MessageTableViewController", title: "消息", imageName: "tabbar_message_center")
        
        addChildViewController("DiscoverTableViewController", title: "发现", imageName: "tabbar_discover")
        
        addChildViewController("ProfileTableViewController", title: "我", imageName: "tabbar_profile")
        
    }
    
    // MARK: - 内部控制方法
//    func addChildViewController(childController: UIViewController, title: String, imageName: String) {
    func addChildViewController(childControllerName: String, title: String, imageName: String) {
    
        // 
//        JPLog(childController)
//        print(childController)
        
        // 0.动态获取命名空间
        let nameSpace = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"]
        if let ns = nameSpace as? String
        {
            // 注意: 1.在Swift中, 如果想通过字符串创建一个类, 那么必须加上命名空间
            //       2.动态获取的命名空间是不包含.的, 所以需要我们自己手动拼接
            let cls: AnyClass? = NSClassFromString(ns + "." + childControllerName)
            // 1.将AnyClass类型转换为UIViewController类型
            // AnyClass本质: AnyObject.Type
            // UIViewController本质: UIViewController.Type
            if let clsType = cls as? UIViewController.Type
            {
                // 2.根据控制器类类型创建一个控制器对象
                let homeVC = clsType.init()
                print(homeVC)
                
                // 1.添加子控制器
                // 如果是在iOS8以前, 只有文字有效果, 而图片没有效果
                tabBar.tintColor = UIColor.orangeColor()
                
                // 1.2设置子控制器的相关属性
                homeVC.tabBarItem.image = UIImage(named: imageName)
                homeVC.tabBarItem.selectedImage = UIImage(named: imageName
                    + "_highlighted")
                // 系统会由内向外的设置标题
                homeVC.title = title
                
                // 1.3给子控制器包装一个导航控制器
                let nav = UINavigationController(rootViewController: homeVC)
                
                // 1.4添加
                addChildViewController(nav)
            }
        }
    }
}
  • 实现方式二:利用guard来判断命名空间是否存在,存在才创建
import UIKit

class MainViewController: UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        addChildViewController("HomeTableViewController", title: "首页", imageName: "tabbar_home")
        
        addChildViewController("MessageTableViewController", title: "消息", imageName: "tabbar_message_center")
        
        addChildViewController("DiscoverTableViewController", title: "发现", imageName: "tabbar_discover")
        
        addChildViewController("ProfileTableViewController", title: "我", imageName: "tabbar_profile")
        
    }
    
    // MARK: - 内部控制方法
    func addChildViewController(childControllerName: String, title: String, imageName: String) {
    
        /*
        苹果也意识到了, Swift会不知不觉的形成多层嵌套, 然代码变得非常丑陋
        所以Swift2.0的时候专门退出了一个条件语句(guard)来解决这个问题
        格式:
        guard 条件表达式 else {
            需要执行的语句
            return
        }
        特点: 只要条件为假才会执行else中的代码
        作用: 用于过滤数据
        对比if else 这哥们只有else{}没有if{}
        */
        
        // 0.动态获取命名空间
        let nameSpace = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"]
        
        guard let ns = nameSpace as? String else{
            print("命名空间不存在")
            return
        }
        
        // 注意: 1.在Swift中, 如果想通过字符串创建一个类, 那么必须加上命名空间
        //       2.动态获取的命名空间是不包含.的, 所以需要我们自己手动拼接
        let cls: AnyClass? = NSClassFromString(ns + "." + childControllerName)
        
         // 1.将AnyClass类型转换为UIViewController类型
        guard let clsType = cls as? UIViewController.Type else{
            print("传入的字符串不能当做UIViewController来使用")
            return
        }

        // 2.根据控制器类类型创建一个控制器对象
        let homeVC = clsType.init()
        print(homeVC)
        
        // 1.添加子控制器
        // 如果是在iOS8以前, 只有文字有效果, 而图片没有效果
        tabBar.tintColor = UIColor.orangeColor()
        
        // 1.2设置子控制器的相关属性
        homeVC.tabBarItem.image = UIImage(named: imageName)
        homeVC.tabBarItem.selectedImage = UIImage(named: imageName
            + "_highlighted")
        // 系统会由内向外的设置标题
        homeVC.title = title
        
        // 1.3给子控制器包装一个导航控制器
        let nav = UINavigationController(rootViewController: homeVC)
        
        // 1.4添加
        addChildViewController(nav)
    }
}

三、动态创建控制器

  • 方案:根据json文件配置所有子控制器列表,来搭建项目
  • 该方式,简答,灵活
    • 根据json文件配置所有子控制器列表来搭建项目更加灵活,使用方案:我们可以通过发网络请求到服务器来返回对应的json数据 -》得到配置显示的子控制器列表的json,然后App根据解析json文件来创建对应的子控制器
1、MainVCSettings.json配置文件
 [
  {
  "vcName": "HomeTableViewController",
  "title": "首页",
  "imageName": "tabbar_home"
  },
  {
  "vcName": "MessageTableViewController",
  "title": "消息",
  "imageName": "tabbar_message_center"
  },
  {
  "vcName": "DiscoverTableViewController",
  "title": "广场",
  "imageName": "tabbar_discover"
  },
  {
  "vcName": "ProfileTableViewController",
  "title": "我",
  "imageName": "tabbar_profile"
  }
  ]
  • 2、解析json,创建对应控制器
    • 如果有JSON数据解析JOSN数据来创建,没JOSN数据就根据以前的方式创建
import UIKit

class MainViewController: UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()

        
        // 1.获取文件路径
        let path = NSBundle.mainBundle().pathForResource("MainVCSettings.json", ofType: nil)!
        // 2.根据文件创建
        let data = NSData(contentsOfFile: path)!

        // 在OC中处理异常是通过传入一个NSError的指针来保存错误
        // Swfit中提供了专门处理异常机制 throws -> AnyObject
        // Swift中提供 try catch, 将有可能发生错误的代码放到do中, 如果真的发生了异常就会执行catch
        // try作用: 如果抛出(throws)异常, 那么就会执行catch
        // try!作用: 告诉一定一定没有错误, 不需要处理, 但是如果使用try!发生了错误, 那么程序就会崩溃, 开发中不推荐使用
        // try?作用: 告诉系统可能有错也可能没有错, 如果发生错误会返回一个nil, 如果没有发生错误, 会将数据包装成可选类型
        do{
            let dictArr = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)
            for dict in dictArr as! [[String: AnyObject]]
            {
                addChildViewController(dict["vcName"] as? String, title: dict["title"] as? String,imageName: dict["imageName"] as? String)
            }
        }catch
        {
            addChildViewController("HomeTableViewController", title: "首页", imageName: "tabbar_home")
            
            addChildViewController("MessageTableViewController", title: "消息", imageName: "tabbar_message_center")
            
            addChildViewController("DiscoverTableViewController", title: "发现", imageName: "tabbar_discover")
            
            addChildViewController("ProfileTableViewController", title: "我", imageName: "tabbar_profile")
        }

    }
    
    // MARK: - 内部控制方法
    func addChildViewController(childControllerName: String?, title: String?, imageName: String?) {

        // 0.动态获取命名空间
        let nameSpace = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"]
        
        guard let ns = nameSpace as? String else{
             JPLog("命名空间不存在")
            return
        }
        guard let vcName = childControllerName else
        {
            JPLog("控制器名称是nil")
            return
        }
        
        // 注意: 1.在Swift中, 如果想通过字符串创建一个类, 那么必须加上命名空间
        //       2.动态获取的命名空间是不包含.的, 所以需要我们自己手动拼接
        let cls: AnyClass? = NSClassFromString(ns + "." + vcName)
        
         // 1.将AnyClass类型转换为UIViewController类型
        guard let clsType = cls as? UIViewController.Type else{
            print("传入的字符串不能当做UIViewController来使用")
            return
        }

        // 2.根据控制器类类型创建一个控制器对象
        let homeVC = clsType.init()
        
        // 1.添加子控制器
        // 如果是在iOS8以前, 只有文字有效果, 而图片没有效果
        tabBar.tintColor = UIColor.orangeColor()
        
        // 1.2设置子控制器的相关属性
        if let iName = imageName
        {
            homeVC.tabBarItem.image = UIImage(named: iName)
            homeVC.tabBarItem.selectedImage = UIImage(named: iName
                + "_highlighted")
        }
        // 系统会由内向外的设置标题
        homeVC.title = title
        
        // 1.3给子控制器包装一个导航控制器
        let nav = UINavigationController(rootViewController: homeVC)
        
        // 1.4添加
        addChildViewController(nav)
    
    }
}

四、自定义 TabBar,添加加号按钮

  • 1、效果图


    Swift 项目搭建_第3张图片
    添加加号按钮子控制器.png
  • 2、配置
    • 分析:在 4 个控制器切换按钮中间增加一个撰写按钮,添加占位控制器,计算控制器按钮位置,在中间添加一个 撰写 按钮
    • 2.1 重新配置MainVCSettings.json文件,添加一个NullViewController配置
[
 {
 "vcName": "HomeTableViewController",
 "title": "首页",
 "imageName": "tabbar_home"
 },
 {
 "vcName": "MessageTableViewController",
 "title": "消息",
 "imageName": "tabbar_message_center"
 },
 {
 "vcName": "NullViewController",
 "title": "",
 "imageName": ""
 },
 {
 "vcName": "DiscoverTableViewController",
 "title": "广场",
 "imageName": "tabbar_discover"
 },
 {
 "vcName": "ProfileTableViewController",
 "title": "我",
 "imageName": "tabbar_profile"
 }
 ]
  • 2.2 创建一个NullViewController.swift控制器文件

  • 2.3 添加NullViewController 控制器,设为加号按钮弹出的控制器,并配置加号按钮点击事件

import UIKit

class MainViewController: UITabBarController {

    // MARK: - 生命周期方法
    override func viewDidLoad() {
        super.viewDidLoad()
        /// 1.添加所有子控制器
        addChildViewControllers()
    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        // 1.初始化加号按钮
        setupComposeButton()
    }
    
    // MARK: - 内部控制方法
    // 如果在方法前面加上private, 代表这个方法只能在当前文件中访问
    // 如果在属性前面加上private, 代表这个属性只能在当前文件中访问
    // 如果在类前面加上private,  代表这个类只能在当前文件中访问
    /// 添加加号按钮
    private func setupComposeButton()
    {
        // 0.将加号按钮添加到tabbar上
        tabBar.addSubview(composeButton)
        
        // 1.计算宽度
        let width = UIScreen.mainScreen().bounds.width / CGFloat(childViewControllers.count)
        // 2.计算高度
        let height = tabBar.bounds.height
        // 3.修改frame
        let rect = CGRect(origin: CGPointZero, size: CGSize(width: width, height: height))
        /*
        CGRectOffset
        第一个参数:控件的frame
        第二个参数:控件x方向偏移的值
        第三个参数:控件y方法偏移的值
        */
        composeButton.frame = CGRectOffset(rect, 2 * width, 0)
    }
    
    /// 添加所有子控制器
    private func addChildViewControllers() {
        // 1.获取文件路径
        let path = NSBundle.mainBundle().pathForResource("MainVCSettings.json", ofType: nil)!
        // 2.根据文件创建
        let data = NSData(contentsOfFile: path)!
        
        do{
            // 解析JSON创建控制器
            let dictArr = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)
            for dict in dictArr as! [[String: AnyObject]]
            {
                addChildViewController(dict["vcName"] as? String, title: dict["title"] as? String,imageName: dict["imageName"] as? String)
            }
        }catch
        {
            // 使用本地数据
            addChildViewController("HomeTableViewController", title: "首页", imageName: "tabbar_home")
            
            addChildViewController("MessageTableViewController", title: "消息", imageName: "tabbar_message_center")
            
            // 设加号按钮对应的控制器  NullViewController
            addChildViewController(NullViewController())
            
            addChildViewController("DiscoverTableViewController", title: "发现", imageName: "tabbar_discover")
            
            addChildViewController("ProfileTableViewController", title: "我", imageName: "tabbar_profile")
        }
    }
    /**
    添加子控制器
    
    - parameter childControllerName: 控制器的名称
    - parameter title:               控制器标题
    - parameter imageName:           控制器图片
    */
    private func addChildViewController(childControllerName: String?, title: String?, imageName: String?) {

        // 0.动态获取命名空间
        let nameSpace = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"]
        
        guard let ns = nameSpace as? String else{
            JPLog("命名空间不存在")
            return
        }
        guard let vcName = childControllerName else
        {
            JPLog("控制器名称是nil")
            return
        }
        
        // 注意: 1.在Swift中, 如果想通过字符串创建一个类, 那么必须加上命名空间
        //       2.动态获取的命名空间是不包含.的, 所以需要我们自己手动拼接
        let cls: AnyClass? = NSClassFromString(ns + "." + vcName)
        
         // 1.将AnyClass类型转换为UIViewController类型
        guard let clsType = cls as? UIViewController.Type else{
            print("传入的字符串不能当做UIViewController来使用")
            return
        }

        // 2.根据控制器类类型创建一个控制器对象
        let homeVC = clsType.init()
        
        // 1.添加子控制器
        // 如果是在iOS8以前, 只有文字有效果, 而图片没有效果
        tabBar.tintColor = UIColor.orangeColor()
        
        // 1.2设置子控制器的相关属性
        if let iName = imageName
        {
            homeVC.tabBarItem.image = UIImage(named: iName)
            homeVC.tabBarItem.selectedImage = UIImage(named: iName
                + "_highlighted")
        }
        // 系统会由内向外的设置标题
        homeVC.title = title
        
        // 1.3给子控制器包装一个导航控制器
        let nav = UINavigationController(rootViewController: homeVC)
        
        // 1.4添加
        addChildViewController(nav)
    
    }
    
    // MARK: - 监听点击事件
    // 注意: 由于点击事件是由NSRunLoop发起的, 并不是当前类发起的, 所以如果在点击方法前面加上private, 那么NSRunLoop无法找到该方法
    // OC是基于运行时动态派发事件的, 而Swift是编译时就已经确定了方法
    // 如果想给监听点击的方法加上private, 并且又想让系统动态派发时能找到这个方法, 那么可以在前面加上@objc, @objc就能让这个方法支持动态派发
    @objc private func composeBtnClick()
    {
        JPLog("")
    }
    
    // MARK: - 懒加载
    private lazy var composeButton: UIButton = {
        /*
       let btn = UIButton()
        // 1.设置背景图片
        btn.setBackgroundImage(UIImage(named: "tabbar_compose_button"), forState: UIControlState.Normal)
        btn.setBackgroundImage(UIImage(named: "tabbar_compose_button_highlighted"), forState: UIControlState.Highlighted)
    
        // 2.设置普通图片
        btn.setImage(UIImage(named: "tabbar_compose_icon_add"), forState: UIControlState.Normal) 
        btn.setImage(UIImage(named: "tabbar_compose_icon_add_highlighted"), forState: UIControlState.Highlighted)
        
        // 3.监听按钮点击
        btn.addTarget(self, action: Selector("composeBtnClick"), forControlEvents: UIControlEvents.TouchUpInside)
        
        btn.sizeToFit()
        */
        
//        let btn = UIButton.create("tabbar_compose_button", backImageName: "tabbar_compose_icon_add")
        
        let btn = UIButton(imageName: "tabbar_compose_button", backImageName: "tabbar_compose_icon_add")  // 使用了UIButton分类的定义的方法,如下UIButton+Extension.swift分类文件可得
         btn.addTarget(self, action: Selector("composeBtnClick"), forControlEvents: UIControlEvents.TouchUpInside)
        return btn;
    }()
}
  • 注意: UIButton+Extension.swift分类实现抽取如下
import UIKit

extension UIButton
{
    // 虽然以下方法可以快速创建一个UIButton对象, 但是Swift风格不是这样写的
    // 在Swift开发中, 如果想快速创建一个对象, 那么可以提供一个便利构造器(便利构造方法)
    // 只要在普通构造方法前面加上一个convenience, 那么这个构造方法就是一个便利构造方法
    // 注意: 如果定义一个便利构造器, 那么必须在便利构造器中调用指定构造器(没有加convenience单词的构造方法)
    
    /*
    class func create(imageName: String, backImageName: String) -> UIButton
    {
        let btn = UIButton()
        // 1.设置背景图片
        btn.setBackgroundImage(UIImage(named: imageName), forState: UIControlState.Normal)
        btn.setBackgroundImage(UIImage(named: imageName + "highlighted"), forState: UIControlState.Highlighted)
        
        // 2.设置普通图片
        btn.setImage(UIImage(named:backImageName), forState: UIControlState.Normal)
        btn.setImage(UIImage(named: backImageName + "highlighted"), forState: UIControlState.Highlighted)
        
        btn.sizeToFit()
        
        return btn
    }
 */
    
    /*
    定义便利构造器步骤:
    1.编写一个构造方法
    2.在构造方法前面加上 convenience
    3.在构造方法中调用当前类的其他"非便利构造器"初始化对象
    */
    convenience init(imageName: String, backImageName: String)
    {
        self.init()
        
        // 1.设置背景图片
        setBackgroundImage(UIImage(named: imageName), forState: UIControlState.Normal)
        setBackgroundImage(UIImage(named: imageName + "highlighted"), forState: UIControlState.Highlighted)
        
        // 2.设置普通图片
        setImage(UIImage(named:backImageName), forState: UIControlState.Normal)
        setImage(UIImage(named: backImageName + "highlighted"), forState: UIControlState.Highlighted)
        
        sizeToFit()
    }
}

阶段性小结

  • 整体开发思路与使用 OC 几乎一致
  • Swift 语法更加简洁,字符串拼接非常简单
vc.tabBarItem.selectedImage = UIImage(named: imageName + "_highlighted")
  • Swift 对类型校验更加严格,不同类型的变量不允许直接计算
v.frame = CGRectOffset(rect, CGFloat(index) * w, 0)
  • Swift 中的懒加载写法
lazy var composedButton: UIButton = {

    let btn = UIButton()

    btn.setImage(UIImage(named: "tabbar_compose_icon_add"), forState: UIControlState.Normal)
    btn.setImage(UIImage(named: "tabbar_compose_icon_add_highlighted"), forState: UIControlState.Highlighted)
    btn.setBackgroundImage(UIImage(named: "tabbar_compose_button"), forState: UIControlState.Normal)
    btn.setBackgroundImage(UIImage(named: "tabbar_compose_button_highlighted"), forState: UIControlState.Highlighted)

    // 懒加载中需要使用 self.
    self.addSubview(btn)

    return btn
}()

懒加载的代码是一个闭包,因此在代码内部需要使用 self.

  • 不希望暴露的方法,应该使用 private 修饰符

  • 按钮点击事件的调用是由 运行循环 监听并且以消息机制传递的,因此,按钮监听函数不能设置为 private

  • Swift 中的类名是包含命名空间的,如果希望用字符串动态创建并且实例化类,需要按照以下代码格式

    let namespace = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String
    let clsName = namespace + "." + vcName
    // 告诉编译器暂时就是AnyClass
    let cls: AnyClass!  = NSClassFromString(ns + "." + vcName)
    // 告诉编译器真实类型是UIViewController
    let vcCls = cls as! UIViewController.Type
    // 实例化控制器
    let vc = vcCls.init()

tabBar 的 items 是在 视图将要出现之前 才被创建的

你可能感兴趣的:(Swift 项目搭建)