Swift IOS Project Todos App

项目概括

制作一个仿任务清单的App,主要为了练习TableViewController。

TableViewController的基本使用

  • row, indexPath
//row -> indexPath
let indexPath = IndexPath(row: row, section: 0)
//indexPath -> row
let row = indexPath.row
  • indexPath, cell
//indexPath -> cell
let cell = tableView.cellForRow(at: indexPath)
//cell -> indexPath
let indexPath = tableView.indexPath(for: cell)
  • row, cell
//row -> cell
let indexPath = IndexPath(row: row, section: section)
let cell = ableView.cellForRow(at: IndexPath)
//cell -> row
let row = tableView.indexPath(for: cell)!.row

Todos cell 设置

  • 新建一个类继承UITableViewCell,链接类和对象,设置cell的identifier
class TodoCell: UITableViewCell {

    @IBOutlet weak var checkMark: UILabel!
    @IBOutlet weak var todo: UILabel!
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

table view data source设置

  • numberOfSection中设置区域数量
  • numberOfRowsInSection中设置一个区域中的段落的数量
  • cellForRowAt 中对cell进行配置,注意要将cell强制转换类型
// MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return todos.count
    }

    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "todo", for: indexPath) as! TodoCell

        // Configure the cell...
        cell.checkMark.text = todos[indexPath.row].checked ? "√" : ""
        cell.todo.text = todos[indexPath.row].name
        
        return cell
    }

设置点击一行后的事件

  • didSelectRowAt的用处是点击一行后的事件
  • cellForRow是tableView实例下的方法,根据indexPath返回相应的cell
  • deselectRow是tableView实例下的方法,根据indexPath取消cell的选中状态
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        todos[indexPath.row].checked = !todos[indexPath.row].checked
        let cell = tableView.cellForRow(at: indexPath) as! TodoCell
        cell.checkMark.text = todos[indexPath.row].checked ? "√" : ""
        tableView.deselectRow(at: indexPath, animated: false)
    }

添加Navigation Controller

  • 将Todos页面添加到Navigation Controller
  • 添加一个新的TableViewController页面,与第一个页面通过show方式连接
  • 在Navigation Bar中勾选prefer large titles使标题变大
  • 在Add Todo页面中添加Navigation Item作为标题

反向传值,从第二个页面传值到第一个页面

  • 从add todo页面把输入的数据传递到todos页面:
  1. 定义协议,协议中只有一个方法addTodo
  2. 在add todo页面实例化协议,并执行addTodo方法
  3. 在todos页面继承协议,实现addTodo方法,在prepare方法中将协议委托给todos页面的控制器
protocol TodoDelegate {
    func didAdd(name: String) -> Void
}
@IBAction func done(_ sender: Any) {
	if let name = todoInput.text, !name.isEmpty {
		delegate?.didAdd(name: name)
	}
	navigationController?.popViewController(animated: true)
}
extension TodosController: TodoDelegate {
    func didAdd(name: String) {
        todos.append(Todo(name: name, checked: false))
        let indexPath = IndexPath(row: todos.count - 1, section: 0)
        tableView.insertRows(at: [indexPath], with: .automatic)
    }

}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
        if segue.identifier == "addTodo" {
            let vc = segue.destination as! TodoController
            vc.delegate = self
        }
}

更新todo

  • control拖拽选择Accessory Action Show
  • 先正向传值,把当前todo的信息传递到todo页面
  • sender指通过哪个对象进行segue,这里通过点击cell进行segue,那么sender就是被点击的cell,通过类型转换获取被点击的cell
  • 通过cell找到indexPath
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
        let vc = segue.destination as! TodoController
        vc.delegate = self
        if segue.identifier == "editTodo" {
            let cell = sender as! TodoCell
            //通过cell获取indexPath
            row = tableView.indexPath(for: cell)!.row
            vc.name = todos[row].name
        }
    }
  • 修改todo页面的标题,改为edit,navigationItem是隐藏的成员
navigationItem.title = "edit"
  • 增加协议里的方法,点击提交按钮后执行委托
	protocol TodoDelegate {
	    func didAdd(name: String) -> Void
	    func didEdit(name: String) -> Void
	}
	
    @IBAction func done(_ sender: Any) {
        if let name = todoInput.text, !name.isEmpty {
            if self.name != nil {
                delegate?.didEdit(name: name)
            } else {
                delegate?.didAdd(name: name)
            }
        }
        navigationController?.popViewController(animated: true)
    }
  • 实现委托
    func didEdit(name: String) {
        todos[row].name = name
        let indexPath = IndexPath(row: row, section: 0)
        let cell = tableView.cellForRow(at: indexPath) as! TodoCell
        cell.todo.text = name
    }

左滑删除

  • 设置leftBarButtonItem
navigationItem.leftBarButtonItem = editButtonItem
  • 设置table view的Editing属性为Multiple Selection During Editing
  • 重载setEditing方法
    override func setEditing(_ editing: Bool, animated: Bool) {
        super.setEditing(editing, animated: animated)
        editButtonItem.title = isEditing ? "Finish" : "Edit"
    }
  • 修改didSelectRowAt,只有当处于非编辑状态的时候,点击cell才会调用didSelectRowAt方法
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if !isEditing {
            todos[indexPath.row].checked = !todos[indexPath.row].checked
            
            let cell = tableView.cellForRow(at: indexPath) as! TodoCell
            cell.checkMark.text = todos[indexPath.row].checked ? "√" : ""
            tableView.deselectRow(at: indexPath, animated: false)
        }
    }
  • 重载editingStyle方法,和titleForDeleteConfirmationForRowAt方法
    // Override to support editing the table view.
    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            todos.remove(at: indexPath.row)
            // Delete the row from the data source
            tableView.deleteRows(at: [indexPath], with: .fade)
        } else if editingStyle == .insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
        }    
    }
    
    override func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
        return "delete"
    }

批量删除

  • tableView.indexPathsForSelectedRows返回[indexPath],表示多选被选中的cell
  • 数组的remove方法
  • tableView的deleteRows方法,删除多个cell
  • 把更新视图的语句放在beginUpdates和endUpdates中间可以优化运行,tableView.reloadData方法也可以优化运行,但视图修改没有动画效果

    @IBAction func batchDelete(_ sender: Any) {
        let indexPaths = tableView.indexPathsForSelectedRows
        if let indexPaths = indexPaths {
            for indexPath in indexPaths {
                todos.remove(at: indexPath.row)
            }
            tableView.beginUpdates()
            tableView.deleteRows(at: indexPaths, with: .automatic)
            tableView.endUpdates()
            
//            tableView.reloadData()//没有动画效果
        }
    }

移动数据

  • 重载moveRowAt方法
    // Override to support rearranging the table view.
    override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
        //move data
        let todo = todos.remove(at: fromIndexPath.row)
        todos.insert(todo, at: to.row)
        //update view
        tableView.moveRow(at: fromIndexPath, to: to)
        tableView.reloadData()
    }

本地存储UserDefaults

  • UserDefaults只支持Swift基础类型和Data类型,所以要把[Todo]类型的数据存储起来需要先编码程Data类型,然后再存储
    func saveData() -> Void {
        do {
            let data = try JSONEncoder().encode(todos)
            UserDefaults.standard.set(data, forKey: "todos")
        } catch {
            print(error)
        }
    }
  • 解码过程类似
        if let data = UserDefaults.standard.data(forKey: "todos"){
            do{
                todos = try JSONDecoder().decode([Todo].self, from: data)
            }catch {
                print(error)
            }
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值