1. 简述
1.1 设置UITableViewController
1.2 加载"数据",利用闭包模拟网络异步加载
1.3 自定义Cell,绑定 Cell,通过模型设置 cell 的内容
1.4 跳转界面 -> DetailViewController,界面的布局
1.5 传递数据
1.6 保存数据,刷新表格,利用闭包来实现
2. 设置UITableViewController
2.1 ViewController 改为 继承 UITableViewController
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("viewDidLoad")
}
}
2.2 Main.storyboard 删除 View Controller,添加 Table View Controller, Custom Class 选项卡中 Class 改为 ViewController,Table View Call 中 Identifler 中输入 Cell
3. 加载"数据",利用闭包模拟网络异步加载
3.1 创建 Person.swift 模型
class Person: NSObject {
@objc var name: String?
@objc var age: Int = 0
init(dict:[String:Any]){
super.init()
//字典转模型
setValuesForKeys(dict)
}
//对象描述信息,有利于开发调试
//类似于 java 中的 toString() 函数
//开发时,在模型中 实现此属性/方法
override var description: String{
let keys = ["name","age"]
//模型转字典 - 网络的 POST JSON
return "\(dictionaryWithValues(forKeys: keys))"
}
}
3.2 数据处理
//MARK: - 数据处理
//extension 类似于 OC 中的分类,可以将控制器的代码分组,便于维护和管理
extension ViewController{
//加载数据 -> 字典数组 -> 模型数组
private func loadData(completion:@escaping (_ array: [Person]) -> ()){
DispatchQueue.global().async {
print("耗时操作")
//拼接个人信息数组
//1.创建数组
var dataList = [Person]()
//2.循环生成模拟数据
for i in 0..<50 {
let name = "xiaoxiao \(i)"
//生成一个 18至22的随机数
let age = Int.random(in: 17...22)
//建立字典
let dict:[String:Any] = ["name":name,"age":age]
//字典转模型
dataList.append(Person(dict: dict))
}
//测试数据
//print(dataList)
DispatchQueue.main.async {
//完成回调
print("完成回调")
completion(dataList)
}
}
}
3.3 调用函数
class ViewController: UITableViewController {
//个人数据数组
private var persons: [Person]?
override func viewDidLoad() {
super.viewDidLoad()
loadData { array in
//记录接收到回调参数
self.persons = array
//刷新表格
self.tableView.reloadData()
}
}
}
3.4 数据源方法
//MARK: - 数据源方法
extension ViewController{
//设置行高
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 65
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//如果 persons 数组为 nil,直接返回 0
// if persons != nil{
// return persons!.count
// }else {
// return 0
// }
return persons?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//persons!: 只有数据源 才会执行此方法
cell.textLabel?.text = persons![indexPath.row].name
return cell
}
}
4. 自定义 Cell,绑定 Cell
4.1 自定义 Cell,创建 PersonCell.swift
class PersonCell: UITableViewCell {
//个人模型 - Swift 中设置没模型可以用 didSet
var person: Person?{
didSet{
//不需要 使用 '_成员变量 = 变量',因为已经完成设置了
// 当 person 模型被设置值完成后,执行的代码
nameLable.text = person?.name
ageLabel.text = "\(person?.age ?? 0)"
}
}
@IBOutlet weak var nameLable: UILabel!
@IBOutlet weak var ageLabel: UILabel!
}
4.2 ViewController.swift 中绑定 Cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//模型查询可重用 cell 返回 UITableViewCell 没有 PersonCell 的属性
//使用 as 转类型
//通常使用 as 的时候,需要使用 ? 或者 !
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! PersonCell
//persons!: 只有数据源 才会执行此方法
//persons 是一定有内容的
cell.person = persons![indexPath.row]
//cell.textLabel?.text = persons![indexPath.row].name
return cell
}
5. 明细界面搭建和连线
5.1 将Table View Controller 设置导航栏控制器/Navigation Controller:
操作步骤:选择 Main.storybord 中 View Controller,菜单栏选择 Editor -> Embed In -> Navigation Controller
5.2 添加信息 View Controller,点击 Table View 中的 Cell,按住 Ctrl 进行连线 show,个人明细界面布局,Custom Class 选项中选择 DetailViewController
5.3 创建 DetailViewController.swift
class DetailViewController: UIViewController {
@IBOutlet weak var nameText: UITextField!
@IBOutlet weak var ageText: UITextField!
//保存按钮
@IBAction func save(_ sender: Any) {
print("save")
}
//文本变化
@IBAction func textChanged() {
//如果 两个输入框都有内容,才允许保存
navigationItem.rightBarButtonItem?.isEnabled = nameText.hasText && ageText.hasText
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
6. 传递数据
6.1 DetailViewController.swift 添加数据更新
class DetailViewController: UIViewController {
@IBOutlet weak var nameText: UITextField!
@IBOutlet weak var ageText: UITextField!
//数据模型
var person: Person?
// {
// didSet{
// //在传递数值的时候,控制器已经被创建,但是控制器的视图没有被创建!
// //视图没有被创建,那么字视图同样没有创建
// //EXC_BAD_INSRTRUCTION 野指针异常??
// //一旦使用了 view 如果 view == nil, 会调用 loadView() 创建 view 以子视图
// //print(view)
// //nameText.text = person?.name
// //ageText.text = "\(person?.age ?? 0)"
// }
// }
//保存按钮
@IBAction func save(_ sender: Any) {
print("save")
}
//文本变化
@IBAction func textChanged() {
//如果 两个输入框都有内容,才允许保存
navigationItem.rightBarButtonItem?.isEnabled = nameText.hasText && ageText.hasText
}
override func viewDidLoad() {
super.viewDidLoad()
nameText.text = person?.name
ageText.text = "\(person?.age ?? 0)"
//激活按钮
textChanged()
}
}
6.2 ViewController.swift 添加传递参数
class ViewController: UITableViewController {
//个人数据数组
private var persons: [Person]?
override func viewDidLoad() {
super.viewDidLoad()
loadData { array in
//记录接收到回调参数
self.persons = array
//刷新表格
self.tableView.reloadData()
}
}
//从一个 VC 跳转到另一个 VC 调用的方法
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//1. 拿到目标控制器
guard let detailVC = segue.destination as? DetailViewController else{
return
}
//2.获取用户当前选中行数据
guard let indexPath = tableView.indexPathForSelectedRow else{
return
}
//detailVC & indexPath 全部拿到
//3.根据 indexPath 获取 Person 数据
print(persons![indexPath.row])
//传递数据
detailVC.person = persons![indexPath.row]
}
}
7. 保存数据,刷新表格
7.1 DetailViewController.swift 添加闭包回调
//定义闭包属性
var didSaveCallBack: (() -> ())?
var didParamSaveCallBack: ((_ person: Person) ->())?
7.2 保存按钮事件
//保存按钮
@IBAction func save(_ sender: Any) {
//1.使用 UI 更新模型
person?.name = nameText.text
//第一个 ! 保证一定有字符串内容
//第二个 ! 保证一定能转换成整数
person?.age = Int(ageText.text!) ?? 0
//print(person)
//2.完成回调,通知控制器刷新数据 - 闭包
//? 表示闭包不存在,就不执行 -> OC 中调用 block 之前一定要判断!
//如果强行解包,同时没有数值,会崩溃
//didSaveCallBack?()
didParamSaveCallBack?(person!)
navigationController?.popViewController(animated: true)
}
7.3 ViewController.swift 更新表格
//从一个 VC 跳转到另一个 VC 调用的方法
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//1. 拿到目标控制器
guard let detailVC = segue.destination as? DetailViewController else{
return
}
//2.获取用户当前选中行数据
guard let indexPath = tableView.indexPathForSelectedRow else{
return
}
//detailVC & indexPath 全部拿到
//3.根据 indexPath 获取 Person 数据
print(persons![indexPath.row])
//4.传递数据
//4.1 个人信息
detailVC.person = persons![indexPath.row]
//4.2 完成回调
// detailVC.didSaveCallBack = {
// //刷新表格
// self.tableView.reloadData()
// }
//简写回调
//detailVC.didSaveCallBack = self.tableView.reloadData
//带参回调
detailVC.didParamSaveCallBack = { param in
print(param)
self.tableView.reloadData()
}
}
8. 完成效果图