项目搭建
课程目标
- 熟悉 Swift 语法
- 搭建系统主体框架结构
- 对比与 OC 开发的异同
- 纯代码搭建框架
创建文件
准备工作
删除模板文件
- 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上图片颜色不一样的设计
其他
如果以后在公司里面开发,美工给的切图是把文字与图标切在一张图片上
的,如:
这个时候我们只设置tabBarItem的image(或者selectedImage)的话,图片会显示 在上半部分的位置:
解决方法:
//设置偏移量,解决把图片放在tabBar中间的问题
childController.tabBarItem.imageInsets = UIEdgeInsetsMake(5, 0, -5, 0)
自定义 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