02-项目搭建

项目搭建

课程目标

  1. 熟悉 Swift 语法
  2. 搭建系统主体框架结构
  3. 对比与 OC 开发的异同
  4. 纯代码搭建框架

创建文件

准备工作

删除模板文件

  • ViewController.swift
  • Main.storyboard
  • LaunchScreen.xib

创建项目结构

主目录 Classes

二级目录

目录名 说明
Model 数据模型
View 视图和控制器
ViewModel 业务逻辑模型
Tools 工具类

View 子目录

目录名 说明
Main 主要
Home 首页
Message 消息
Compose 撰写
Discover 发现
Profile

View中每个文件夹 子目录

目录名 说明
Controller 控制器
View 自定义View控件

创建项目文件

Main

目录 Controller
Main HMMainViewController.swift(:UITabBarController)

功能模块

目录 Controller
Home HMHomeTableViewController.swift
Message HMMessageTableViewController.swift
Discover HMDiscoverTableViewController.swift
Profile HMProfileTableViewController.swift

细节

  • 每个 ViewController 继承自 UITableViewController
  • 修改 AppDelegate 中的 didFinishLaunchingWithOptions 函数,设置启动控制器
window?.rootViewController = MainViewController()

添加子控制器

功能需求

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

文件准备

  • 将素材文件夹中的 TabBar 拖拽到 Assets.xcassets 目录下

代码实现

添加第一个视图控制器

override func viewDidLoad() {
    super.viewDidLoad()

    addChildViewController()
}

private func addChildViewController() {
    tabBar.tintColor = UIColor.orangeColor()

    let vc = HomeTableViewController()
    vc.title = "首页"
    vc.tabBarItem.image = UIImage(named: "tabbar_home")

    let nav = UINavigationController(rootViewController: vc)

    addChildViewController(nav)
}

重构代码抽取参数

/// 添加控制器
///
/// - parameter vc       : 视图控制器
/// - parameter title    : 标题
/// - parameter imageName: 图像名称
private func addChildViewController(vc: UIViewController, title: String, imageName: String) {
    //设置标题
    vc.title = title
    //设置图片
    vc.tabBarItem.image = UIImage(named: imageName)
    vc.tabBarItem.selectedImage = UIImage(named: "\(imageName)_selected")
    //使用导航控制器包裹起来
    let nav = UINavigationController(rootViewController: vc)
    addChildViewController(nav)
}
  • 扩充调用函数,添加其他控制器
/// 添加所有子控制器
private func addChildViewControllers() {
    addChildViewController(HMHomeTableViewController(), title: "首页", imageName: "tabbar_home")
    addChildViewController(HMMessageTableViewController(), title: "消息", imageName: "tabbar_message_center")
    addChildViewController(HMDiscoverTableViewController(), title: "发现", imageName: "tabbar_discover")
    addChildViewController(HMProfileTableViewController(), title: "我", imageName: "tabbar_profile")
}

UITabBar调整

遇到问题:

  • tabBar上的图片与文字显示的颜色不对,应该怎么去调整?

调整方式:

  • 第1种.一起设置
//设置UITabBar的TintColor属性
UITabBar.appearance().tintColor = UIColor.orangeColor()

这种方式可以把TabBar上显示的图片与文字的颜色一并设置

  • 第2种.分开设置
    • 设置图片:
      • 设置图片按原来的颜色渲染:
        设置tabBarItem的selectedImage属性的时候
//UIImageRenderingMode --> 渲染模式
//设置默认的图片
vc.hildController.tabBarItem.image = UIImage(named: imageName)?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
//设置选中的图片
vc.tabBarItem.selectedImage = UIImage(named: "\(imageName)_selected")?.imageWithRenderingMode( UIImageRenderingMode.AlwaysOriginal)
  • 也可以在Assets.xcassets目录下,找到对应图片,利用右边控制面板设置其渲染的模式:
  • 设置文字:可以利用tabBarItem的setTitleTextAttributes方法给title添加属性
//文字颜色的属性
let textColorAttr = [
    NSForegroundColorAttributeName: UIColor.orangeColor()
]
//设置选中文字颜色
vc.tabBarItem.setTitleTextAttributes(textColorAttr, forState: UIControlState.Selected)
  • 可以利用这种方法调整UItabBar上的文字大小:
//设置字体的属性
let textFontAttr = [
    NSFontAttributeName: UIFont.systemFontOfSize(12)
]
vc.tabBarItem.setTitleTextAttributes(textFontAttr, forState: UIControlState.Normal)

注意:后面state是Normal

第2种方式更加灵活,可以应对UITabBar上图片颜色不一样的设计

其他

如果以后在公司里面开发,美工给的切图是把文字与图标切在一张图片上的,如:

allproducts.png
allproducts_s.png

这个时候我们只设置tabBarItem的image(或者selectedImage)的话,图片会显示 在上半部分的位置:

allproducts_effects.png

解决方法:

//设置偏移量,解决把图片放在tabBar中间的问题
childController.tabBarItem.imageInsets = UIEdgeInsetsMake(5, 0, -5, 0)
allproducts_effects_right.png

自定义 TabBar

功能需求

  • 在 4 个控制器切换按钮中间增加一个撰写按钮
  • 点击撰写按钮能够弹出发表微博的控制器

需求分析

  • 自定义 TabBar
  • 计算控制器按钮位置,在中间添加一个 撰写 按钮

思路

  • 加号按钮的大小与其他 tabBarItem 的大小是一致的
  • 添加加号按钮到TabBar中
  • 遍历查找到其他4个UITabBarButton,设置宽度并调整位置
  • 将撰写按钮放在自定义的UITabBar中间位置
  • 在UITabBar内部监听撰写按钮的点击事件,通过闭包回调到控制器

代码实现

  • 按钮的初始化
/// 撰写按钮
lazy var composeButton: UIButton = {
    let button = UIButton()

    //给按钮添加点击事件(因为button最重要的就是点击事件,所以写在前面)
    button.addTarget(self, action: "composeButtonClick", forControlEvents: UIControlEvents.TouchUpInside)

    //设置按钮不同状态的图片与背景图片
    button.setImage(UIImage(named: "tabbar_compose_icon_add"), forState: UIControlState.Normal)
    button.setImage(UIImage(named: "tabbar_compose_icon_add_highlighted"), forState: UIControlState.Highlighted)
    button.setBackgroundImage(UIImage(named: "tabbar_compose_button"), forState: UIControlState.Normal)
    button.setBackgroundImage(UIImage(named: "tabbar_compose_button_highlighted"), forState: UIControlState.Highlighted)

    //设置大小
    button.sizeToFit()

    return button
}()
  • 设置按钮位置
/// 调整子控件的位置在layoutSubviews里面调整
override func layoutSubviews() {
    super.layoutSubviews()

    //设置加号按钮位置
    //调整子控件的center,不能依靠于父控制的center,因为你知道父控件是放在什么地方的
    composeButton.center = CGPoint(x: frame.width * 0.5, y: frame.height * 0.5)

    ///每一个按钮的宽度
    let childW = frame.size.width / 5
    //定义脚标记录当前button的位置
    var index = 0

    //遍历子控件调整'UITabBarButton'的宽度与位置
    for childView in subviews {

        //如果当前遍历的View是UITabBarButton
        if childView.isKindOfClass(NSClassFromString("UITabBarButton")!) {

            //设置按钮的宽
            childView.frame.size.width = childW
            //设置按钮的x
            childView.frame.origin.x = CGFloat(index) * childW
            //index递增
            index++
            //如果当前遍历到'发现',则把发现往后移动一个位置
            if index == 2 {
                index++
            }
        }
    }
}

  • 按钮监听
/// 定义闭包类型的属性
var composeButtonClosure: (()->())?

/// 在按钮点击的时候调用闭包
@objc private func composeButtonClick(){
    composeButtonClosure?()
}
  • 注意:按钮的监听方法不能使用 private,否则运行循环是找不到这个方法的,但是在这种情况下,如果不私有化的话,外界(控制器)可以直接调用这个方法,不符合设计思想,所以可以在加了private的情况下,可以用 @objc 去修饰这个方法

设置tabBar

因为tabBarController上的tabBar是只读属性,不能直接设置值,所以我们可以采用KVC的方式赋值:

let tab = HMTabBar()
//设置撰写按钮点击的事件响应
// 注意:此闭包内使用 `self` 会形成循环引用
tab.composeButtonClickBlock = {
    print("撰写按钮点击")
}
setValue(tab, forKey: "tabBar")

阶段性小结

  • 整体开发思路与使用 OC 几乎一致
  • Swift 语法更加简洁
  • Swift 对类型校验更加严格,不同类型的变量不允许直接计算
let w = tabBar.bounds.width / CGFloat(childViewControllers.count)
//设置按钮的宽
var frame = childView.frame
frame.size.width = childW
//设置按钮的x,此处index为Int类似的值
frame.origin.x = CGFloat(index) * childW
childView.frame = frame
  • Swift 中的懒加载本质上是一个闭包,因此引用当前控制器的对象时需要使用 self.
  • 不希望暴露的方法,应该使用 private 修饰符
    • 按钮点击事件的调用是由 运行循环 监听并且以消息机制传递的
    • 而Swift为了追求性能的这个特性决定了其在编译时期就已经决定好了某个事件该如何去调用
    • 而使用 private 修饰的方法在运行的时候对于运行循环是不可见的,所以会提示 找不到xxx方法
    • 可以使用 @objc 修饰此私有方法,其在于告诉系统此方法使用 Objective-C 的基于运行时的机制(KVC以及动态派发)
  • viewDidLoad 函数中添加子控制器只会完成控制器的添加,而不会为 tabBar 创建 tabBarButton
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,947评论 4 60
  • 嗯哼嗯哼蹦擦擦~~~ 转载自:https://github.com/Tim9Liu9/TimLiu-iOS 目录 ...
    philiha阅读 4,796评论 0 6
  • 枚举 枚举中,成员值是一般是字符类型(不带引号),原始值则可以为Int、Double、String等等 枚举的三个...
    Mi欧阳阅读 742评论 1 0
  • 题主问如果未来的人有某种镜像,能够看到现在的你所做的事,包括一些隐私或者是错事,这会让你感到焦虑,而这种焦虑如何解...
    尾生斯基阅读 297评论 0 2