iOS Apps 开发(Swift)(10)——Implement Edit and Delete Behavior

本文档介绍了如何在Swift中为iOS应用添加编辑和删除菜品的功能。内容包括:配置TableViewCell,识别 segue 以区分添加和编辑操作,实现取消编辑时的正确响应,以及添加删除菜品的支持。通过实现这些功能,用户可以编辑现有菜品信息并从列表中删除菜品。
摘要由CSDN通过智能技术生成

前言:网上一直没有找到用Swift开发IOS的好的教程,所以找了官网的文档翻译一下算了。如有错误欢迎指正。博主首发CSDN,mcf171专栏。

博客链接:mcf171的博客

原文链接:Implement Edit and Delete Behavior

——————————————————————————————

在本次课程中,在本次课程中我们将允许用户进行编辑和删除菜品

学习目标

在本次课程中,你可以了解到:

  • 推拉模式和模态模式的导航区别
  • 基于 presentation 模式的 消失试图控制
  • 理解什么时候使用不同的向下转型的操作符
  • 为了应对复杂条件检查的 Leverage optional 
  • 使用切换识别器来了解当且的切换是哪一个

允许编辑存在的菜品

目前,我们可以添加新的菜品,下一步要做的事实现对已有菜品的编辑。

当用户点击一个菜品单元的时候我们需要下拉一个菜品场景,并且已经将相关的菜品信息填充好了。当用户修改之后点击保存按钮,我们需要更新信息并且覆盖菜品之前在列表中的信息。

配置 table view cell

1、如果助手编辑器是打开的,返回标准编辑器


2、打开storyboard

3、在画布中选中 table view cell

4、使用control + 拖拽的方式将table view cell 拖到菜品场景


当松开鼠标的时候一个快捷菜单会显示。


5、选择show

6、将菜品列表和菜品场景之间的导航控制器往下拖,我们可以看见一个新的切换


可以通过 Command + 减号来缩小范围

7、在画布中选择新添加的切换


8、在属性查看器中的 Identifier 选项中输入ShowDetail,然后回车


当切换被触发的时候,它就会将菜品场景的 视图控制推拉出来。

里程碑:运行app。在菜品列表场景,当点击一个菜品单元的时候我们可以调到菜品场景,但是里面的类容还是空的。

现在对相同的场景中我们有两种切换的方式了,所以我们需要定义什么时候用户是想编辑一个菜品,什么时候是想添加一个。

我们可以回想一下,prepareForSegue方法是在任何切换被执行前调用的,那么我们可以用这个方法来判断当前是哪个切换被触发了,并在菜品场景中显示合适的信息。我们可以基于身份识别器来区分不同的场景。

ps:小插曲,点击单个的菜品时并没有进行场景切换,我把Xcode退出重新打开一次工程发现就可以了。估计是Xcode的bug

识别当前是哪个切换

1、打开MealTableViewController.swift

2、找到prepareForSegue方法,去掉注释

// MARK: - Navigation
 
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}

因为MealTableVIewController是UITableViewController的子类,所以模板实现了一个大体的框架

3、删除两行注释,添加以下代码

if segue.identifier == "ShowDetail" {
}
else if segue.identifier == "AddItem" {
}

4、在第一个 if 语句中添加以下代码

let mealDetailViewController = segue.destinationViewController as! MealViewController
上述代码将 destinationViewController进行向下转型。注意我们的转型方法是使用了感叹号,也就是我们是一个强行的类型转换,转换成功的时候我么可以得到一个MealViewController类型的结果,不成功则会报一个运行时错误。

一般来说只有我们知道强转类型会成功的时候我们才使用这种方式。不然如果失败的话我们的app可能会崩溃。

5、在上面的代码下面加以下的代码

// Get the cell that generated this segue.
if let selectedMealCell = sender as? MealTableViewCell {
}

6、在 if语句中加入以下代码

let indexPath = tableView.indexPathForCell(selectedMealCell)!
let selectedMeal = meals[indexPath.row]
mealDetailViewController.meal = selectedMeal
上述代码是获取了在列表中选中的Meal 对象,然后将这个值付给了mealDetailViewController中的meal属性
7、在 else中输出消息

print("Adding new meal.")


现在的方法应该如下:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "ShowDetail" {
        let mealDetailViewController = segue.destinationViewController as! MealViewController
        
        // Get the cell that generated this segue.
        if let selectedMealCell = sender as? MealTableViewCell {
            let indexPath = tableView.indexPathForCell(selectedMealCell)!
            let selectedMeal = meals[indexPath.row]
            mealDetailViewController.meal = selectedMeal
        }
    }
    else if segue.identifier == "AddItem" {
        print("Adding new meal.")
    }
}
现在我们已经完成逻辑上的代码了,接下来我们需要在MealViewController.swift中做一点小改动,来确保UI正确的加载。

修改 viewDidLoad 方法
1、打开MealViewController.swift
2、找到viewDidLoad方法

override func viewDidLoad() {
    super.viewDidLoad()
    
    // Handle the text field’s user input via delegate callbacks.
    nameTextField.delegate = self
    
    // Enable the Save button only if the text field has a valid Meal name.
    checkValidMealName()
}

3、在 nameTextField.delegate 下添加下述代码

// Set up views if editing an existing Meal.
if let meal = meal {
    navigationItem.title = meal.name
    nameTextField.text   = meal.name
    photoImageView.image = meal.photo
    ratingControl.rating = meal.rating
}


完整的viewDidLoad方法应该如下
override func viewDidLoad() {
    super.viewDidLoad()
    
    // Handle the text field’s user input via delegate callbacks.
    nameTextField.delegate = self
    
    // Set up views if editing an existing Meal.
    if let meal = meal {
        navigationItem.title = meal.name
        nameTextField.text   = meal.name
        photoImageView.image = meal.photo
        ratingControl.rating = meal.rating
    }
    
    // Enable the Save button only if the text field has a valid Meal name.
    checkValidMealName()
}

里程碑:运行app。我们点击table view 中的一个单元将导航到菜品场景,同时看见数据也有了。但是当我们点击保存按钮的时候还是添加一个新的菜品。


为了能够覆盖菜品列表中存在了的菜品,我们需要修改 unwindiToMealList 方法来处理这两种情况。1、我们需要添加菜品。2、我们需要替换已有的菜品。这个方法只有在点击保存按钮的时候才会出发,所以我们不需要变动取消按钮。

修改 unwindToMealList 方法的实现

1、打开 MealTableViewController.swift

2、找到 unwindToMealList 方法

@IBAction func unwindToMealList(sender: UIStoryboardSegue) {
    if let sourceViewController = sender.sourceViewController as? MealViewController, meal = sourceViewController.meal {
        // Add a new meal.
        let newIndexPath = NSIndexPath(forRow: meals.count, inSection: 0)
        meals.append(meal)
        tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
    }
}

3、在第一个 if 语句的开始添加一个 if 语句

if let selectedIndexPath = tableView.indexPathForSelectedRow {
}
上述代码检测在table view 中一行是否被选中,如果是的话,意味着用户点击了一个菜品来编辑。

4、在这个if语句中添加一下代码

// Update an existing meal.
meals[selectedIndexPath.row] = meal
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
上述代码第一行是更新数组中的菜品信息,第二行是重新加载视图中的数据

5、在if语句之后添加else 语句

else {
    // Add a new meal.
    let newIndexPath = NSIndexPath(forRow: meals.count, inSection: 0)
    meals.append(meal)
    tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
}

最终我们的unwindToMealList方法为:

@IBAction func unwindToMealList(sender: UIStoryboardSegue) {
    if let sourceViewController = sender.sourceViewController as? MealViewController, meal = sourceViewController.meal {
        if let selectedIndexPath = tableView.indexPathForSelectedRow {
            // Update an existing meal.
            meals[selectedIndexPath.row] = meal
            tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
        }
        else {
            // Add a new meal.
            let newIndexPath = NSIndexPath(forRow: meals.count, inSection: 0)
            meals.append(meal)
            tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
        }
    }
}

里程碑:运行app。现在应该可以点击table view 中的单元来导航到菜品场景,同时可以看见预加载了菜品。如果我们点击保存,应该是覆盖了存在的菜品。

对于已有的菜品取消编辑

用户可能取消对一个菜品的编辑,从而返回菜品列表。在这种情况我们需要设置取消按钮的行为来是场景在合适的情况下消失。

消失类型依赖于呈现的类型,我们需要实现一个方法来决定当用户点击取消按钮的时候当前的场景是如何显示的。如果是presented modally时候,那么会通过dismissViewControllerAnimated 方法消失,如果是通过推拉当行的形式,那么应该由呈现的导航控制来使场景消失。

改变取消按钮的实现方式

1、打开MealViewController.swift

2、找到取消按钮方法

@IBAction func cancel(sender: UIBarButtonItem) {
    dismissViewControllerAnimated(true, completion: nil)
}

3、在这个方法实现的最前面添加一行代码

// Depending on style of presentation (modal or push presentation), this view controller needs to be dismissed in two different ways.
let isPresentingInAddMealMode = presentingViewController is UINavigationController

这个创建了一个布尔值表明显示视图控制器是否是UINavigationController 类型。常量名意味着这个菜品场景是通过添加按钮显示的。

    4、在这一行之后我们需要添加一个 if 语句。并且删除之前的代码
    if isPresentingInAddMealMode {
        dismissViewControllerAnimated(true, completion: nil)
    }

    6、添加else 语句
    else {
        navigationController!.popViewControllerAnimated(true)
    }
    popViewControllerAnimated方法是通过动画的方式切换弹出在导航堆最上面的试图控制。

    现在我们的取消方法应该为:

    @IBAction func cancel(sender: UIBarButtonItem) {
        // Depending on style of presentation (modal or push presentation), this view controller needs to be dismissed in two different ways.
        let isPresentingInAddMealMode = presentingViewController is UINavigationController
        
        if isPresentingInAddMealMode {
            dismissViewControllerAnimated(true, completion: nil)
        }
        else {
            navigationController!.popViewControllerAnimated(true)
        }
    }

    里程碑:运行app。我们点击+号。然后献祭取消,我们应该回到菜单列表

    支持菜品的删除

    接下来我们需要让用户可以从菜品列表中删除一个菜品。我们可以通过用户让table view 编入编辑模式,然后用户就可以进行删除操作了。我们可以通过在导航栏添加一个编辑按钮来实现。

    在table view 添加一个编辑按钮

    1、打开MealTableViewController.swift

    2、找到viewDidLoad方法

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Load the sample data.
        loadSampleMeals()
    }

    3、在super.viewDidLoad方法前添加以下代码

    // Use the edit button item provided by the table view controller.
    navigationItem.leftBarButtonItem = editButtonItem()

    上述代码在导航栏的左边添加了一个编辑按钮

    现在 viewDidLoad方法应该为:

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Use the edit button item provided by the table view controller.
        navigationItem.leftBarButtonItem = editButtonItem()
        
        // Load the sample data.
        loadSampleMeals()
    }
    里程碑:运行app,我们发现一个编辑按钮出现在了导航栏的左边,如果我们点击编辑按钮我们会进入编辑模式,但是我们不能删除任何菜品。因为我们还没有实现。

      为了在table view 中进行一些编辑行为,我们需要实现他的代理方法。tableView(_:commitEditingStyle:forRowAtIndexPath:)这个代理方法管理了在编辑模式下它的行。

      我们也可以通过取消注释了的tableView(_:canEditRowAtIndexPath)方法来支持编辑

      删除菜品

      1、打开MealTableViewController.swift,找到注释了的方法tableView(_:commitEditingStyle:forRowAtIndexPath:)。然后去掉注释。

      // Override to support editing the table view.
      override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
          if editingStyle == .Delete {
              // Delete the row from the data source
              tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .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
          }
      }

      2、在注释//Delete the row from the  data source 下添加代码

      meals.removeAtIndex(indexPath.row)

      3、找到 tableView(_:canEditRowAtIndexPath)并取消注释

      // Override to support conditional editing of the table view.
      override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
          // Return false if you do not want the specified item to be editable.
          return true
      }
      现在我们的 tableView(_:commitEditingStyle:forRowAtIndexPath:)方法应该为:

      // Override to support editing the table view.
      override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
          if editingStyle == .Delete {
              // Delete the row from the data source
              meals.removeAtIndex(indexPath.row)
              tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .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
          }
      }

      里程碑:运行app。当我们点击编辑按钮的时候回进入编辑模式。我们可以先点击左边的减号,然后点击右边的删除按钮删除。同时我们也可以通过将想删除的菜品往左边滑动进行快速的删除。

      ps:完整的项目文件Download File















      • 0
        点赞
      • 0
        收藏
        觉得还不错? 一键收藏
      • 0
        评论
      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

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

      抵扣说明:

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

      余额充值