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

在清单中添加新的项目

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

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

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

点击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的。它是一种使你可以一个界面转到另一个界面的东西:

导航控制的工作方式

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

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

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

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

将view controller放入navigation controller中

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

现在navgation controller连接着你的view controller

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

运行app试试看。

现在app的顶部有了一个navigation bar

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

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

将标题修改为Checklists。

改变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!

Checklist View Controller的navigation bar

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

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

Bar Button Item的属性设置

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

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

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

app中有了一个添加按钮

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

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

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

@IBAction func addItem() {
    }

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

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

拖拽按钮到Checklist View Controller

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

另一个方法

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

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

连接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

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

点击➕按钮后会添加新的行上去

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

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

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

删除行

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

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

通过滑动删除某一行

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

在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)的机制来管理对象的生命周期,对你而言无须在记住哪里需要释放它们。对我而言,不用去操心释放内存,简直太棒了!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容