一个简单的swift项目(基于TableView实现数据增添,删除,查找,更改功能)
项目来源:
慕课网的swift教程 http://www.imooc.com/learn/173
参考资料:
《如何使用Swift添加Table View搜索框》http://www.tairan.com/archives/7721/
《UISearchController Tutorial: Getting Started》http://www.raywenderlich.com/113772/uisearchcontroller-tutorial
一,往新建的swift单页面程序中添加一个Cocoa Touch类,用于存储数据以及交互
import UIKit
//创建本地数据模型
class TodoModel: NSObject {
var id:String
var image:String
var title:String
var date:NSDate
init(id:Stringimage:String,title:String,date:NSDate) {
self.id = id
self.image = image
self.title = title
self.date = date
}
}
二,在viewController的class外部,创建全局变量,作为本地运行时的数据库
var todos:[TodoModel]=[]
在viewDidLoad()中添加初始化数据:
todos = [TodoModel(id: "1", image: "child-selected", title:"1.去游乐场" , date: dateFromString("2016-1-1")!),
TodoModel(id: "2", image: "shopping-cart-selected", title:"2.购物" , date: dateFromString("2016-1-2")!),
TodoModel(id: "3", image: "phone-selected", title:"3.打电话" , date: dateFromString("2016-1-3")!),
TodoModel(id: "4", image: "travel-selected", title:"4.去阳逻旅游" , date: dateFromString("2016-1-4")!)]
为了方便处理NSDate变量,在Class外面创建两个全局方法
func dateFromString(dateStr:String)->NSDate?{
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = dateFormatter.dateFromString(dateStr)
return date
}
func stringFromDate(date:NSDate)->String{
let locale = NSLocale.currentLocale()
let dateFormat = NSDateFormatter.dateFormatFromTemplate("yyyy-MM-dd", options: 0, locale: locale)
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = dateFormat
return dateFormatter.stringFromDate(date)
}
三,使用TableView控件显示数据库中的数据
1⃣打开storyboard,在view中拖控件TableView,在TableView 中添加Cell
2⃣将cell的style属性更改为basic,同时更改title内容
3⃣拖线到viewcontroller创建tableview的属性
4⃣为了配置tableview的属性,让viewcontroller继承UITableViewDataSource协议
5⃣实现协议里的两个方法:
//设置tableview的行数
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return 20
}
//设置复用的cell
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = self.tableview.dequeueReusableCellWithIdentifier("todoCell")! as UITableViewCell//取出cell
return cell
}
6⃣为了将tableview和viewcontroller连接起来,回到storyboard,左边栏,从tableview拖线到viewcontroller,选择datasource
四,客户化tablecell
1⃣将cell的style设为custom
2⃣更改cell的高度,往里面添加imageview,lable等控件
五,将Model 中的数据绑定到TableView中
1⃣更改设置tableview行数代码如下
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return todos.count //将数据库中的数据数量动态地显示到tableview中
}
2⃣更改前文复用cell的方法的代码如下
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = self.tableView.dequeueReusableCellWithIdentifier("todoCell")! as UITableViewCell//取出目标cell
//获取数据库以及存放数据的控件,为了方便取出cell中的控件,事先给cell中的控件tag赋值101,102,103
let todo = todos[indexPath.row] as TodoModel
let image = cell.viewWithTag(101) as! UIImageView
let title = cell.viewWithTag(102) as! UILabel
let date = cell.viewWithTag(103) as! UILabel
//将数据库里的数据传递到控件中去
image.image = UIImage(named:todo.image)
title.text = todo.title
date.text = stringFromDate(todo.date)
//返回配置好的cell
return cell
}
六,给tableview增加删除功能
1⃣为了配置tableview的行为,让viewcontroller继承UITableViewDelegate协议
2⃣实现协议中的删除方法:
//增加删除方法
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath){
//捕获删除操作
if editingStyle == UITableViewCellEditingStyle.Delete{
//移除选定的数据
todos.removeAtIndex(indexPath.row)
//增加删除动画
self.tableview.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
3⃣为避免用户不知道删除手势,增加编辑按钮,由于编辑按钮在navigationBar上,因此先在viewController上添加Navigation:
在viewDidLoad中激活EditButton:
navigationItem.leftBarButtonItem = editButtonItem()
4⃣重写增加编辑按钮的方法
//增加编辑按钮
override func setEditing(editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
self.tableview.setEditing(editing, animated: animated)
}
七,实现新增item的功能
1⃣添加一个viewController作为新添item时的编辑页面,在页面上添加textFiled,datePicker等控件并拖线创建对应的属性
2⃣在navigationBar的右上角加一个BarButtonItem作为新建按钮,并将Systemitem属性设为Add
3⃣从add Button拖线到新的ViewController,segue属性设为push
4⃣新增一个名为DetailViewController的cocoatouch类,继承自UIViewController
在实现新增item功能前,先实现键盘自动隐藏功能
1⃣ViewController继承 UITextFieldDelegate 协议
2⃣在viewDidLoad中添加语句:
todoItem.delegate = self//将textField交给当前的viewController处理
3⃣重写两个方法:
//点击return,隐藏键盘
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
//点击父容器空白处,隐藏键盘
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
todoItem.resignFirstResponder()
}
5⃣拖线创建各个控件的属性
@IBOutlet weak var childButton: UIButton!
@IBOutlet weak var phoneButton: UIButton!
@IBOutlet weak var shoppingButton: UIButton!
@IBOutlet weak var travelButton: UIButton!
@IBOutlet weak var todoItem: UITextField!
@IBOutlet weak var todoDate: UIDatePicker!
6⃣拖线创建四个事件按键的方法
@IBAction func childTapped(sender: AnyObject) {
resetButton()
childButton.selected = true
}
@IBAction func phoneTapped(sender: AnyObject) {
resetButton()
phoneButton.selected = true
}
@IBAction func shoppingTapped(sender: AnyObject) {
resetButton()
shoppingButton.selected = true
}
@IBAction func travelTapped(sender: AnyObject) {
resetButton()
travelButton.selected = true
}
其中包含一个复位按键的方法
//一个复位按键的方法,点击某个按键前,先复位所有按键
func resetButton(){
childButton.selected = false
phoneButton.selected = false
shoppingButton.selected = false
travelButton.selected = false
}
7⃣拖线创建确定按键的方法
@IBAction func okTapped(sender: AnyObject) {
//根据选中的Button确定要存入哪些图片
var image:String!
if childButton.selected{
image = "child-selected"
}else if phoneButton.selected{
image = "phone-selected"
}else if shoppingButton.selected{
image = "shopping-cart-selected"
}else if travelButton.selected{
image = "travel-selected"
}
//当用户没有输入任何内容时,不予添加,关于图片的判断,后面会提到
if todoItem.text != ""{
let uuid = NSUUID().UUIDString //生成一个唯一ID
let todo = TodoModel(id:uuid,image:image,title: todoItem.text!,date:todoDate.date)//整合成一条数据
todos.append(todo) //将获得的数据存入本地全局数据库
}
}
8⃣为了保证确定之后能够返回原页面,在viewController中给确定按键绑定unwing方法
//新增Item页的OK按键的unwing方法
@IBAction func reload(segue:UIStoryboardSegue){
tableview.reloadData() //添加新数据后,刷新页面
}
八,增加修改功能
1⃣从cell拖线到DetailViewController,push,给新建的segue命名EditTodo
2⃣在DetailViewController中新建变量
var todo:TodoModel? //用于存放传进来的数据
3⃣在ViewController中重写prepareForSegue方法,该方法在触发segue时会调用,用于传递已有的数据
//传递数据前往修改页面
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//指定要使用的segue
if segue.identifier == "EditTodo" {
//获取前往的ViewController
let vc = segue.destinationViewController as! DetailViewController
//获取选中的那一行
let indexPath = tableview.indexPathForSelectedRow
//如果选到了数据,就将数据传送到目标页面的todo变量中
if let index = indexPath{
vc.todo = todos[index.row]
}
}
}
4⃣在DetailViewController中将传过来的数据呈现出来,viewDidLoad中添加如下代码
if todo == nil {
//当todo为空的时候,说明是在新建item,默认选中一张图片
childButton.selected = true
navigationItem.title = "New"
}else {
//否则,是在编辑item
navigationItem.title = "Edit"
//根据选中的图片,反向绑定到按键
switch todo?.image {
case "child-selected"? : childButton.selected = true
case "shopping-cart-selected"? : shoppingButton.selected = true
case "phone-selected"? : phoneButton.selected = true
case "travel-selected"? : travelButton.selected = true
default : print("image error")
}
//将已有的事件和日期呈现在页面上
todoItem.text = todo?.title
todoDate.setDate((todo?.date)!, animated: false)
}
5⃣同时修改okTapped方法,避免修改时新建item
//todo为空,说明是在新建一个事项
if todo == nil{
if todoItem.text != ""{
let uuid = NSUUID().UUIDString //生成一个唯一ID
let todo = TodoModel(id:uuid,image:image,title: todoItem.text!,date:todoDate.date)//整合成一条数据
todos.append(todo) //将获得的数据存入本地全局数据库
}
}else {//否则是在修改事项
todo?.image = image
todo?.title = todoItem.text!
todo?.date = todoDate.date
}
九,增加item的拖动功能
1⃣触发编辑时可选中移动的功能
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool{
return editing
}
2⃣获取选中数据并移动到指定位置
func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath){
let todo = todos.removeAtIndex(sourceIndexPath.row)
todos.insert(todo, atIndex: destinationIndexPath.row)
}
十,增加搜索功能(慕课网上的教程有bug,此处已做修正:向detail页面传递数据时要根据搜索结果还是完整列表做判断)
1⃣在cell上方添加控件searchBar and search Display
2⃣在viewController中定义用于存储搜索结果的数组
var filteredTodos:[TodoModel] = []
3⃣继承协议UISearchDisplayDelegate
4⃣实现方法搜索并显示的方法
func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String?) -> Bool{
//在本地数据库todos中搜索,title中包含搜索的字符串,将结果放到filteredTodos中
filteredTodos = todos.filter(){$0.title.rangeOfString(searchString!) != nil}
return true
}
5⃣由于引入了搜索,不得不对代码做一些调整
//设置tableview的行数
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
//加入搜索功能后,需要根据搜索结果刷新现实数据
if tableView == searchDisplayController?.searchResultsTableView{
return filteredTodos.count
}else{
return todos.count
}
}
//将数组中的内容呈现在界面上
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = self.tableView.dequeueReusableCellWithIdentifier("todoCell")! as UITableViewCell//取出cell
let todo:TodoModel
if tableView == searchDisplayController?.searchResultsTableView{
//搜索情况,将搜索结果展示
todo = filteredTodos[indexPath.row] as TodoModel
}else{
//非搜索情况
todo = todos[indexPath.row] as TodoModel
}
let image = cell.viewWithTag(101) as! UIImageView
let title = cell.viewWithTag(102) as! UILabel
let date = cell.viewWithTag(103) as! UILabel
image.image = UIImage(named:todo.image)
title.text = todo.title
date.text = stringFromDate(todo.date)
return cell
}
//加入搜索功能后,需要向viewController告知是哪一个列表要传递数据,完整列表还是搜索后的列表
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//指定要使用的segue
if segue.identifier == "EditTodo" {
//获取前往的ViewController
let vc = segue.destinationViewController as! DetailViewController
//从搜索结果传递数据
let indexPath = searchDisplayController?.searchResultsTableView.indexPathForSelectedRow
if let index = indexPath{
vc.todo = filteredTodos[index.row]
} else {
//从完整列表传递数据
let indexPath = tableview.indexPathForSelectedRow
if let index = indexPath{
vc.todo = todos[index.row]
}
}
}
}
//设置搜索结果的高度
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 70
}
6⃣通过修正偏移量实现搜索框的默认隐藏,在viewDidLoad中添加代码:
//隐藏search,即将search控件向上移动一个偏移量
var contentOffset = tableView.contentOffset
contentOffset.y += (searchDisplayController?.searchBar.frame.size.height)!
tableView.contentOffset = contentOffset