使用Swift从头开始安装iOS:构建购物清单应用程序2

上一课中 ,我们为购物清单应用程序奠定了基础。 在本课程的第一部分中,我们通过允许用户编辑和删除列表中的项目来进一步优化应用程序。 稍后,我们添加了从列表中选择商品以创建购物清单的功能。

1.删除项目

从用户体验和整体可用性方面,从列表中删除项目是一项重要的补充。 添加此功能涉及:

  • 从视图控制器的items属性中删除该项目
  • 更新表格视图
  • 将更改保存到磁盘

让我们看看这在实践中是如何工作的。 我们首先需要在导航栏中添加一个编辑按钮。 在视图控制器的viewDidLoad()方法中,创建UIBarButtonItem的实例,并将其分配给视图控制器的navigationItem属性的rightBarButtonItem属性。

正如我们在做上一课 ,我们通过调用创建栏按钮项目init(barButtonSystemItem:target:action:) ,传入.Edit ,成员值UIBarButtonSystemItemself为目标,和"editItems:"作为选择。

override func viewDidLoad() {
    super.viewDidLoad()
    
    // Register Class
    tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: CellIdentifier)
    
    // Create Add Button
    navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "addItem:")
    
    // Create Edit Button
    navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Edit, target: self, action: "editItems:")
}

如下所示, editItems(_:)的实现只是一行代码。 每当用户点击编辑按钮时,表视图就会切换到编辑模式或从中退出。 我们通过使用一些技巧来做到这一点。 我们询问表视图是否处于编辑模式,该模式返回Bool值,然后反转返回的值( true变为false ,反之亦然)。 我们在表格视图上调用的方法是setEditing(_:animated:) ,这是一个接受动画参数的专用设置器。

func editItems(sender: UIBarButtonItem) {
    tableView.setEditing(!tableView.editing, animated: true)
}

如果您在模拟器中运行购物清单应用程序并点击“编辑”按钮,则应该看到表格视图已切换为进入和退出编辑模式。

将表视图切换为进入和退出编辑模式
将表视图切换为进入和退出编辑模式

UITableViewDataSource协议的两种方法对于在表格视图中启用编辑很重要:

  • tableView(_:canEditRowAtIndexPath:)
  • tableView(_:commitEditingStyle:forRowAtIndexPath:)

如果用户点击“编辑”按钮,则表视图通过向数据源发送tableView(_:canEditRowAtIndexPath:)消息来询问其数据源可编辑的行。 如果为特定的索引路径返回true ,则表视图将指示相应的表视图单元格需要切换为进入或退出编辑模式,具体取决于表视图的编辑模式。 这将转换为显示或隐藏其编辑控件的表格视图单元格。 如下所示实现tableView(_:canEditRowAtIndexPath:)方法,以查看其实际工作方式。

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    if indexPath.row == 1 {
        return false
    }
    
    return true
}

tableView(_:canEditRowAtIndexPath:)的上述实现使用户可以编辑表视图中的每一行,但第二行除外。 在模拟器中运行该应用程序,然后尝试一下。

添加编辑表格视图的功能

对于购物清单应用程序,用户应该能够编辑表视图中的每一行。 这意味着tableView(_:canEditRowAtIndexPath:)应该始终返回true

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    return true
}

您可能已经注意到,当您尝试删除表视图中的一行时,什么也没有发生。 难题尚未结束。 每当用户点击一行的删除按钮时,表视图就会向其数据源发送tableView(_:commitEditingStyle:forRowAtIndexPath:) 。 相应方法的第二个参数表示用户执行了什么类型的操作,即插入或删除一行。 对于购物清单应用程序,我们将仅实现对从表视图中删除行的支持。

从表视图中删除行涉及:

  • 从视图控制器的items属性中删除相应的items
  • 通过删除相应的行来更新表视图

让我们检查tableView(_:commitEditingStyle:forRowAtIndexPath:) 。 该方法首先检查编辑样式是否等于.DeleteUITableViewCellEditingStyle的成员值),因为我们只想允许用户从表视图中删除行。

如果编辑样式等于.Delete ,则从items属性中删除相应的items ,从表视图中删除相应的行,并将更新后的项目列表保存到磁盘。

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == .Delete {
        // Delete Item from Items
        items.removeAtIndex(indexPath.row)
        
        // Update Table View
        tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Right)
        
        // Save Changes
        saveItems()
    }
}

在模拟器中运行该应用程序,然后删除一些项目。 不要忘记退出并重新启动应用程序,以验证项目已从列表中永久删除。

2.编辑项目

我们可以重用AddItemViewController类来编辑项目。 但是,由于使用单个视图控制器添加和编辑项目通常会使实现复杂化,因此我通常最终会创建一个单独的类来编辑项目。 最初可能会导致我们重复一遍,但会给我们带来更大的灵活性。

步骤1:创建Editing View Controller

创建一个新的UIViewController子类,并将其命名为EditItemViewControllerAddItemViewControllerEditItemViewController类的接口非常相似。

import UIKit

protocol EditItemViewControllerDelegate {
    func controller(controller: EditItemViewController, didUpdateItem item: Item)
}

class EditItemViewController: UIViewController {

    @IBOutlet var nameTextField: UITextField!
    @IBOutlet var priceTextField: UITextField!
    
    var item: Item!
    
    var delegate: EditItemViewControllerDelegate?
    
    ...

}

区别在于添加了属性item ,以存储对正在编辑的项目的引用以及EditItemViewControllerDelegate协议的方法的定义。 请注意, item属于Item!类型Item! ,为强制展开的可选。 由于没有项目要编辑时,编辑项目视图控制器毫无用处,因此我们希望item属性始终具有一个值。

打开Main.storyboard ,从对象库中拖动UIViewController实例,将其类设置为EditItemViewController ,然后从列表视图控制器到编辑项目视图控制器创建手动显示序列。 将segue的标识符设置为EditItemViewController

创建到编辑项目视图控制器的推送选择

将文本字段从“ 对象库 ”拖到视图控制器的视图中,然后将它们定位,如下图所示。 选择顶部的文本字段,打开“ 属性”检查器 ,然后在“ 占位符”字段中输入“ 名称 ”。 选择底部的文本字段,然后在“ 属性”检查器中将其占位符文本设置为Price ,并将Keyboard设置为Number Pad 。 选择视图控制器对象,打开Connections Inspector ,然后将nameTextFieldpriceTextField插座与用户界面中的相应文本字段连接。

配置文本字段

在视图控制器的viewDidLoad()方法中,像在AddItemViewController类中一样创建保存按钮。

// MARK: -
// MARK: View Life Cycle
override func viewDidLoad() {
    super.viewDidLoad()
    
    // Create Save Button
    navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Save, target: self, action: "save:")
}

save(_:)操作的实现与我们在AddItemViewController类中实现的操作非常相似。 不过,我想指出一些细微的差异。

// MARK: -
// MARK: Actions
func save(sender: UIBarButtonItem) {
    if let name = nameTextField.text, let priceAsString = priceTextField.text, let price = Float(priceAsString) {
        // Update Item
        item.name = name
        item.price = price
        
        // Notify Delegate
        delegate?.controller(self, didUpdateItem: item)
        
        // Pop View Controller
        navigationController?.popViewControllerAnimated(true)
    }
}

我们没有直接将名称和价格值传递给委托,而是直接更新了项目并将更新后的项目传递给视图控制器的委托。 由于视图控制器是导航控制器的子视图控制器,因此我们通过从导航堆栈中弹出视图控制器来关闭视图控制器。

步骤2:显示编辑项目视图控制器

几分钟后,我们将实现使用户能够从列表视图控制器中选择商品并将其添加到购物清单的功能。 用户可以通过在列表视图中点击一行来执行此操作。 问题是,如果保留点击以将商品添加到购物清单的行,用户将如何编辑商品?

UIKit框架针对此用例提供了一个详细的披露按钮。 详细信息披露按钮位于表格视图单元格的右侧。 要将详细信息披露按钮添加到表视图单元格,我们需要在列表视图控制器中重新访问tableView(_:cellForRowAtIndexPath:)并进行如下所示的修改。

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    // Dequeue Reusable Cell
    let cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier, forIndexPath: indexPath)
    
    // Fetch Item
    let item = items[indexPath.row]
    
    // Configure Table View Cell
    cell.textLabel?.text = item.name
    cell.accessoryType = .DetailDisclosureButton
    
    return cell
}

每个表视图单元格都有一个accessoryType属性。 在tableView(_:cellForRowAtIndexPath:) ,我们将其设置为.DetailDisclosureButton ,它是UITableViewCellAccessoryType的成员值。 您可能已经注意到Apple的工程师不喜欢简称。

轻按详细信息披露按钮后,表格视图如何通知其委托人? 毫不奇怪, UITableViewDelegate协议为此定义了tableView(_:accessoryButtonTappedForRowWithIndexPath:)方法。 看一下它的实现。

// MARK: -
// MARK: Table View Delegate Methods
override func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath) {
    // Fetch Item
    let item = items[indexPath.row]
    
    // Update Selection
    selection = item
    
    // Perform Segue
    performSegueWithIdentifier("EditItemViewController", sender: self)
}

我们从items属性中获取正确的items ,并将其存储在selection ,该属性是我们稍后将声明的列表视图控制器的属性。 然后,我们使用标识符EditItemViewController进行segue。

在更新prepareForSegue(_:sender:)的实现之前,我们需要声明用于存储所选项目的属性。 我们还需要使ListViewController类符合EditItemViewControllerDelegate协议。

import UIKit

class ListViewController: UITableViewController, AddItemViewControllerDelegate, EditItemViewControllerDelegate {

    let CellIdentifier = "Cell Identifier"
    
    var items = [Item]()
    var selection: Item?
    
    ...

}

如以下所示, prepareForSegue(_:sender:)的更新实现非常简单。 我们获得对编辑项目视图控制器的引用,并设置其delegateitem属性。 因为selection属性的类型为Item? ,我们将其绑定到item常量以安全地将其解包。

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "AddItemViewController" {
      ...
        
    } else if segue.identifier == "EditItemViewController" {
        if let editItemViewController = segue.destinationViewController as? EditItemViewController, let item = selection {
            editItemViewController.delegate = self
            editItemViewController.item = item
        }
    }
}

EditItemViewController的实现即将完成。 在其viewDidLoad()方法中,我们使用item属性的数据填充文本字段。 因为文本字段的text属性的类型为String? ,我们使用字符串插值从商品的price属性的Float值创建字符串。 字符串插值在Swift中非常强大。

override func viewDidLoad() {
    super.viewDidLoad()
    
    // Create Save Button
    navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Save, target: self, action: "save:")
    
    // Populate Text Fields
    nameTextField.text = item.name
    priceTextField.text = "\(item.price)"
}

步骤3:采用委托协议

采用EditItemViewControllerDelegate协议意味着实现controller(_:didUpdateItem:)方法,如下所示。 我们不更新表视图的数据源可能会让您感到惊讶。 我们在委托方法中所做的就是重新加载表视图的一行。

// MARK: -
// MARK: Edit Item View Controller Delegate Methods
func controller(controller: EditItemViewController, didUpdateItem item: Item) {
    // Fetch Index for Item
    if let index = items.indexOf(item) {
        // Update Table View
        tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: 0)], withRowAnimation: .Fade)
    }
    
    // Save Items
    saveItems()
}

我们不需要更新表视图的数据源( items数组)的原因是,已更新的项目是通过引用传递给编辑项目视图控制器的。 换句话说,编辑项视图控制器更新的对象与items数组中包含的对象相同。 这是使用类实例的好处之一。 它们通过引用传递。

不要忘记保存项目列表,以确保将编辑内容写入磁盘。 运行该应用程序以测试编辑功能。

3.创建购物清单视图控制器

在探索购物清单视图控制器的数据源之前,让我们创建一些可以使用的支架。 创建一个新的UITableViewController子类,并将其命名为ShoppingListViewController

打开ShoppingListViewController.swift并添加[Item]类型的两个属性:

  • items ,其中将包含项目的完整列表
  • shoppingList ,它将仅包含购物清单中的项目
import UIKit

class ShoppingListViewController: UITableViewController {

    var items = [Item]()
    var shoppingList = [Item]()
    
    ...

}

这个想法是每次对列表进行更改时都加载项目列表,解析项目列表,然后仅提取将inShoppingList属性设置为true那些项目。 然后将这些项目添加到shoppingList数组。

另一种选择是将购物清单存储在单独的文件中。 这种方法的缺点是我们必须保持列表视图控制器中的商品和购物清单中的商品同步。 如果你问我,这就是麻烦。

步骤1:添加属性观察者

财产观察员是回应财产价值变化的好方法。 让我们看看如何在items的值更改时利用属性观察器更新shoppingList属性。

var items = [Item]() {
    didSet {
        buildShoppingList()
    }
}

语法很容易理解。 财产观察员被包裹在一个封闭。 有两种类型的属性观察器:

  • willSet ,在设置属性的新值之前调用
  • didSet ,在设置属性的新值后调用

在上面的示例中,只要将新值分配给items ,我们就会调用buildShoppingList() 。 这就是buildShoppingList()的实现。

// MARK: -
// MARK: Helper Methods
func buildShoppingList() {
    shoppingList = items.filter({ (item) -> Bool in
        return item.inShoppingList
    })
}

我们过滤items数组的元素,仅包括inShoppingList设置为true 。 结果分配给shoppingList

我们还创建了一个didSet的属性观察者shoppingList财产。 在此属性观察器中,我们更新表视图以反映shoppingList的内容,该表视图的数据源。

var shoppingList = [Item]() {
    didSet {
        tableView.reloadData()
    }
}

步骤2:显示购物清单

到目前为止,实现UITableViewDataSource协议的方法应该是UITableViewDataSource 。 看一下下面的实现。

// MARK: -
// MARK: Table View Data Source Methods
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return shoppingList.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    // Dequeue Reusable Cell
    let cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier, forIndexPath: indexPath)
    
    // Fetch Item
    let item = shoppingList[indexPath.row]
    
    // Configure Table View Cell
    cell.textLabel?.text = item.name
    
    return cell
}

不要忘记声明单元重用标识符并像在ListViewController类中一样注册UITableViewCell类。

import UIKit

class ShoppingListViewController: UITableViewController {

    let CellIdentifier = "Cell Identifier"
    
    ...

}
// MARK: -
// MARK: View Life Cycle
override func viewDidLoad() {
    super.viewDidLoad()
    
    // Register Class
    tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: CellIdentifier)
}

步骤3:载入项目

如前所述,使用项目列表存储和构建购物清单的主要优势在于应用程序仅将每个项目存储在一个地方。 这使得更新列表和购物清单中的项目变得轻而易举。 loadItems()pathForItems()方法与我们在ListViewController类中实现的方法相同。

private func loadItems() {
    if let filePath = pathForItems() where NSFileManager.defaultManager().fileExistsAtPath(filePath) {
        if let archivedItems = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as? [Item] {
            items = archivedItems
        }
    }
}
private func pathForItems() -> String? {
    let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
    
    if let documents = paths.first, let documentsURL = NSURL(string: documents) {
        return documentsURL.URLByAppendingPathComponent("items").path
    }
    
    return nil
}

当您发现重复的代码时,您会听到警铃响起。 实现新功能时,重复代码没有问题。 但是,此后,您应该考虑重构代码,以最大程度减少应用程序代码库中的重复次数。 这是软件开发中一个非常重要的概念,通常被称为DRY不要重复自己 。 克里斯·彼得斯(Chris Peters)在Envato Tuts +上写了一篇有关DRY编程的精彩文章

在使用ShoppingListViewController类之前,我们需要更新该类的viewDidLoad()方法。 除了设置视图控制器的标题之外,我们还加载了项目列表,如我们先前所见,它会自动填充shoppingList数组。

override func viewDidLoad() {
    super.viewDidLoad()
    
    // Set Title
    title = "Shopping List"
    
    // Load Items
    loadItems()
    
    // Register Class
    tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: CellIdentifier)
}

最后一步是通过更新情节提要来初始化购物清单视图控制器。 这涉及添加UITableViewController实例,将其类设置为ShoppingListViewController ,将其嵌入到导航控制器中,并在标签栏控制器和导航控制器之间创建关系。 选择购物清单视图控制器的表视图,并将原型单元数设置为0

创建购物清单视图控制器

运行该应用程序以查看是否一切正常。 当然,购物清单目前为空,我们无法将商品添加到购物清单中。 让我们在下一步中进行补救。

4.将项目添加到购物清单

如我先前所写,其想法是在列表视图控制器中点击商品时将其添加到购物清单中。 为了改善用户体验,如果购物清单中有商品,我们会在商品名称的左侧显示一个绿色的选中标记。 如果点击了购物清单中已经存在的商品,则将其从购物清单中删除,绿色的对勾消失。 这意味着我们需要看一下UITableViewDelegate协议的tableView(_:didSelectRowAtIndexPath:)方法。

在实现tableView(_:didSelectRowAtIndexPath:) ,请下载本课的源文件。 在名为Resources的文件夹中,找到名为checkmark.pngcheckmark@2x.png的文件。 将这两个文件都添加到项目中,因为稍后需要它们。

tableView(_:didSelectRowAtIndexPath:)的第一行中,我们向表视图发送一条deselectRowAtIndexPath(_:animated:)消息,以取消选择用户点击的行。 每当点击一行时,都应仅在瞬间突出显示该行,因此需要添加该行。 接下来,我们获取与用户的选择相对应的商品,并更新该inShoppingList属性(“ true变为“ false ,反之亦然)。

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    tableView.deselectRowAtIndexPath(indexPath, animated: true)
    
    // Fetch Item
    let item = items[indexPath.row]
    
    // Update Item
    item.inShoppingList = !item.inShoppingList
    
    // Update Cell
    let cell = tableView.cellForRowAtIndexPath(indexPath)
    
    if item.inShoppingList {
        cell?.imageView?.image = UIImage(named: "checkmark")
    } else {
        cell?.imageView?.image = nil
    }
    
    // Save Items
    saveItems()
}

根据商品的inShoppingList属性的值,我们显示或隐藏绿色的选中标记。 我们发现通过设置复选标记image表视图单元格的财产imageView财产。 表格视图单元格的左侧包含一个图像视图( UIImageView类的实例)。 通过将图像视图的image属性设置为nil ,图像视图为空白,不显示图像。

tableView(_:didSelectRowAtIndexPath:)的实现通过将项目列表保存到磁盘以确保更改是永久性的操作而结束。

当用户点击列表视图控制器中的商品时,购物列表如何得知? 如果对列表视图控制器中的项目列表进行了更改,则购物列表视图控制器将不会自动更新其表视图。 为了避免紧密耦合,我们不希望列表视图控制器和购物列表视图控制器直接相互通信。

解决此问题的一种方法是使用通知。 每当列表视图控制器对项目列表进行更改时,它都会将带有特定名称的通知发布到通知中心(一个管理通知的对象)。 对某些通知感兴趣的对象可以将自己添加为这些通知的观察者,这意味着它们可以在这些通知发布到通知中心时做出响应。

这一切如何运作? 涉及三个步骤:

  • 购物清单视图控制器从告诉通知中心它有兴趣接收名称为ShoppingListDidChangeNotification通知开始
  • 每当列表视图控制器更新项目列表时,它都会向通知中心发布通知
  • 当购物清单视图控制器从通知中心收到通知时,它会更新其数据源和表视图以作为响应

在执行我刚刚描述的三个步骤之前,最好仔细看一下NSNotificationCenter类。

简而言之, NSNotificationCenter管理通知的广播。 应用程序中的对象可以在通知中心注册,以使用addObserver(_:selector:name:object:)接收通知,其中:

  • 一个参数是将接收通知的对象(观察者)
  • selector是观察者在收到通知时调用的操作
  • name是通知的名称
  • object是触发通知发送的对象

如果最后一个参数设置为nil ,则观察者将收到具有指定名称的每个通知。

步骤1:接收通知

重新访问ShoppingListViewController类的viewDidLoad()方法,并将视图控制器实例添加为观察者,以接收名称为ShoppingListDidChangeNotification通知。

override func viewDidLoad() {
    super.viewDidLoad()
    
    // Set Title
    title = "Shopping List"
    
    // Load Items
    loadItems()
    
    // Register Class
    tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: CellIdentifier)
    
    // Add Observer
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateShoppingList:", name: "ShoppingListDidChangeNotification", object: nil)
}

当视图控制器接收到具有该名称的通知时触发的动作是updateShoppingList(_:) 。 最后一个参数objectnil ,因为哪个对象发送了通知都没有关系。

步骤2:回应通知

观察者收到通知时触发的方法具有特定的格式,如下所示。 它接受一个参数,即NSNotification类型的通知对象。

通知对象保留对发布通知的对象的引用,并且它还可以包含带有附加信息的字典。 updateShoppingList(_:)方法的实现非常简单。 我们在视图控制器上调用loadItems() ,这意味着项目列表是从磁盘加载的。 剩下的事情自动发生,这要归功于我们之前实现的属性观察器。

// MARK: -
// MARK: Notification Handling
func updateShoppingList(notification: NSNotification) {
    loadItems()
}

步骤3:发送通知

难题的第三部分是,只要列表视图控制器更改了项目列表,就会发布通知。 我们可以在ListViewController类的saveItems()方法中执行此操作。

private func saveItems() {
    if let filePath = pathForItems() {
        NSKeyedArchiver.archiveRootObject(items, toFile: filePath)
        
        // Post Notification
        NSNotificationCenter.defaultCenter().postNotificationName("ShoppingListDidChangeNotification", object: self)
    }
}

我们首先通过调用NSNotificationCenter类上的defaultCenter()来请求对默认通知中心的引用。 接下来,我们在默认的通知中心上调用postNotificationName(_:object:) ,传入通知名称ShoppingListDidChangeNotification ,以及发布通知的对象。

在生成项目之前,请确保如下所示修改ListViewController.swift中的 tableView(_:cellForRowAtIndexPath:) ,以显示购物清单中已经存在的商品的绿色复选标记。

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    // Dequeue Reusable Cell
    let cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier, forIndexPath: indexPath)
    
    // Fetch Item
    let item = items[indexPath.row]
    
    // Configure Table View Cell
    cell.textLabel?.text = item.name
    cell.accessoryType = .DetailDisclosureButton
    
    if item.inShoppingList {
        cell.imageView?.image = UIImage(named: "checkmark")
    } else {
        cell.imageView?.image = nil
    }
    
    return cell
}

运行购物清单应用程序以试一下。 您是否注意到对商品的编辑会自动反映在购物清单中?

完成购物清单申请

准备出版吗?

大。 将购物清单应用程序发布到App Store的按钮在哪里? 我们还没有完成。 尽管我们已经奠定了购物清单应用程序的基础,但尚未发布。 还有一些事情要考虑。

可扩展性

购物清单应用程序是购物清单的适度实现。 如果应用程序包含数百或数千个项目,那么就应该添加搜索功能以及按字母顺序对项目进行排序的部分(就像我们在本系列前面所做的那样)。 重要的是要意识到,每次更新项目时,项目列表便会完整地写入磁盘。 当列表很小时,这没问题,但是随着时间的推移,列表会增加到成百上千个项目。

人际关系

此外,用户可能希望保存多个购物清单。 您将如何处理? 一种选择是将每个购物清单存储在单独的文件中,但是您将如何处理对商品所做的更改? 您是否要更新包含该商品的每个购物清单? 当您开始处理关系时,最好选择一个SQLite数据存储。

如果您选择走这条路,Core Data是一个很好的伴侣。 它是一个功能强大的框架,具有许多功能,这些功能使我们的购物清单应用程序中的许多代码都已过时。 确实,Core Data会带来更多的开销,因此首先考虑Core Data是否适合您的应用程序(换句话说,是否值得)是至关重要的。

附加功能

附加功能还有很多潜力。 商品的price属性在购物清单应用程序的当前实现中未使用。 同样,如果用户可以通过点击商品从购物清单中检出商品,那就太好了。 如您所见,购物清单应用程序的当前实现只是一个适度的开始。

结论

即使购物清单应用程序尚未为App Store准备就绪,您也不能否认其按计划工作,并且向您展示了Cocoa开发的几个新方面,例如通知和实现自定义委托协议。

您现在知道了iOS SDK的期望以及iOS的发展。 由您决定是否要继续自己的旅程并成为一名熟练的iOS开发人员。 如果您选择继续进行iOS开发,那么我将在本系列的下一部分和最后一部分中为您提供一些不错的资源。

如果您有任何问题或意见,可以将其留在下面的评论中,或通过Twitter与我联系。

翻译自: https://code.tutsplus.com/tutorials/ios-from-scratch-with-swift-building-a-shopping-list-application-2--cms-25516

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值