一个简单的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

 

posted on 2015-12-10 17:19  罗小夕  阅读(711)  评论(0编辑  收藏  举报

导航