iOS Apprentice中文版-从0开始学iOS开发-第十三课

在清单中添加新的项目

目前为止,你的列表中仅包含几个固定的行,但是我们这个app的目的是可以让用户创建属于自己的列表。因此,你需要给予用户自己添加项目的能力。

在这一小节你要增加一个叫做navigation bar(导航bar)的东西到app的顶部。这个bar有一个蓝色的“➕”按钮,点击后可以打开一个新的界面,使你可以为新的项目输入名称。

但你点击“Done(完成)”按钮后,新的项目会被增加到列表里。

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第1张图片
点击navigation bar上的➕按钮打开项目添加界面

展现一个新的界面用于添加项目是许多app的一个基本套路。一旦你学会这个技巧,你会离成熟的iOS开发者更进一步。

以下是你在这一节中要做的事情。

1、添加一个navigation controller(导航控制器)

2、将Add按钮放入navigation bar

3、当你点击Add按钮时,伪造一些数据进去

4、通过滑动删除一行

5、在添加项目的界面中使用户可以输入新项目的名称

一如既往的,我们先将这些功能分解成小的步骤。当你将Add按钮放入界面后,你先在代码里伪造一条数据添加到列表中。与一次性写完添加项目的代码不同,我们会假装很多东西已经存在了,仅仅是处理一小部分内容,然后一点点的扩充它。

当你伪造的数据可以成功的添加到列表中后,我们就可以开始真正把添加新项目这个界面做完了。

Navigation Controllers(导航控制器)

首先我们来添加这个navgation bar。你也许在对象库(Object Library)中见过一个叫做Navigation Bar的东西。你可以把它拖出来,并且放置在屏幕的顶端。不过,我们这次不打算这样做。

取而代之的是,你会用嵌入的方式将view controller添加到navigation controller内。

在iOS用户界面组件中,navigation controller(导航控制器)的使用频率可能是仅次于table view的。它是一种使你可以一个界面转到另一个界面的东西:

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第2张图片
导航控制的工作方式

UINavigationController会为你照顾绝大多数关于navigation bar的内容,这极大的提高了你的编程效率。navigation bar的中间有一个标题,和左边的一个“back(返回)”按钮用户自动将用户带回之前的界面。你可以在右边放入一个自定义的按钮。

添加导航控制器是非常容易的。

打开Main.storyboard并且选择Checklist View Controller(黄色图标那个)。

在Xcode顶部的菜单中,选择Editor -> Embed In -> Navigation Controller。

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第3张图片
将view controller放入navigation controller中

这样,界面建造器就添加了一个新的Navgation Controller场景并且将自己和view controller联系起来。

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第4张图片
现在navgation controller连接着你的view controller

当app启动时,Checklist View Controller自动被放入Navigation Controller中。

运行app试试看。

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第5张图片
现在app的顶部有了一个navigation bar

app在外观上与之前唯一的不同就是顶部有了一个navigation bar。感谢这件事,状态栏不再会覆盖列表第一行的一部分了。

回到storyboard并且双击Checklist View Controller中的navigation bar来编辑navigation bar的标题(你需要双击大概是navigation bar的中间的位置,否则不会出现编辑内容,可以参考下面图示的位置)。

将标题修改为Checklists。

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第6张图片
改变navigation bar的标题

你现在所改变的,就是你选择Embed In命令后自动添加的Navigation Item的对象。

这个Navigation Item对象包含标题及按钮,当view controller被激活时它们就会展现在navigation bar中。每一个嵌入式的视图都有它自己的Navigation Item用于配置在navigation bar显示什么内容。

当navigation controller滑动一个新的view controller到屏幕上时,它会用这个新的view controller的Navigation Item代替原有navigation bar上的内容。

从对象库中拖出一个Bar Button Item到navigation bar的右边。

确认使用的是Checklist View Controller的navigation bar,而不是navigation controller!

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第7张图片
Checklist View Controller的navigation bar

这个新按钮默认的名称叫做“Item”,但是我们想要的是一个➕号。

选定这个按钮,打开它的属性检查器,将System Item选项更改为Add。

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第8张图片
Bar Button Item的属性设置

如果你看一下System Item中的选项,你会看到这里有许多预置的按钮类型:Add、Compose、Reply、Camera、等等。你可以在你自己的app中按照它们的含义使用它们。

例如,你不能在用于发送邮件的按钮上使用一个摄像机图标,不恰当的使用这些图标会使你的app在审核时遇到极大的麻烦。

OK,我们又有了一个按钮。如果你运行app,你会看到如下界面:

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第9张图片
app中有了一个添加按钮

当然,点击这个什么都不会发生,因为你还没有将它和动作方法连接起来。在接下来的短暂时间内,你会创建一个新的界面,就是“Add Item(添加新项目)”界面,并且通过点击这个➕号按钮跳转到这个界面。但是在这之前,你首先要学习如何在列表中添加新的一行。

我们来把这个➕号按钮连接到一个动作方法上。在这方面,你已经有了十足的经验,我们前一个课程中多次有过这种操作,所以你应该很轻车熟路。

打开ChecklistViewController.swift,添加动作方法:

@IBAction func addItem() {
    }

暂时我们不用添加具体的内容进去,只需要将它和➕按钮连接起来。

打开storyboard,按住ctrl将➕按钮拖拽到Checklist View Controller上,如下图所示:

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第10张图片
拖拽按钮到Checklist View Controller

或者,有个更加简单的办法,就是按住ctrl拖拽➕按钮到离你很近的一个黄色圆圈图标上,见下图:

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第11张图片
另一个方法

实际上你可以拖拽按钮到任何代表这个view controller的地方,而上面的方法,是最好的选择。

放开鼠标后会弹出一个窗口,选择Sent Actions分节下的addItem:

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第12张图片
连接addItem动作方法

让我们来给addItem()添加点内容,回到ChecklistViewController.swift,在这个方法中添加以下代码:

@IBAction func addItem() {
        let newRowIndex = items.count
        
        let item = ChecklistItem()
        item.text = "I am a new row"
        item.checked = false
        items.append(item)
        
        let indexPath = IndexPath(row: newRowIndex,section: 0)
        let indexPaths = [indexPath]
        tableView.insertRows(at: indexPaths, with: .automatic)
    }

在这个方法内部你创建了一个新的Checklistitem对象,并且将它添加到数据模型(就是items数组)中。你同时也告诉table view,“我插入了一个新的行,你要更新一下数据”

我们来讲讲上面代码的作用:

let newRowindex = items.count

你需要计算这个新添加的行在数组中的索引(index)编号。为了使table view能正确的更新新的一行,这个计算是必须的。

当app启动时,数组中有5条数据,并且屏幕上也相应的显示出5行。这5行的index分别是0,1,2,3,4,电脑是从0开始计数的,记得吗?所以新增到数据中的数据它的index必然是5(itmes里有5条数据,所以items.count=5,count就是计算数组中元素的个数)。

换而言之,当你新增一行到table view中时,新的一行的indexpath一定和数组中的index是相等的。

你将新的一行的index放到一个局部常量newRowIndex中。因为它不会被变更,所以用作常量。

接下来的几行,你就应该比较熟悉了:

        let item = ChecklistItem()
        item.text = "I am a new row"
        item.checked = false
        items.append(item)

我们之前在init?(coder)里见过他们。他们创建了一个新的ChecklistItem对象,并且将这个新的对象添加到数组中。

现在数据模型中有6个ChecklistItem对象了,它们都在items数组中。注意一下,此时newRowIndex仍然是5,而items.count已经是6了。这就是为什么你需要在添加新的一行前,先计算items.count的值并且存储到newRowIndex中了。

只把新的ChecklistItem对象添加到数据模型的数组中是不够的。你同时还要告诉table view把这个新的数据添加到新的cell上去,然后在一个新的行里展现它。

let indexPath = IndexPath(row: newRowIndex,section: 0)

和你之前知道的一样,table view使用index-path来标示行号,所以首先你要使用一个IndexPath对象来标示这个新的行,这里用的就是newRowIndex的值。所以新的一行的index-path就是5。

接下来的一行创建了一个临时的数组,用来保存刚才生成的indexPath:

let indexPaths = [indexPath]

你需要使用insertRows(at:with:)这个方法来告诉table view添加新的一行,但是注意这个方法的名称Rows是复数,这意味着其实你可以通过这个方法,一次性添加许多行。

所以它的参数并不是一个单独的IndexPath,而是一个包含index-path的数组。幸运的是创建一个包含index-path的数组是非常简单的,我们只需要使用[]这对方括号就可以了,这对方括号的作用就是创建一个新的数组,而数组的类型就和方括号中的类型一致,这里我们就写作[indexPath],这样就是一个包含indexpath对象的数组了。

最后,你通知table view插入新的这一行,这里的“with: .automatic”参数的作用是:使table view插入新行时,闪现一个漂亮的小动画。

tableView.insertRows(at: indexPaths, with: .automatic)

复习一下整个过程:

1、创建一个新的ChecklistItem对象

2、将这个新创建的对象添加到数据模型中

3、为它在table view插入一个新的cell

其实你也可以添加多行到表格中,可以自己试试。这些新的行当你点击它们时,也可以触发对勾符号的开关。而且不论你怎么上下滚动,对勾符号的状态都会保持一致。

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第13张图片
点击➕按钮后会添加新的行上去

记住,你一定要同时添加数据模型和表格。当你发送insertRows(at: with:)到table view时,你的意思是:“嗨,表格,我的数据模型中有一些新的数据要你添加进去”

这是非常重要的!如果你忘记了告诉table view这里有新的数据,或者你告诉table view这里有新的数据,但是实际上你没有将这些数据添加到数据模型中的话,你的app就会挂掉。数据模型与视图必须保持同步。

练习:给新增加的行默认对勾符号的状态。

删除行

也许我们该给用户删除某些行的权利。

在iOS app中通常的操作方法是“滑动删除”。你用手指将某一行从右往左滑,然后在在最后边就会出现一个删除按钮。点击一下这个按钮就可以完成删除,如果点击了其他地方则取消这次操作。

iOS Apprentice中文版-从0开始学iOS开发-第十三课_第14张图片
通过滑动删除某一行

这种滑动删除是非常容易做的。

在ChecklistViewController.swift中添加下面这个方法,我建议把这个方法放在table view方法附近。

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        items.remove(at: indexPath.row)
        let indexPaths = [indexPath]
        tableView.deleteRows(at: indexPaths, with: .automatic)
    }

当你在你的视图控制器中使用commitEditingStyle方法后,table view会自动激活滑动删除功能。你要做的全部事情就是:

1、从数据模型中移除掉这条数据。

2、删除table view中对应的行。

它和addItem()做的事情正好相反。你再一次使用了一个临时数组保存index-path对象并且告诉table view删除掉这一行。

运行app,试试效果。

⚠️:释放对象
当你操作item.remove(at:)时,并不仅仅是从Checklistitem中取出了这个值,而是永久的将它破坏掉了。
我们会在下一个课程中详细的讨论这件事,在这里我们只需要记住如果一个对象没有任何引用,那么这个对象会被自动销毁掉。当ChecklistItem对象进入数组时,这个数组就引用了它。
但是当你把ChecklistItem对象从数组里取出来时,这个引用就不存在了,并且这个对象也就被销毁了。或者,以计算机的语言来讲,它被释放了。
一个对象被销毁了意味着什么?每一个对象都占用计算机中的一点内存。当你创建一个对象的实例的时候,一块内存就会被预占,用于保存这个对象的数据。
如果这个对象被释放了,那么这块内存也就被释放了,可以用来存储其他东西。被释放掉的对象不再存在,并且再也无法被使用。
在早起的iOS版本中,你必须手动释放这些内存。幸运的是,现在时代变得很快,Swift使用一种叫做自动引用计数(Automatic Reference Counting,简写为ARC)的机制来管理对象的生命周期,对你而言无须在记住哪里需要释放它们。对我而言,不用去操心释放内存,简直太棒了!

你可能感兴趣的:(iOS Apprentice中文版-从0开始学iOS开发-第十三课)