Repository: CS193P-Translation-Group/Developing-iOS-9-Apps-with-Swift
Branch: master
Commit: 4e288f0bdac0
Files: 20
Total size: 2.3 MB
Directory structure:
gitextract_9dzbwgkv/
├── README.md
├── download.md
└── subtitles/
├── 1. Course Overview and Introduction to iOS, Xcode, and Swift.srt
├── 10. Core Data.srt
├── 11. Core Data Demo.srt
├── 12. AutoLayout.srt
├── 13. NSTimer and Animation.srt
├── 14. Animation and Core Motion.srt
├── 15. Application Lifecycle, Alerts, CloudKit.srt
├── 16. Notifications and CloudKit.srt
├── 17. Segues, Core Location, and MapKit.srt
├── 18. Persistence.srt
├── 2. Applying MVC.srt
├── 3. More Swift and Foundation Framework.srt
├── 4. Views.srt
├── 5. Interface Builder, FaceView Controller, Gestures, and Multiple MVCs.srt
├── 6. Multiple MVCs, Segues, FaceIt, and View Controller Lifecycle.srt
├── 7. Closures, Extensions, Protocols, Delegation, and ScrollView.srt
├── 8. Multithreading and Text Field.srt
└── 9. Table View.srt
================================================
FILE CONTENTS
================================================
================================================
FILE: README.md
================================================
Developing iOS 9 Apps with Swift 字幕简体中文翻译项目
---
iTunes U 课程地址:[iTunes U/Developing iOS 9 Apps Swift](https://itunes.apple.com/cn/course/developing-ios-9-apps-swift/id1104579961)
CS193P 课程地址:[CS193P iPhone Application Development](http://web.stanford.edu/class/cs193p/cgi-bin/drupal/)
### 相关链接
[Developing iOS 8 Apps with Swift 字幕翻译](https://github.com/X140Yu/Developing_iOS_8_Apps_With_Swift)
### 校对规则
请参见文档:[校对规则](https://github.com/X140Yu/Developing_iOS_8_Apps_With_Swift/blob/master/proofread-rules.md)。
### 字幕下载
直接点击[项目 Zip 包](https://github.com/X140Yu/Developing-iOS-9-Apps-with-Swift/archive/master.zip)进行全部已有字幕的下载。
### 视频及课件下载
[Download](./download.md)
### 联系我们
如果您想参与项目,请直接在 [Issues](https://github.com/X140Yu/Developing-iOS-9-Apps-with-Swift/issues) 区发言。
如果仅仅是想了解翻译的进度,关注项目的更新或者到 [Issues](https://github.com/X140Yu/Developing-iOS-9-Apps-with-Swift/issues) 区发言即可。
### 版权说明
The original work by Stanford University is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 United States License.
================================================
FILE: download.md
================================================
# Video
[lecture 1](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic1/v4/64/b0/84/64b084ce-11d0-a3ff-e901-bcb0b38340b9/318-3290009375018043679-01_iTunes_CS193P_03_28_16_1080_720p_1500cc.m4v)
[lecture 2](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/45/74/46/45744676-931d-e1d4-1787-952713e66c45/320-8373286204145257290-02_CS193P_3_30_16_720p1500cc.mp4)
[lecture 3](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic20/v4/a8/92/8e/a8928e55-ff74-cd37-6c9d-a91d1dda2b71/309-7506772204647137370-03_CS193P_4_04_16_1080p_3mb_cc2.m4v)
[lecture 4](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/70/86/bf/7086bfd5-f133-6686-a319-7ba7947bae16/305-7247414449295650666-04_CS193P_4_06_16_1080p_3mb_cc.m4v)
[lecture 5](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic20/v4/36/91/d6/3691d659-ec3b-d34f-b8ec-a92e1183095f/328-1962441713930624806-05_CS193P_4_11_16_1080p_3mb_cc.m4v)
[lecture 6](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic30/v4/ab/b2/70/abb270a8-6473-7658-72b6-f614a3bd5489/333-2664853460560613531-06_CS193P_4_13_16_rev_1080p_3mb.m4v)
[lecture 7](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic20/v4/81/b0/1f/81b01f26-ccd9-9df7-df6f-c5d34aea47aa/302-7208808734861639493-07_CS193__4_18_16_1080p_3mb_cc.m4v)
[lecture 8](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/99/1e/e0/991ee064-fcd4-267f-4595-361d14bc09d0/336-4841244943511706201-08_CS193_4_20_16_1080p_3mb_cc.m4v)
[lecture 9](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic30/v4/b3/4f/d0/b34fd003-ee43-43dc-4b21-6c355688fb3c/311-4524248345011539875-09_CS193_4_25_16_1_1080p_3mb.m4v)
[lecture 10](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic20/v4/df/93/44/df9344e9-03cf-fcfe-e550-3548d53c190d/326-7677824725330965182-10_CS193_4_27_16_1_1080p_3mb.m4v)
[lecture 11](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/d8/08/29/d80829e3-fced-1630-5942-5306cc663461/322-952121251318135044-11_CS193_5_02_16_1080p_3mb.m4v)
[lecture 12](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/64/6c/40/646c4016-9bd4-d378-a880-2ac977f38093/330-2525015638168359918-12_CS193_5_04_16_rev2_1080p_3mb.m4v)
[lecture 13](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/14/a3/b6/14a3b675-ba6c-7594-fcb7-fc0b4e82a0b7/310-1407346536923462131-13_CS193_5_09_16_1080p_3mb.m4v)
[lecture 14](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/f1/ca/44/f1ca444b-4578-8114-7297-555f4cdc9b57/326-7999025039413500594-14_CS193_5_11_16_1080p_3mb.m4v)
[lecture 15](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/80/eb/65/80eb6594-3604-9eac-1a93-187de9e32c1e/337-4900765140487554688-15_CS193_5_16_16_1080p_3mb.m4v)
[lecture 16](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/2b/9a/58/2b9a58e0-7761-b64f-45a2-2582d7f93644/324-5681395727728634007-16_CS193_5_18_16_1080p_3mb.m4v)
[lecture 17](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/bb/39/61/bb3961d5-73a0-42ec-1e71-0336508e9dfa/311-3400152801833314139-17_CS193_5_23_16_1080p_3mb.m4v)
[lecture 18](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/c8/db/e9/c8dbe99d-6f8e-28f5-47f4-ffe0c873d021/335-740161242915993166-18_CS193_5_25_16_1080p_3mb.m4v)
# Slides
[lecture 1 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic1/v4/f4/05/ff/f405ffb5-50b0-95c6-fd17-a6c262311a2d/321-5596613686960899183-CS193P_S16_Lecture_1_Slides.pdf)
[lecture 2 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic20/v4/ef/31/cc/ef31cc34-7cd0-e3c4-425c-e7f75a262f00/321-3432256539360740647-CS193P_S16_Lecture_2_Slides.pdf)
[lecture 3 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/e6/a1/7e/e6a17ee2-b99f-3221-addd-da39d12d8802/321-6470380314597358395-CS193P_S16_Lecture_3_Slides.pdf)
[lecture 4 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/fb/b9/58/fbb95870-c6be-79e2-77c5-c6474b8a40bf/307-5050413922657858617-CS193P_S16_Lecture_4_Slides.pdf)
[lecture 5 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/7d/de/00/7dde003d-b32b-93fc-9786-0ec16c29ffb5/334-741241953225611839-CS193P_S16_Lecture_5_Slides.pdf)
[lecture 6 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/ae/3e/6e/ae3e6ece-fead-e585-2035-756ccd82bfd0/308-1536759857490550427-CS193P_S16_Lecture_6_Slides.pdf)
[lecture 7 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic20/v4/47/02/9e/47029e12-1843-f40c-095c-ce3781137ed0/308-1708618185040143775-CS193P_S16_Lecture_7_Slides.pdf)
[lecture 8 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/57/48/6c/57486c13-678e-7892-9cb4-9dbd58ab6fae/328-2295701575035385283-CS193P_S16_Lecture_8_Slides.pdf)
[lecture 9 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/31/fb/33/31fb33c5-65ff-8e07-9c8a-2c49dd75f734/319-404525925375936666-CS193P_S16_Lecture_9_Slides.pdf)
[lecture 10 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic20/v4/7c/36/24/7c36249c-605a-8fc7-5c6c-a49abe0c8d37/317-3926700305190050506-CS193P_S16_Lecture_10_Slides.pdf)
[lecture 11 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/52/74/73/52747331-a857-1914-9ebb-dc4a6dde68e7/319-3727135049627801646-CS193P_S16_Lecture_11_Slides.pdf)
[lecture 12 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/f2/9c/61/f29c6128-8f92-5c81-b1ce-6d0d62d29f0c/318-7132989426423920990-CS193P_S16_Lecture_12_Slides.pdf)
[lecture 13 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/c5/37/96/c537967b-6e29-f272-b57a-88e0ce60d531/306-350276286767890645-CS193P_S16_Lecture_13_Slides.pdf)
[lecture 14 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic20/v4/9b/5e/59/9b5e5936-5126-85d4-6b18-4a13d9e7afc5/335-2050748076731576057-CS193P_S16_Lecture_14_Slides.pdf)
[lecture 15 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/55/28/db/5528dbac-7421-90ea-0d18-61d8b085e7e1/304-2868878214971040553-CS193P_S16_Lecture_15_Slides.pdf)
[lecture 16 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic20/v4/42/13/f9/4213f984-9a28-80ac-8db8-b242dd377c4c/335-760378487450689313-CS193P_S16_Lecture_16_Slides.pdf)
[lecture 17 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic20/v4/d8/aa/24/d8aa2482-9c87-d22a-ecec-c5bff601bc0e/322-3967892605293848768-CS193P_S16_Lecture_17_Slides.pdf)
[lecture 18 slides](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/d9/38/2c/d9382c15-0379-0000-3cbe-233ac1f92023/328-8624592354314154483-CS193P_S16_Lecture_18_Slides.pdf)
[project 1](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic30/v4/8b/a7/8c/8ba78c73-a911-b9d9-7406-7b6c35b1a635/330-4665516795304515844-CS193P_S16_Programming_Project_1.pdf)
[project 2](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/62/62/f2/6262f2b4-721c-d325-3c23-ced31802068d/303-6526141645397285536-Programming_2.pdf)
[project 3](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/90/64/be/9064beaa-cc04-ad7f-30e4-a9330e21caba/320-5665175142130618734-CS193P_S16_Programming_Project_3.pdf)
[project 4](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic30/v4/0b/d2/ad/0bd2ad88-4abf-29a5-b406-cefeb3637c55/315-4965520355348488665-CS193P_S16_Programming_Project_4.pdf)
[project 5](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic18/v4/e8/ba/cc/e8bacc96-9c52-8652-5613-950db7b65e5e/328-5947046615294240890-CS193P_S16_Programming_Project_5.pdf)
[project 6](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic20/v4/46/05/9a/46059aff-bffb-1dcf-00ef-0d22bad80b47/322-6708752098278348100-CS193P_S16_Programming_Project_6.pdf)
[reading 1](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic60/v4/df/8f/72/df8f7285-414d-f3f3-318a-6bb7246252b8/329-6584533342020921682-Reading_1.pdf)
[reading 2](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic30/v4/32/fc/8b/32fc8b6a-bc29-8977-9035-1fc2b1b427d3/323-869352964012275742-Reading_2.pdf)
[reading 3](https://itunesu-assets.itunes.apple.com/apple-assets-us-std-000001/CobaltPublic30/v4/4a/d9/8f/4ad98f9f-18ba-7d7e-8a3e-99e8bebd2e82/313-6521184191452265856-Reading_3_2.pdf)
================================================
FILE: subtitles/1. Course Overview and Introduction to iOS, Xcode, and Swift.srt
================================================
1
00:00:00,001 --> 00:00:08,207
[SOUND] Stanford University. >> All right well welcome
斯坦福大学
2
00:00:08,209 --> 00:00:13,212
to Stanford CS193P. This is Spring of 2016 version and
欢迎来到 2016 年春季的斯坦福 CS193P 课程
3
00:00:13,214 --> 00:00:16,982
today I'm gonna give you a real quick explanation of what
今天我会快速阐述一下这门课是关于什么的
4
00:00:16,984 --> 00:00:20,986
this class is to make sure you're in the right place. I'm
以免你们来错了地方
5
00:00:20,988 --> 00:00:23,722
gonna talk about prerequisites cuz it's super important to be
也会介绍一下参加这门课程的前提
6
00:00:23,724 --> 00:00:27,159
successful in this class to have the prerequisites.
要想上好这门课,有些准备还是很重要的
7
00:00:27,161 --> 00:00:30,129
Then I'm just gonna give you the briefest overview of what
接着我会介绍一下 iOS 是什么
8
00:00:30,131 --> 00:00:34,466
iOS is. Most of you know because you have iPhones or
你们应该都知道
9
00:00:34,468 --> 00:00:35,300
iPads or something like that, so
因为你们用过 iPhone 或者 iPad
10
00:00:35,302 --> 00:00:38,037
I'm not gonna waste a lot of time talking about that. And
我不会在介绍 iOS 上浪费很多时间
11
00:00:38,039 --> 00:00:41,206
then finally I'm gonna dive right into a big demo that's
最后我会带你们做一个大一点的 demo
12
00:00:41,208 --> 00:00:43,475
gonna build our first application. Cuz I really
直接开始打造我们的第一个应用程序
13
00:00:43,477 --> 00:00:45,411
don't believe in wasting a lot of time with overhead.
因为我不太想花太多时间讲一些理论的知识
14
00:00:45,413 --> 00:00:47,513
We're just gonna dive right in and start learning how to do
我们就直接开始上手做一些东西
15
00:00:47,515 --> 00:00:51,984
this stuff, okay? We only have ten weeks to learn iOS. That's
因为我们的课程只有 10 周
16
00:00:51,986 --> 00:00:54,753
a short amount of time for it. It's a very powerful system.
对于一个这么强大的系统来说,这点学习时间太短了
17
00:00:54,755 --> 00:00:59,158
So we're gonna have to really go at break neck speed. So
所以需要以飞快的速度进行学习
18
00:00:59,160 --> 00:01:00,292
what will you learn in this class?
在这门课程中你将学到什么?
19
00:01:00,294 --> 00:01:02,561
You're gonna learn to build cool apps, all right.
你将会学到如何打造很酷的 App
20
00:01:02,563 --> 00:01:05,064
iOS apps are cool. Why are they cool?
iOS App 很酷,为什么呢?
21
00:01:05,066 --> 00:01:07,466
Well, they're cool because they sit in your pocket, and
因为它们就在你的口袋里
22
00:01:07,468 --> 00:01:09,468
you can bring it out and show your friends.
你可以直接拿出来给你的朋友们看
23
00:01:09,470 --> 00:01:12,171
They're cool because they're really easy to distribute.
还有就是,App 很容易被发布
24
00:01:12,173 --> 00:01:12,304
If you build an app,
如果你开发了一款应用
25
00:01:12,306 --> 00:01:15,040
you can put it on the app store with no work. Boom,
你可以很轻松地把它放到 App Store 上
26
00:01:15,042 --> 00:01:19,078
ship it out to your customers. And also it's a very vibrant,
通过 App Store 把它移交给你的顾客
27
00:01:19,080 --> 00:01:22,448
community. All right? A lot of people doing iOS development,
iOS 开发的社区也很活跃
28
00:01:22,450 --> 00:01:24,416
and they're very excited about it.
很多人在做 iOS 开发,并且他们也很热爱
29
00:01:24,418 --> 00:01:28,320
And, also the apps that you build have animation, a lot of
App 也会包含一些很酷的动画
30
00:01:28,322 --> 00:01:31,323
really cool interactions with users, so they're fun apps to
和用户之间很炫的交互
31
00:01:31,325 --> 00:01:33,725
build. Okay, so you probably know all that,
所以制作 App 是很有趣的
32
00:01:33,727 --> 00:01:36,161
that's why you're here. Another thing you're gonna
你们都知道这一点,这也是你们为什么来到这里的原因
33
00:01:36,163 --> 00:01:38,197
learn though and you're gonna get out of this class
你们还会从这门课中学到
34
00:01:38,199 --> 00:01:40,899
is real-life object-oriented programming.
面向对象编程的知识
35
00:01:40,901 --> 00:01:43,569
iOS is entirely object-oriented in its design.
iOS 在设计上是完全面向对象的
36
00:01:43,571 --> 00:01:46,505
All the writing of code you have to do is object-oriented
你们写的每一行代码都会是面向对象的
37
00:01:46,507 --> 00:01:49,141
programming. And you're really gonna get the experience of
你们将会从现实的面向对象编程中
38
00:01:49,143 --> 00:01:52,444
building a real-world thing using object-oriented
获得很多经验
39
00:01:52,446 --> 00:01:55,481
programming which is a great programming paradigm. And
面向对象编程也是一个很好的编程范式
40
00:01:55,483 --> 00:01:57,282
some of the things your going to learn for example using
你们将会使用
41
00:01:57,284 --> 00:02:00,085
object-oriented programming. Object-oriented databases,
面向对象的数据库
42
00:02:00,087 --> 00:02:02,521
were going to do graphics with object oriented programming.
使用面向对象来编写界面
43
00:02:02,523 --> 00:02:04,790
Obviously going to be some multimedia stuff,
而且还会有一些关于多媒体的东西,
44
00:02:04,792 --> 00:02:06,959
multi threading, animation, networking.
多线程、动画、网络等
45
00:02:06,961 --> 00:02:08,927
All kinds of stuff that you're learning in your other
你们从其它 CS 课程中学到的知识
46
00:02:08,929 --> 00:02:11,163
computer science classes, you're going to do for
都会在这门课程中
47
00:02:11,165 --> 00:02:13,065
real, real life here in this class. So
得到真实的应用
48
00:02:13,067 --> 00:02:15,734
that's really good, it's kind of a good synthesis class or
这很好,这就像一门综合的课程
49
00:02:15,736 --> 00:02:18,570
practical synthesis class for all that stuff.
来运用你学所学到的知识
50
00:02:18,572 --> 00:02:22,908
The prerequisites are really important in this class.
这门课的前提非常重要
51
00:02:22,910 --> 00:02:25,110
The main one is object-oriented programming.
最主要的一个是面向对象编程
52
00:02:25,112 --> 00:02:27,746
I really don't have time in ten weeks to take the time to
我不会在这 10 周中花时间教你们
53
00:02:27,748 --> 00:02:30,015
teach you object-oriented programming first, so
如何使用面向对象编程
54
00:02:30,017 --> 00:02:32,851
you really need to know that coming in. So in terms of
你们需要知道在这门课中会用到
55
00:02:32,853 --> 00:02:37,890
Stanford's prerequisites that means CS106A&B or CS106X.
所以在学这门课之前需要先学习 CS106A&B 或 CS106X
56
00:02:37,892 --> 00:02:40,259
That's absolutely required okay, so you get some
这是必须要有的前提
57
00:02:40,261 --> 00:02:44,062
Java experience in there and a little bit of object-oriented.
所以我假定你们现在有了一些 Java 的编程经验和一些面向对象的基础
58
00:02:44,064 --> 00:02:47,399
And then really important to take some,
在上这门课这之前
59
00:02:47,401 --> 00:02:50,202
just some class where you're gonna do a lot of programming.
上过一些写了很多代码的课程也是很重要的
60
00:02:50,204 --> 00:02:53,305
Okay like CS107 which is not hugely object-oriented but
例如 CS107,虽然没有大量关于面向对象的东西
61
00:02:53,307 --> 00:02:54,273
there's a lot of programming.
但是上那门课会写很多代码
62
00:02:54,275 --> 00:02:58,043
CS108 which is an awesome prerequisite for this class.
如果你在参加这门课之前上过 CS108 是非常好的
63
00:02:58,045 --> 00:02:59,912
Okay, that's basically object-oriented Programming.
它讲是关于面向对象编程的东西
64
00:02:59,914 --> 00:03:02,548
So if you've taken CS108, you're gonna be well prepared
所以如果你上过 CS108,你就已经准备好上这门课了
65
00:03:02,550 --> 00:03:05,551
for this class. CS110, again, not really object oriented
CS110,同样也没有太多关于面向对象的知识
66
00:03:05,553 --> 00:03:07,553
per-se, but there's a lot of coding in there.
但同样是一门非常好的编程课程,因为其中有大量的编程实践
67
00:03:07,555 --> 00:03:10,189
You know, preferably you've taken some other CS classes,
你最好还上过其它的一些 CS 课程
68
00:03:10,191 --> 00:03:12,925
you've written a lot of code because, let me warn you,
写过很多代码
69
00:03:12,927 --> 00:03:16,728
this class is a lot of coding. All your assignments are all
因为上这门课需要写很多代码
70
00:03:16,730 --> 00:03:19,731
coding. You're gonna write a ton of code in this class.
所有的作业都是写代码,你会在这门课中写大量的代码
71
00:03:19,733 --> 00:03:22,267
So if you're not comfortable writing a ton of code,
所以如果你真的不太适应写比较多的代码
72
00:03:22,269 --> 00:03:24,703
then this class is going to be a lot of work for you.
那么这门课程对你来说将是一个挑战
73
00:03:24,705 --> 00:03:27,639
You're gonna find it, maybe you'll be a little underwater.
你会慢慢发现的
74
00:03:27,641 --> 00:03:31,677
Okay so be prepared for a lot of coding.
所以做好要写很多代码的准备
75
00:03:32,379 --> 00:03:33,779
When it comes to object-oriented programming,
当谈到面向对象编程
76
00:03:33,781 --> 00:03:36,515
you should know all these terms like the back of your
你就应该像了解你的手背一样了解这些术语
77
00:03:36,517 --> 00:03:37,616
hand. You shouldn't be looking at those and
你不应该连一些类似
78
00:03:37,618 --> 00:03:40,652
going what, what's that, super class, I don't get it.
「父类」的术语都不懂
79
00:03:40,654 --> 00:03:42,354
Okay, you should not even be here, okay.
如果是那样的话,你不应该来参加这门课程
80
00:03:42,356 --> 00:03:45,691
Go take these other classes and then come back.
先去参加别的课程,然后再回来
81
00:03:46,260 --> 00:03:50,062
This is an upper-level CS course, this is not 106 or
这是一门更高等级的 CS 课程
82
00:03:50,064 --> 00:03:52,965
107. Okay? It's a next level up. So if you haven't
它不是 CS106 或 CS107,它是更高一级的东西
83
00:03:52,967 --> 00:03:56,802
written a fairly sophisticated app before, okay, then you're
如果你还没有写过一些比较复杂的程序
84
00:03:56,804 --> 00:04:00,472
gonna find out whoa, this is a lot of work, okay. So
那你就会发现,你需要学习的东西有很多
85
00:04:00,474 --> 00:04:02,674
that's the prerequisites, make sure you're prepared, for
这就是参加这门课程的条件,确定你已经准备好了
86
00:04:02,676 --> 00:04:05,210
this class. And if you're not, just go take those classes and
如果没有,就先去参加其它课程,然后再回来
87
00:04:05,212 --> 00:04:08,180
come back. All right. So, what's in iOS?
什么是 iOS?
88
00:04:08,182 --> 00:04:10,749
You all know what's in iOS from a user standpoint.
从一个用户的角度,你们应该都知道 iOS 是什么
89
00:04:10,751 --> 00:04:12,951
I'm sure, you've used, all used iOS. So
你确定你们全都用过 iOS
90
00:04:12,953 --> 00:04:16,622
what's kinda under the covers? What are the programming APIs?
但更深层的东西是什么?编程的 API 都是什么?
91
00:04:16,624 --> 00:04:19,791
Everyone know what the words API means? That basically
大家都知道 API 是什么意思
92
00:04:19,793 --> 00:04:23,161
programming interface, it's the functions, and methods,
它其实就是编程接口的意思,它是使 iOS App
93
00:04:23,163 --> 00:04:26,698
and classes that we use to make an iOS app work. So
工作的函数、方法和类
94
00:04:26,700 --> 00:04:30,936
what are those APIs that we use? Well they're huge. Okay,
我们能调用的 API 都有什么?它们有很多
95
00:04:30,938 --> 00:04:33,772
there's so much of it I can barely even summarize it here,
API 实在太多了,我都没办法概括出来
96
00:04:33,774 --> 00:04:36,708
but I'm gonna summarize it by dividing it into four layers
但是我可以把它分成四个层次来总结
97
00:04:36,710 --> 00:04:40,445
okay? The core OS which is kind of near the hardware
Core OS 层很接近硬件
98
00:04:40,447 --> 00:04:44,216
all the way up to Cocoa Touch which is near the user, okay?
Cocoa Touch 层很接近用户
99
00:04:44,218 --> 00:04:46,318
And then there's some layers in between. So
在这两层之间还有一些层
100
00:04:46,320 --> 00:04:46,918
let's talk about these layers,
所以我们先分层次来讨论
101
00:04:46,920 --> 00:04:50,055
let's start down at the bottom there. The hardware.
先从底部开始看,硬件层
102
00:04:50,057 --> 00:04:54,159
So, people don't really realize this, but an iPhone
大部分人都没有发现其实 iPhone 运行着 UNIX 系统
103
00:04:54,161 --> 00:04:58,330
is running Unix, okay? There's basically a version of Unix
UNIX 的最基本版本
104
00:04:58,332 --> 00:05:02,567
running on it, so it has full multitasking, Unix filesystem,
所以它有多线程、UNIX 文件系统
105
00:05:02,569 --> 00:05:04,303
everything in there. And, you know,
所有 UNIX 有的东西
106
00:05:04,305 --> 00:05:08,206
you get the power of that when you build iOS apps, okay? And
在做 iOS 开发的时候你可以运用这些强大的能力
107
00:05:08,208 --> 00:05:09,675
that includes all these things here. Now,
iPhone 包含所有这些
108
00:05:09,677 --> 00:05:12,911
there's some things that this Unix layer, that are specific
还有在 UNIX 这一层特意为了移动设备而打造的东西
109
00:05:12,913 --> 00:05:15,447
to a mobile device like Power Management is really
例如很重要的电量管理
110
00:05:15,449 --> 00:05:19,351
important, Keychain Access to make sure that the people
Keychain,来管理
111
00:05:19,353 --> 00:05:21,753
are not accessing things on the phone that shouldn't,
用户在手机上不应该接触的东西
112
00:05:21,755 --> 00:05:25,524
etc. But this is basically Unix in all its glory,
但这最底层,基本上都是 UNIX 带来的光芒
113
00:05:25,526 --> 00:05:30,095
down at this low level. Next, there's a core services layer.
在它上一层,是 Core Services 层
114
00:05:30,097 --> 00:05:33,265
Because we wanna program in object-oriented programming as
因为我们在编写程序的时候尽量用到面向对象
115
00:05:33,267 --> 00:05:36,702
much as possible, there's a layer which is written in
这一层是在 UNIX 之上使用面向对象语言编写的
116
00:05:36,704 --> 00:05:40,906
object-oriented language, on top of Unix so that you can
你可以在这一层得到
117
00:05:40,908 --> 00:05:44,743
get at things like networking and multithreading and
网络、多线程
118
00:05:44,745 --> 00:05:48,513
network, or preferences and SQL for databases.
或者 SQL 数据库之类的东西
119
00:05:48,515 --> 00:05:51,083
You can get at these things using object oriented APIs.
你可以通过面向对象的 API 来调用这些
120
00:05:51,085 --> 00:05:54,319
So this is kind of an object oriented layer on top of
所以这一层是在 Core Services 之上的面向对象的一层
121
00:05:54,321 --> 00:05:57,589
the core services. Okay but there is no UI in this layer.
在这一层是没有 UI(用户界面) 的
122
00:05:57,591 --> 00:06:01,126
Okay this is kind of base layer. Now the next layer,
有点像比较基础的层
123
00:06:01,128 --> 00:06:04,596
super important. Ironically I don't have a lot of time to
下一个非常重要的层
124
00:06:04,598 --> 00:06:07,899
talk about this in this class. I'll kind of let you know it's
我没有太多时间在课堂上介绍它
125
00:06:07,901 --> 00:06:10,369
there but it'll be something you'll have to dive into
你对这一层的了解取决你想要做些什么东西
126
00:06:10,371 --> 00:06:13,605
depending on what you want to do. But that's multimedia.
这一层就是多媒体层
127
00:06:13,607 --> 00:06:17,909
So this is video, all kinds of different audio frameworks.
在这一层,有视频、音频库
128
00:06:17,911 --> 00:06:21,680
And then of course still pictures like JPEGs,
处理例如 JPEG 图片的
129
00:06:21,682 --> 00:06:22,414
things like that and
图片库
130
00:06:22,416 --> 00:06:26,551
drawing okay the Quartz API for drawing in 2D. OpenGL for
用于 2D 绘图的 Quartz API
131
00:06:26,553 --> 00:06:29,388
drawing in 3D. So there's a lot of stuff here okay we
用于 3D 绘图的 OpenGL
132
00:06:29,390 --> 00:06:32,257
obviously don't have time in ten weeks to teach you OpenGL
显然在 10 周的课程之内我不会教你们 OpenGL
133
00:06:32,259 --> 00:06:35,427
you're gonna have to learn that in a graphics class but
你们需要在图形课程里学习
134
00:06:35,429 --> 00:06:36,962
you'll be able to use it here, okay.
并在那里运用
135
00:06:36,964 --> 00:06:39,664
And then at the top layer, this is where were going to
最顶层,就是我们要花几乎所有的时间在的层
136
00:06:39,666 --> 00:06:41,800
spend most of our time, is Cocoa touch, okay.
就是 Cocoa Touch 层
137
00:06:41,802 --> 00:06:46,638
It is object-oriented API for building user interfaces okay?
它提供了面向对象的 API 来搭建 UI
138
00:06:46,640 --> 00:06:49,341
And these user interfaces are not just buttons and
这些 UI 元素不仅仅包含
139
00:06:49,343 --> 00:06:52,043
sliders on the screen. But things like,
屏幕上的按扭的滑块
140
00:06:52,045 --> 00:06:54,012
what to do when you shake your phone? Or
还有当你摇动手机的时候应该怎么处理
141
00:06:54,014 --> 00:06:58,250
using the orientation of the phone as input for example,
或者把设备的方向当作输入
142
00:06:58,252 --> 00:07:01,086
okay. It also includes animation because in iOS,
它还包含动画
143
00:07:01,088 --> 00:07:04,890
in a lot of the apps there's a lot of animation going on.
因为在 iOS 中,大量的 App 使用动画
144
00:07:04,892 --> 00:07:06,992
It's just part of the way it's being built and
它被搭建的时候这就是其中的一部分了
145
00:07:06,994 --> 00:07:09,461
a lot of the stuff at this level is really high
这一层的许多东西都是非常高层次的
146
00:07:09,463 --> 00:07:12,264
level. Like there are objects that basically let you put
例如,你可以对一个对象进行操作
147
00:07:12,266 --> 00:07:16,701
an entire Safari browser in a rectangle inside your app.
把整个 Safari 浏览器放进你 App 的一块区域中
148
00:07:16,703 --> 00:07:18,336
Okay, this one object you just drag it in,
你可以拖进来一个对象
149
00:07:18,338 --> 00:07:20,071
boom you've got it. All you have to do,
然后你就有对应的功能了
150
00:07:20,073 --> 00:07:23,108
maybe wire up the back button and you're ready to go.
你也许只需要做一个连线就可以了
151
00:07:23,110 --> 00:07:26,278
Same thing with maps. You've seen the whole map 3D maps and
关于地图,你可能见过一些 2D,3D 地图
152
00:07:26,280 --> 00:07:28,814
all that stuff. Again one thing you drag it in,
同样的,你把它拖进来
153
00:07:28,816 --> 00:07:30,816
now you've got full map functionality
你就有了全部地图相关的功能
154
00:07:30,818 --> 00:07:34,986
in you application. So this is super powerful objects. Again
它是非常强大的对象
155
00:07:34,988 --> 00:07:37,355
in 10 weeks and I can only show you the basics of this.
在 10 周的课程内,我只能展示给你这些对象的基本用途
156
00:07:37,357 --> 00:07:39,858
Hopefully the things I've chosen to teach you
好在我选择教给你们的东西
157
00:07:39,860 --> 00:07:43,261
will have paradigms for using them. That you can apply to
都有一些使用的范式
158
00:07:43,263 --> 00:07:45,730
the things that I don't have time to teach you, okay.
你可以把它运用到我没有教给你的东西上面
159
00:07:45,732 --> 00:07:48,033
So I'm going to try and teach you the basics
所以我会尽量教给你们让
160
00:07:48,035 --> 00:07:51,436
of how to make it all work in a way that lets you go out and
这些东西基本运行起来的方式
161
00:07:51,438 --> 00:07:54,973
learn new things quite straightforwardly. Okay, so
然后你们就可以通过这种方式来直接学习新的东西了
162
00:07:54,975 --> 00:08:00,145
that's basically what's an iOS in three minutes or less.
所以这就是关于什么是 iOS 的小演讲
163
00:08:00,147 --> 00:08:02,848
The components of the platform that we're gonna be developing
以下这些东西是在这个平台开发所需要的
164
00:08:02,850 --> 00:08:07,185
on are as follows. First, there's some tools. Really,
首先是工具
165
00:08:07,187 --> 00:08:11,323
Xcode 7 is 99% of what you're gonna be using in this class.
99% 的时间你们都要使用 Xcode 7
166
00:08:11,325 --> 00:08:13,959
Okay? It's your editor, your debugger,
它是编辑器,debug 的工具
167
00:08:13,961 --> 00:08:15,894
source code management, everything is in Xcode7,
源码管理,Xcode 7 都有
168
00:08:15,896 --> 00:08:20,131
it's kind of a one stop shop. There is an extra thing there
它类似于一个一站式的工具
169
00:08:20,133 --> 00:08:22,133
instruments which kind of plugs in with Xcode7 for
Xcode 7 中还有类似于插件一样的 Instruments 工具
170
00:08:22,135 --> 00:08:24,669
doing things like performance, measurement things like that
做一些性能检测之类的事情
171
00:08:24,671 --> 00:08:27,372
but basically we're going to be in Xcode7. Okay, that's
但是基本我们会在 Xcode 7 中干活
172
00:08:27,374 --> 00:08:32,344
where we're gonna be doing all our work. Language, languages,
编程语言
173
00:08:32,346 --> 00:08:34,279
there's actually two languages you can develop for
做 iOS 开发你可以使用两种语言
174
00:08:34,281 --> 00:08:37,215
in iOS. We're gonna teach you the more modern one which is
我们会教你更现代的那个,Swift
175
00:08:37,217 --> 00:08:39,618
called Swift, okay? But there's another one called
另一个叫作 Objective-C
176
00:08:39,620 --> 00:08:42,187
Objective C, I'm not really gonna talk about Objective C
我不会太介绍 Objective-C
177
00:08:42,189 --> 00:08:47,425
in this class, at least not primarily in the lecture. So
至少在 lecture 中不是作为主要讲解的语言
178
00:08:47,427 --> 00:08:48,960
you will have to learn a new language,
所以你得学一门新的语言
179
00:08:48,962 --> 00:08:50,729
I'm sure none of you know Swift, or
我知道你们都不了解 Swift
180
00:08:50,731 --> 00:08:52,964
if you do, it's because you already know iOS.
如果你了解,是因为你对 iOS 也已经有了了解
181
00:08:52,966 --> 00:08:55,500
So Swift will be a new language you'll have to learn,
Swift 会作为你们必须要学习的新语言
182
00:08:55,502 --> 00:08:57,802
I'll spend the first two weeks really trying to
前两周我会尽力用火箭一般的速度
183
00:08:57,804 --> 00:09:01,406
give you rocket speed learning of this new language.
给你们讲解这门新的语言
184
00:09:01,408 --> 00:09:03,275
Then there's a lot of frameworks,
然后还有大量的框架
185
00:09:03,277 --> 00:09:06,811
like foundation and UI kit which is the kinda that base
例如 Foundation 和 UIKit
186
00:09:06,813 --> 00:09:09,247
core services stuff and the UI stuff, but
它们分别是核心服务和 UI 之类的东西
187
00:09:09,249 --> 00:09:12,551
there's also core data and core motion and map kit for
还有 Core Data、 Core Motion 和 MapKit
188
00:09:12,553 --> 00:09:16,087
all these things like the gyro and the accelerometer and
它们分别是对象数据库、陀螺仪和加速计
189
00:09:16,089 --> 00:09:19,724
the object database so there's a lot of frameworks and
还有地图相关的东西
190
00:09:19,726 --> 00:09:22,327
we'll cover as many of them as we can. And
还有一些其它的框架,我们将会尽可能地介绍它们
191
00:09:22,329 --> 00:09:24,229
really just as important of all these things,
还有和这些东西一样重要的一样东西
192
00:09:24,231 --> 00:09:28,667
there's a design strategy or design methodology, okay, for
就是设计 iOS 应用的一种设计策略或设计方式
193
00:09:28,669 --> 00:09:31,970
iOS called MVC, it's used on other platforms as well.
叫作 MVC,在其它平台上也有应用
194
00:09:31,972 --> 00:09:35,040
How many people here have programmed before in an MVC
有多少同学在之前使用过 MVC
195
00:09:35,042 --> 00:09:38,143
type of environment? See, so about half of you.
哦,大概一半
196
00:09:38,145 --> 00:09:40,412
Now I'm going to, since it's only half of you,
既然只有一半的人了解 MVC
197
00:09:40,414 --> 00:09:41,212
I will spend Wednesday,
在周三 lecture
198
00:09:41,214 --> 00:09:44,482
the start of the lecture, explaining MVC. Okay, how,
开始的时候,我会介绍 MVC
199
00:09:44,484 --> 00:09:49,454
what MVC is, how it applies in iOS development. But it's very
什么是 MVC,在 iOS 开发中如何运用
200
00:09:49,456 --> 00:09:51,990
important to know, it's a very important thing to know. And
这都非常重要
201
00:09:51,992 --> 00:09:55,093
I'm even going to mention it a little bit in my demo today.
我也会在今天的 demo 中介绍一些
202
00:09:55,095 --> 00:09:59,197
And I'll just give you the 10 second overview of
我只会给你们简单地讲述一下它
203
00:09:59,199 --> 00:10:02,701
it. Because I can't really do anything, I can't show you any
因为我还不能做任何事情
204
00:10:02,703 --> 00:10:06,304
demo without talking about MVC, it's that important.
在介绍 MVC 之前我不能做任何 demo,它就是这么重要
205
00:10:06,306 --> 00:10:10,775
Okay? So this next slide here is kind of giving you
下一张幻灯片简单地介绍了一下
206
00:10:10,777 --> 00:10:13,144
a summary of what I'm gonna show you in the demo today.
今天的这个 demo 大概要做些什么
207
00:10:13,146 --> 00:10:15,146
You don't need to read this slide now,
你现在还不需要阅读这上面的内容
208
00:10:15,148 --> 00:10:18,216
my slides will be posted for you so you'll always going to
幻灯片都会在课后发给你们
209
00:10:18,218 --> 00:10:20,919
be able to go back. This is a kind of slide you go back and
这一张就是那种课后回去阅读
210
00:10:20,921 --> 00:10:24,456
look at later to make sure, yeah did I understand that in
问问自己到底有没有看懂之类的幻灯片
211
00:10:24,458 --> 00:10:27,192
the demo, etc. Also as I'm doing the demo you'll
你们还会有我做 demo 时候的视频
212
00:10:27,194 --> 00:10:30,629
have the video of it. You'll have a video of this demo so
所以在我做 demo 的时候
213
00:10:30,631 --> 00:10:32,197
you don't need to furiously take notes or
你们不需要非常着急地记笔记
214
00:10:32,199 --> 00:10:34,499
try to follow along to understand what I'm doing.
或者完全跟着我同步做
215
00:10:34,501 --> 00:10:39,004
You can watch it at your leisure later.
你可以在课后看视频
216
00:10:39,006 --> 00:10:42,307
All right, so I'm not going get back to my slides before,
在 demo 之后,就不回放之前的幻灯片了
217
00:10:42,309 --> 00:10:45,577
after the demo so let me tell you what's coming up and then
所以我先告诉你们后续会发生什么
218
00:10:45,579 --> 00:10:50,682
the rest of this is gonna be a demo. Today I have assigned
然后这门课的后半部分都会是 demo
219
00:10:50,684 --> 00:10:54,119
and it's on Piasa already, reading assignment number 1.
今天我已经把第一次作业放到 Piazza(他们的课程平台) 上了
220
00:10:54,121 --> 00:10:57,355
Okay, the reading assignments in this class are all learning
这节课的阅读作业都是关于 Swift 的
221
00:10:57,357 --> 00:10:59,958
Swift. You're gonna have three reading assignments and
一共会有三次阅读作业
222
00:10:59,960 --> 00:11:01,693
all of the three of them are gonna basically
这几次作业
223
00:11:01,695 --> 00:11:05,897
take you through the entire Swift language guide, okay. So
会让你对 Swift 有一个完整的了解
224
00:11:05,899 --> 00:11:09,034
that's what the reading assignments are all about.
阅读作业的内容就是这些
225
00:11:09,269 --> 00:11:11,369
On Wednesday, I'm gonna continue the demo that I'm
周三,我会继续进行这个 demo
226
00:11:11,371 --> 00:11:13,938
doing today. I'm gonna talk about MVC as I said and
会介绍 MVC
227
00:11:13,940 --> 00:11:18,109
then programming assignment number 1 will go out. And
第一次编程作业也会放出
228
00:11:18,111 --> 00:11:20,045
that's going to be due one week later.
一周后截至
229
00:11:20,047 --> 00:11:25,483
Before lecture the next Wednesday. On Friday we
在下周三的 lecture 之前
230
00:11:25,485 --> 00:11:29,087
have an optional section, room and time to be determined.
会有个可选的部分,时间和地点有待确定
231
00:11:29,089 --> 00:11:32,023
Probably around 1 o' clock. I'm not sure where.
也许 1 点左右,我也不太确定在哪
232
00:11:32,025 --> 00:11:34,926
This Friday's and each Friday sometimes we'll have these
有的周五都会有课
233
00:11:34,928 --> 00:11:36,561
sections, sometimes not and it's optional,
它们是可选的
234
00:11:36,563 --> 00:11:38,430
you don't have to show up if you don't want to but
你不必须参加
235
00:11:38,432 --> 00:11:39,998
it's gonna be pretty valuable information.
但是课上会有很有价值的信息
236
00:11:40,000 --> 00:11:42,734
This Friday is how to use the debugger, which as you can
这个周五会介绍如何使用 debugger,还是挺有价值的内容
237
00:11:42,736 --> 00:11:49,240
imagine pretty valuable. Next Monday next week in general
下周一
238
00:11:49,242 --> 00:11:52,143
we will be doing more of the same of getting started here
会介绍如何上手
239
00:11:52,145 --> 00:11:54,713
with Swift and Xcode 7. You're, you're reading
Swift 和 Xcode 7
240
00:11:54,715 --> 00:11:57,716
assignment from today is gonna be due on Monday, and
今天的阅读作业会在周一之前截至
241
00:11:57,718 --> 00:11:59,684
then, you'll have Reading Assignment 2 go
接下来会有第二个阅读作业
242
00:11:59,686 --> 00:12:01,352
out which will be due the next Monday, so
下周一之前截至
243
00:12:01,354 --> 00:12:03,088
the Reading Assignments for the first three weeks
前三周的阅读作业
244
00:12:03,090 --> 00:12:05,423
are overlapping the Program Assignments. The first two
和编程作业是重叠的
245
00:12:05,425 --> 00:12:08,026
programing assign- assignments are quite easy. So
前两个作业是比较简单的
246
00:12:08,028 --> 00:12:10,328
there won't be a problem where you're really overloaded.
所以这些作业对于你们来说也不会显得很多
247
00:12:10,330 --> 00:12:12,530
Starting with programming assignment number 3,
第三次编程作业
248
00:12:12,532 --> 00:12:15,166
it gets quite a bit steeper, okay.
会有一个很大的跳跃
249
00:12:15,168 --> 00:12:15,834
Question? >> The programming
有问题?
250
00:12:15,836 --> 00:12:19,270
assignments, they ask you to solve specific tasks?
>> 编程作业是解决特定的编程任务吗?
251
00:12:19,272 --> 00:12:22,006
Are there any ones that are more predicted?
会有一些主观的问题吗?
252
00:12:22,008 --> 00:12:22,707
>> Yes, so the question is
>> 问题是,
253
00:12:22,709 --> 00:12:24,409
do the programming assignments ask you to solve
编程作业是让你解决特定的编程任务
254
00:12:24,411 --> 00:12:27,011
a specific task, or are they kind of open-ended? And
还是有一些开放性的问题?
255
00:12:27,013 --> 00:12:29,781
the answer is the program assignments have very specific
答案是,编程作业有很明确的需求
256
00:12:29,783 --> 00:12:33,551
required tasks that you must do. You can do them any way
你可以通过任何方式完成
257
00:12:33,553 --> 00:12:36,154
you want, but you must satisfy the required tasks.
但是你必须要满足指定的任务要求
258
00:12:36,156 --> 00:12:39,157
As far as open ended is, your gonna have a final project in
提到开放性任务,你们最终持续三周的项目
259
00:12:39,159 --> 00:12:40,825
three weeks, that's completely open ended.
是完全开放性的
260
00:12:40,827 --> 00:12:42,594
You get to choose entirely what you're gonna do,
你可以自己选择你想要做什么
261
00:12:42,596 --> 00:12:45,396
completely on your own, okay? If you read the document
如果你读 Piazza 上的文档
262
00:12:45,398 --> 00:12:48,800
that's on Piazza, it explains all that, okay? So for
上面就会有相关的解释了
263
00:12:48,802 --> 00:12:50,902
those of you who arrived late, read that doc,
所以对于迟到的这些同学,去读文档
264
00:12:50,904 --> 00:12:54,005
get on Piazza, read it, stay on Piazza, it's all about
上 Piazza,读内容,泡在 Piazza 上
265
00:12:54,007 --> 00:12:56,207
Piazza in this class in terms of communication.
Piazza 就是用来交流的工具
266
00:12:56,209 --> 00:12:59,477
So get on there. Okay, all right,
好的
267
00:12:59,479 --> 00:13:03,648
that's it. I'm going to dive right into this demo right
我们现在开始做 demo
268
00:13:03,650 --> 00:13:08,620
here. We're going to start by launching Xcode.
先启动 Xcode
269
00:13:08,622 --> 00:13:11,589
Xcode is free, you just go to the Mac app store and
它是免费的,可以到 Mac App Store 上下载
270
00:13:11,591 --> 00:13:15,226
download it. And when you launch it, you're going to get
启动它
271
00:13:15,228 --> 00:13:19,697
this splash screen is going to appear right here. Let's hide
会显示一个启动画面
272
00:13:19,699 --> 00:13:23,134
these other things. So here's Xcode. This spash screen has
把其它的隐藏起来,这就是 Xcode
273
00:13:23,136 --> 00:13:25,703
a list of all the projects that you've been working on so
启动界面的这个列表包含你当前进行的项目
274
00:13:25,705 --> 00:13:28,540
right now we have none cuz it's the start of the quarter.
现在还没有,因为课程才刚开始
275
00:13:28,542 --> 00:13:30,441
But you'll have you know each week you're gonna
但你应当知道
276
00:13:30,443 --> 00:13:33,344
have more and more projects piling up in this side. And
随着课程的进行,你会有越来越多的项目
277
00:13:33,346 --> 00:13:35,747
then these three choices are the three ways to kinda
这三个选项是进入 Xcode 的三个选项
278
00:13:35,749 --> 00:13:38,416
get into Xcode. 1, Here, playground,
第一个选项 playground
279
00:13:38,418 --> 00:13:42,921
that's basically, it gives you the ability to write iOS apps,
它给了你写 iOS App 的能力
280
00:13:42,923 --> 00:13:45,924
and play with Xcode without building an app. Okay.
但不需要新建一个 App
281
00:13:45,926 --> 00:13:49,194
So you can call APIs, put things on screen, draw things,
所以你可以调用 API,画一些控件或图形等等
282
00:13:49,196 --> 00:13:52,330
etc. It's kind of a playground for playing around with iOS.
它就像一个学习开发 iOS 应用的小训练场
283
00:13:52,332 --> 00:13:54,599
We really won't be using that much in this app.
我们不会在这个 App 中使用它
284
00:13:54,601 --> 00:13:57,001
But in this class, in my demos, but
但在这门课,或者 demo 中
285
00:13:57,003 --> 00:13:59,404
you can certainly you're welcome to use them,
在你了解 Xcode 的过程中,
286
00:13:59,406 --> 00:14:01,439
as you're playing around with Xcode.
还是欢迎你使用它的
287
00:14:01,441 --> 00:14:03,508
It's kind of a fun environment. Down at
它是个很有趣的编程环境
288
00:14:03,510 --> 00:14:06,911
the bottom here you can check out an existing Xcode,
在最下面,你可以添加已有
289
00:14:06,913 --> 00:14:11,716
project, iOS project, that is in Source Code, management.
源码管理的 Xcode 项目
290
00:14:11,718 --> 00:14:14,118
Okay, now we're going to talk about Source Code management
也许会在周五的课程中讨论源码管理
291
00:14:14,120 --> 00:14:16,888
probably at a Friday section in this quarter but
但是不会在
292
00:14:16,890 --> 00:14:18,122
not right at the beginning of the class.
这门课程刚开始的时候就讲
293
00:14:18,124 --> 00:14:20,859
But Source Code manager is basically a way that you can
源码管理就是你可以通过类似数据库的方式
294
00:14:20,861 --> 00:14:23,728
check your code in and out of like a database okay,
来管理你的源代码
295
00:14:23,730 --> 00:14:27,131
so that you can work with a team, each make changes,
通过这种方式进行团队协作
296
00:14:27,133 --> 00:14:28,099
merge your changes, all that stuff.
每个人对代码产生一些修改,然后合并这些修改之类的
297
00:14:28,101 --> 00:14:32,370
So here is where you would check out your project to work
所以这个就是你获取已有源码管理项目的地方
298
00:14:32,372 --> 00:14:36,207
on. And this middle one is to create a new Xcode project,
中间的这个是新建一个 Xcode 项目
299
00:14:36,209 --> 00:14:38,176
a new Xcode App and that's we're gonna do right now,
现在就进行这一项
300
00:14:38,178 --> 00:14:41,212
okay? We're gonna create from scratch a new app. Most of my
从头开始创建一个新的 App
301
00:14:41,214 --> 00:14:44,449
demos involved creating an application from scratch,
几乎所有的 demo 都是从头开始创建的
302
00:14:44,451 --> 00:14:45,750
okay? Just because I think it's easier for
因为在目前阶段
303
00:14:45,752 --> 00:14:49,120
you to learn if there's no magic behind the scenes where
从零开始写项目
304
00:14:49,122 --> 00:14:49,921
things are appearing out of nowhere. So
对你们来说更容易一些
305
00:14:49,923 --> 00:14:53,658
where gonna start from scratch and go from there each time.
所以我们从头开始新建应用
306
00:14:53,660 --> 00:14:55,660
All right, so I'm gonna click on this.
点这里
307
00:14:55,662 --> 00:14:58,029
You can see it wants to create an app for me so
它会为我新建一个应用
308
00:14:58,031 --> 00:14:59,264
it's gonna ask me some questions.
会问我几个问题
309
00:14:59,266 --> 00:15:01,866
First it wants to know what kind of app I wanna build, and
首先,它问我想要创建哪种类型的应用
310
00:15:01,868 --> 00:15:04,903
we wanna build an iOS application. So make sure you
我们想新建 iOS App
311
00:15:04,905 --> 00:15:09,974
click iOS Application here, not watch app, or AppleTV app,
所以确认你点击了 iOS Application,而不是其它的
312
00:15:09,976 --> 00:15:12,610
or an OS X app, okay? We don't want any of that.
我们不想要其它的
313
00:15:12,612 --> 00:15:16,047
We want iOS application. And inside there we have some
我们想要 iOS 应用
314
00:15:16,049 --> 00:15:20,652
choices of the kind of iOS app we wanna build, like a game or
在这里,它问我们想要建哪种类型的 iOS 应用
315
00:15:20,654 --> 00:15:23,087
this master detail which is usually for browsing
例如是 Game 还是为了浏览数据库的 Master Detail Application
316
00:15:23,089 --> 00:15:25,890
a database. We're almost always gonna choose this one,
我们几乎只会先择这一项
317
00:15:25,892 --> 00:15:30,028
single view application. The view there is kinda referring
Single View Application,这里的 View 代表
318
00:15:30,030 --> 00:15:32,196
to the view in MVC, model view controller.
MVC 中的 V
319
00:15:32,198 --> 00:15:35,667
That design paradigm but this is the ba-, most basic
这个模板会为我们的创建最基本的
320
00:15:35,669 --> 00:15:39,003
template and it gives us the least amount of code to start
MVC 骨架,它会产生最少量的代码
321
00:15:39,005 --> 00:15:41,472
with, which is what we want cuz we're learning so we wanna
这也是我们希望的
322
00:15:41,474 --> 00:15:44,676
write the code. We don't want it to, write any code for us.
我们正在学习,所以希望写代码的是我们,而不是 Xcode
323
00:15:44,678 --> 00:15:46,678
So we're gonna pick single view template there.
所以这里选择 Single View 的模板
324
00:15:46,680 --> 00:15:49,447
Now it wants to know a few things about our app,
现在它需要知道关于 App 的几点信息
325
00:15:49,449 --> 00:15:52,784
what the name of our app is, etc. The app we're going
App 的名字是什么等等
326
00:15:52,786 --> 00:15:55,853
to build today is a calculator and it's gonna look quite
今天要创建的应用是一个计算器
327
00:15:55,855 --> 00:15:58,790
a bit like this calculator, not exactly the same, but
它看起来非常像一个计算器,但又不完全一致
328
00:15:58,792 --> 00:16:01,826
similar to this calculator. This is a basic calculator,
它会像这个,最基本的计算器
329
00:16:01,828 --> 00:16:04,262
it comes with Mac OS and it's gonna look like this,
是 Mac OS 上的原生应用
330
00:16:04,264 --> 00:16:06,464
it's gonna have some keypads, gonna have some operations,
它会有键盘,会有运算符
331
00:16:06,466 --> 00:16:09,867
gonna have a little display in here. Basically a calculator,
在这里还会有个小显示器
332
00:16:09,869 --> 00:16:11,336
so I'm gonna call my app calculator.
所以给我的 App 起名为 calculator
333
00:16:11,338 --> 00:16:13,938
So that's the name of this app. When I look at it on my
它就是 App 的名字,当我在手机上查看它的时候
334
00:16:13,940 --> 00:16:16,074
phone it's gonna say calculator is the name of it.
上面显示的名字就会是 calculator
335
00:16:16,076 --> 00:16:19,010
Okay? This organization name can be anything you want.
这个组织名你可以随意填
336
00:16:19,012 --> 00:16:22,880
It's basically gonna appear in the copyright of your code.
它会显示在你代码的版权信息上
337
00:16:22,882 --> 00:16:23,748
Okay? When you write code,
当你写代码的时候
338
00:16:23,750 --> 00:16:25,116
at the top it's gonna say copyright,
在最上面会显示版权信息
339
00:16:25,118 --> 00:16:26,718
whatever you put here. Put whatever you want.
你可以在这里放什么都行
340
00:16:26,720 --> 00:16:28,953
Your name or whatever. This however,
姓名之类的
341
00:16:28,955 --> 00:16:33,925
wants to uniquely identify you, okay, as a developer. And
这里,需要一个不重复的标识
342
00:16:33,927 --> 00:16:36,728
a good way to do it is this reverse DNS notation here,
一个解决这个问题的很好方式是这个逆向的 DNS 标志
343
00:16:36,730 --> 00:16:40,698
edu.stanford.cs193p and then instead of putting instructor,
edu.stanford.cs193p
344
00:16:40,700 --> 00:16:44,268
put your SU net ID. Cuz no one else has that.
在 instructor 那里填上你的学号就好,因为这不会重复
345
00:16:44,270 --> 00:16:46,304
This would uniquely identify you in the universe.
这会在宇宙中唯一确定你
346
00:16:46,306 --> 00:16:49,407
No one else is going to have that organization identifier.
没人会跟你重复的
347
00:16:49,409 --> 00:16:50,875
And then it builds a unique
随后它会产生一个独一无二的标识
348
00:16:50,877 --> 00:16:54,679
identifier from the app by combining these two things.
通过把这两个结合起来的方式
349
00:16:54,681 --> 00:16:57,315
Okay? You get to choose the language you want to program
在这里选择你想要编程的语言
350
00:16:57,317 --> 00:17:00,385
in here. It actually doesn't matter what you choose
在这里选择什么无关紧要
351
00:17:00,387 --> 00:17:03,154
here, this is just going to be the language that the initial
它只会为你创建
352
00:17:03,156 --> 00:17:05,723
template is going to give you it starts out in.
相关语言的初始模板
353
00:17:05,725 --> 00:17:07,525
You can freely mix Objective C and
在写 iOS 应用的时候
354
00:17:07,527 --> 00:17:10,561
Swift when you're building IOS apps. Okay? Back and
你可以把 Swift 和 Objective-C 混合起来
355
00:17:10,563 --> 00:17:13,364
forth they're, you don't have to pick one and be stuck with
你不需要在选择完一个之后就卡在这了
356
00:17:13,366 --> 00:17:16,234
it. We're going to do all of our programming in this class
在这门课中我们所有的编程都会使用 Swift
357
00:17:16,236 --> 00:17:19,570
in Swift however. Then this is whether we want
这里决定我们是要搭建一个
358
00:17:19,572 --> 00:17:21,139
to build an iPhone only calculator.
专属 iPhone 的
359
00:17:21,141 --> 00:17:24,909
An iPad only calculator. Or a universal calculator that will
还是 iPad 的
360
00:17:24,911 --> 00:17:27,979
work on either iPad or iPhone. Okay? And
或者是它们都可以使用的计算器
361
00:17:27,981 --> 00:17:30,181
we're gonna go universal even though the first two weeks
选择 universal,尽管前两周
362
00:17:30,183 --> 00:17:33,151
we're going to be focusing on iPhone only. In the third week
我们只会专注在 iPhone 上
363
00:17:33,153 --> 00:17:37,688
we'll expand our calculator to work on an iPad as well. Here,
在第三周,我们会让计算器在 iPad 上也正常地跑起来
364
00:17:37,690 --> 00:17:39,657
we're not going to be using databases yet, that will be
目前我们还不需要使用数据库
365
00:17:39,659 --> 00:17:43,661
about week six and we will be talking about testing later in
第六周我们会讨论测试
366
00:17:43,663 --> 00:17:45,563
the quarter. Probably in a Friday section again, but
也许在周五的课程中
367
00:17:45,565 --> 00:17:48,733
we're not doing it now so you can leave these unchecked.
我们现在不会处理这些,所以这些不需要勾选
368
00:17:48,735 --> 00:17:49,767
All right, so now I hit Next, and
点 Next
369
00:17:49,769 --> 00:17:53,037
it says where do you wanna put this project? I'm gonna put
它问你想把项目放在哪里
370
00:17:53,039 --> 00:17:56,808
it in my home directory, in a folder called Developer, and
我把它放到 ~/Developer 中
371
00:17:56,810 --> 00:17:59,444
I highly recommend you do the same, okay.
我也建议你们这样做
372
00:17:59,446 --> 00:18:02,180
Home Directory Developer, so all your apps will start
~/Developer,你们所有的 App 都放到这里
373
00:18:02,182 --> 00:18:05,750
piling up in here. Here's the source control thing I was
关于源码管理
374
00:18:05,752 --> 00:18:07,785
talking about, we're not gonna do this at the beginning, so
我们一开始先不管
375
00:18:07,787 --> 00:18:10,521
we're gonna leave this unchecked. All right. So
所以先不勾选
376
00:18:10,523 --> 00:18:13,324
we hit create, and it creates our first app. Now, it's
点 create,它就创建了我们的第一个 App
377
00:18:13,326 --> 00:18:16,961
actually created these six files, over here on the side.
它实际上创建了这 6 个文件,在这侧边
378
00:18:16,963 --> 00:18:17,762
And over the course of the quarter,
在课程期间
379
00:18:17,764 --> 00:18:20,498
I'll show you what all six of these are about. Today,
我会告诉你们这 6 个文件都是做什么的
380
00:18:20,500 --> 00:18:23,768
we're only gonna focus on two of them.
今天我们只会关注这两个
381
00:18:24,871 --> 00:18:27,772
How many of you have used Xcode before, for any class or
你们有谁用过 Xcode?
382
00:18:27,774 --> 00:18:31,509
anything? Okay, so about a third of you.
好,大概 1/3
383
00:18:31,511 --> 00:18:34,178
So let me explain a little bit about Xcode's UI,
我先介绍一下 Xcode 的 UI
384
00:18:34,180 --> 00:18:36,781
what it looks like. You see this area on the left,
左边这里
385
00:18:36,783 --> 00:18:40,051
this blue area? That's called the Navigator and
蓝色的区域,它是 Navigator(导航器)
386
00:18:40,053 --> 00:18:43,554
it's basically for navigating your project.
它用来导航你的项目
387
00:18:43,556 --> 00:18:44,755
And you can do it in different ways.
通过几种不同的方式
388
00:18:44,757 --> 00:18:48,459
This is the Project Navigator that we see right here, and
现在这个是项目导航
389
00:18:48,461 --> 00:18:50,595
it's basically showing you your files.
它用来显示项目的文件
390
00:18:50,597 --> 00:18:52,029
You can arrange them in folders and stuff, but
你可以通过文件夹来重新组织它们
391
00:18:52,031 --> 00:18:55,166
just basically the files in your app. But you can navigate
但只是导航 App 中的文件
392
00:18:55,168 --> 00:18:58,669
by searching, okay, you can type some search text in here,
但是你可以通过搜索来查找 App 文件中的内容
393
00:18:58,671 --> 00:19:01,472
search for it in your app. You can navigate all your
如果你在 debug 的时候
394
00:19:01,474 --> 00:19:04,675
breakpoints if you're debugging. You can go back and
可以在这里查看所有断点
395
00:19:04,677 --> 00:19:07,345
look at all your old builds to see any errors or
查看之前编译版本的错误或警告
396
00:19:07,347 --> 00:19:11,149
warnings you have, okay, even if you fixed it you go back,
即使你已经修复了它们并返回了
397
00:19:11,151 --> 00:19:13,484
and look at them. So the navigator is kind
Navigator 就像是用来关注
398
00:19:13,486 --> 00:19:16,521
of how you keep track of what's going on in your app.
你的 App 到底发生了什么事情的地方
399
00:19:16,523 --> 00:19:19,457
On the right hand side here we have the utilities area,
在右手边,有 Utilities(工具) 区
400
00:19:19,459 --> 00:19:22,426
which is actually two windows, see it's split top and
有两个窗口,分成上下两部分
401
00:19:22,428 --> 00:19:26,230
bottom here. The top part is essentially an inspector,
上部是 Inspector(监视器)
402
00:19:26,232 --> 00:19:29,800
it's going to let you analyze and change attributes of
你可以通过它看到或改变
403
00:19:29,802 --> 00:19:33,004
whatever's selected in this main window. Right now we
在主窗口选中的东西的属性
404
00:19:33,006 --> 00:19:35,673
don't have anything selected, so it's not showing anything.
现在没有选中任何东西,所以没显示任何东西
405
00:19:35,675 --> 00:19:38,676
And this bottom is essentially like a palette. Okay,
这下面的部分就像一个调色板
406
00:19:38,678 --> 00:19:41,979
you're gonna be an artist building your app, okay, and
在写应用的时候,你就是一个艺术家
407
00:19:41,981 --> 00:19:45,983
you're gonna need things like buttons and web views,
因为你需要类似于 button 或 web view 之类的东西
408
00:19:45,985 --> 00:19:47,818
and things like that, to build your app, and
用来搭建你的应用
409
00:19:47,820 --> 00:19:48,953
you're gonna be finding them all,
你会在这里找到它们
410
00:19:48,955 --> 00:19:51,122
and pulling them all out of here. So it's your palette,
然后可以从这里把它们拖出来,所以这就是你的调色板
411
00:19:51,124 --> 00:19:54,825
your artist palette, as you build things. Okay? You can
你的艺术调色板,用来创造东西
412
00:19:54,827 --> 00:19:57,361
show and hide these two side panels with these buttons,
你可以通过这些按扭显示或者隐藏侧面的面板
413
00:19:57,363 --> 00:20:00,831
up here. I will tend to not do command keys, except for
我尽量不使用快捷键,除了复制粘贴
414
00:20:00,833 --> 00:20:03,801
like copy and paste, so that you can see what's going on.
所以你们就可以知道我到底做了哪些事情
415
00:20:03,803 --> 00:20:06,204
But there are tons of command keys in Xcode, and
但是 Xcode 中有大量的快捷键
416
00:20:06,206 --> 00:20:10,441
if you go To Xcode preferences and look under
在 Xcode 的设置界面
417
00:20:10,443 --> 00:20:12,610
key bindings right here you can see all of them and
你可以发现有大量大量的快捷键
418
00:20:12,612 --> 00:20:15,680
there's tons and tons and tons and you can even change them.
你也可以对它们进行定制
419
00:20:15,682 --> 00:20:17,348
Of course if you don't like them but
如果你不喜欢它们也没关系
420
00:20:17,350 --> 00:20:18,849
it has a lot of commands. All right, so
但是它有大量的快捷键
421
00:20:18,851 --> 00:20:21,886
I'll try to avoid using those command keys. So you can hide
我尽量不使用快捷键
422
00:20:21,888 --> 00:20:25,756
those with these things so you have hidden both sides, right.
可以使用按扭来做这些事情
423
00:20:25,758 --> 00:20:28,426
So you can kind of manage your screen real estate.
你可以管理屏幕的分辨率
424
00:20:28,428 --> 00:20:31,662
Now I have a very low-resolution screen here, so
我电脑的分辨率非常低
425
00:20:31,664 --> 00:20:34,732
I have to manage my real estate quite efficiently.
所以为了高效我需要改变自己的分辨率
426
00:20:34,734 --> 00:20:36,567
If you have a higher resolution screen,
如果你的分辨率很高
427
00:20:36,569 --> 00:20:39,837
then you would obviously not be doing as much hiding and
你就不需要把这些东西给隐藏起来了
428
00:20:39,839 --> 00:20:44,108
showing of these things. So if we look at the navigator,
现在我们看 Navigator
429
00:20:44,110 --> 00:20:44,775
what's selected right now?
当前选中的是什么?
430
00:20:44,777 --> 00:20:48,980
It's the project itself at the very top level, this blue bar.
是最顶层的项目本身
431
00:20:48,982 --> 00:20:49,680
This selecting the project.
它选中了项目
432
00:20:49,682 --> 00:20:53,217
So we're actually seeing the project, the kind
所以在主窗口
433
00:20:53,219 --> 00:20:55,820
of settings of the project, in the main window here.
我们可以看到这个项目的设置
434
00:20:55,822 --> 00:20:59,624
This main window. Here are some of them here, right?
这个主窗口,这里有一些设置
435
00:20:59,626 --> 00:21:01,325
Some of these we set on that first page,
第一页有一些可以设置的
436
00:21:01,327 --> 00:21:05,029
like the bundle identifier and whether it was universal or
例如 Bundle Identifier(包的 ID)
437
00:21:05,031 --> 00:21:07,898
iPad and iPhone, etc. But there's plenty more
是通用的还是只为了 iPhone 或 iPad 而造
438
00:21:07,900 --> 00:21:10,868
settings over here. We'll get to most of those settings as
这里还有许多其它设置信息
439
00:21:10,870 --> 00:21:14,372
the quarter goes by. The only setting that's important for
随着课程的进行,我们会逐渐了解这些设置
440
00:21:14,374 --> 00:21:16,540
you to know now, is this team setting.
现在需要知道的设置信息是这个 team
441
00:21:16,542 --> 00:21:19,477
See here where it says team none? You're gonna wanna be
现在上面显示的是 none
442
00:21:19,479 --> 00:21:24,782
able to write your app, and have it run on a device. Okay.
你想让你写的 App 运行在真实的设备上
443
00:21:24,784 --> 00:21:26,817
It has a simulator, which I'm gonna use today.
我们今天会使用模拟器
444
00:21:26,819 --> 00:21:29,253
But you also wanna be able to run on a device. And
但是你想让它运行在真实的设备上
445
00:21:29,255 --> 00:21:33,124
to do that you need to register an Apple ID.
为了这样,我们需要注册一个 Apple ID
446
00:21:33,126 --> 00:21:36,060
So here I already have mine, this CS193p Instructor, but
我已经注册好了
447
00:21:36,062 --> 00:21:39,630
you're going to go down here to add an account, and
不过你需要在这下面添加一个账号
448
00:21:39,632 --> 00:21:42,466
you're going to give an Apple ID and password. Any Apple ID
填入 Apple ID 和密码
449
00:21:42,468 --> 00:21:45,736
will do, you don't have to join any program or anything,
任意一个 Apple ID 都可以,不需要加入什么开发者计划之类的
450
00:21:45,738 --> 00:21:48,372
just any Apple ID will work here. And
普通的 Apple ID 就可以
451
00:21:48,374 --> 00:21:49,874
you'll put the ID and password in, and
填入 ID 和密码
452
00:21:49,876 --> 00:21:51,676
then you're going to select it here. And
选这里
453
00:21:51,678 --> 00:21:56,280
then it's going to appear here, like this. Okay? Now,
它会在这里出现
454
00:21:56,282 --> 00:21:59,617
when you have a team selected now you can plug a device
当 team 有选择的东西以后,你就可以插上一个设备
455
00:21:59,619 --> 00:22:02,720
in and you're gonna get this warning here, no provisioning
会有一个没有证书的警告
456
00:22:02,722 --> 00:22:07,158
profiles found. And you just click fix issue right there.
点这里的 fix issue
457
00:22:07,160 --> 00:22:08,926
You have to be connected to the network at the time,
这时你需要联网
458
00:22:08,928 --> 00:22:11,829
all right? Fix issue and it'll fix that.
它会修复这个问题
459
00:22:11,831 --> 00:22:14,198
It'll basically get the ID of your device,
它会获取你设备的 ID
460
00:22:14,200 --> 00:22:18,436
register it with you as the developer and off you go.
注册成为开发者,然后就可以了
461
00:22:18,438 --> 00:22:21,672
Okay? So you'll need to do that. You only really need to
你只需要在想把 App 运行在真实设备的时候
462
00:22:21,674 --> 00:22:23,808
do that when it comes to running on a device.
才需要解决这个问题
463
00:22:23,810 --> 00:22:25,476
When we're running in a simulator you're not gonna
当在模拟器上运行的时候
464
00:22:25,478 --> 00:22:28,913
need to do that, but you might as well get this started now.
就不用这么做
465
00:22:29,048 --> 00:22:32,316
Okay? Now what about these other files over here in the
Navigator 中的其它这些文件
466
00:22:32,318 --> 00:22:35,453
navigator that it made these 6 files. Well like I said, were
都有什么作用呢?
467
00:22:35,455 --> 00:22:37,922
not going to talk about all of them. This one for example,
我不会对它们每一个都进行详细讲解
468
00:22:37,924 --> 00:22:42,093
assets.xcassetts. That's all your media assets. So that's
例如 assets.xcassets 是存放媒体文件的地方
469
00:22:42,095 --> 00:22:46,997
images, sounds, icons, things like that. Here for example is
图片,声音和图标之类的东西
470
00:22:46,999 --> 00:22:47,965
the app icon which I haven't set so
例如这里的应用图标
471
00:22:47,967 --> 00:22:51,635
all of these are all blank. So we're not really gonna talk
我还没有设置,所以这些都是空白的
472
00:22:51,637 --> 00:22:53,738
about that today but we'll eventually talk about it.
我今天不会介绍,但总有一天会介绍的
473
00:22:53,740 --> 00:22:56,841
And in fact not only am I not gonna talk about
实际上今天我不但不会介绍这个 xcassets 文件
474
00:22:56,843 --> 00:22:59,877
this xcassets thing right here, but I'm also not gonna
同样我也不会介绍
475
00:22:59,879 --> 00:23:02,813
talk about this app delegate or the launch screen or
AppDelegate 和 LaunchScreen 还有
476
00:23:02,815 --> 00:23:06,350
the info.plist. Okay, so I'm gonna select all those and
info.plist 文件
477
00:23:06,352 --> 00:23:10,254
right-click on them and choose new group from selection and
我选中它们
478
00:23:10,256 --> 00:23:13,424
put them all in a folder. Okay, which I'm gonna call
然后右键为它们新建一个组
479
00:23:13,426 --> 00:23:17,261
supporting files. Because that's what they are really,
起名为 Supporting files
480
00:23:17,263 --> 00:23:19,330
they're kind of files to support my app.
因为它们对我 App 起的就是支持作用
481
00:23:19,332 --> 00:23:20,264
You see they're still there,
你看,它们还在这里
482
00:23:20,266 --> 00:23:21,799
I just kind of got them out of the way so
我只是把它们清理一下
483
00:23:21,801 --> 00:23:23,834
we can focus on these two files
这样我就可以关注这两个文件
484
00:23:23,836 --> 00:23:27,972
which is the most important thing to our demo today. Okay?
它俩是今天 demo 中最重要的部分
485
00:23:27,974 --> 00:23:31,409
Now what are these two files? Now is where I have to talk
这俩文件是干什么的?
486
00:23:31,411 --> 00:23:34,812
about this MVC thing, okay? So MVC stands for
现在我就得讲讲 MVC 了
487
00:23:34,814 --> 00:23:39,917
Model View Controller, okay? Model View Controller is a way
MVC 代表 Model(模型) View(视图) Controller(控制器)
488
00:23:39,919 --> 00:23:43,354
of kind of dividing up all the classes in our object orient
MVC 是一种把我们面向对象 App 中的类分成几种不同组的方式
489
00:23:43,356 --> 00:23:46,457
app into three different groups, the model group,
Model 组
490
00:23:46,459 --> 00:23:48,826
the view group, and the controller group.
View 组和 Controller 组
491
00:23:48,828 --> 00:23:52,163
Okay, what are those three groups? The model group is
这三个组都是什么?Model 组是
492
00:23:52,165 --> 00:23:55,900
what our program does It's UI independent.
关于我们的 App 是做什么的,它与 UI 是独立的
493
00:23:55,902 --> 00:23:58,736
So for a calculator it's going to be all the calculating.
对于计算器来说,它是关于计算的
494
00:23:58,738 --> 00:24:01,172
The things that actually does the calculations, right?
它实际做的事情就是计算,对吧?
495
00:24:01,174 --> 00:24:04,275
That's UI independent, that's going to be our model, okay?
它是 UI 独立的,它就是我们的 Model
496
00:24:04,277 --> 00:24:08,012
The view is the user interface that the user interacts with.
View 就是用户直接接触的 UI 界面
497
00:24:08,014 --> 00:24:11,315
So for the calculator it's the buttons and the display and
对于计算器来说就是按扭还有显示之类的东西
498
00:24:11,317 --> 00:24:14,051
all that stuff. That is the view. Okay, usually that's
这就是 View
499
00:24:14,053 --> 00:24:17,254
made of generic user interface elements like buttons and
通常它是由基本的例如按扭之类的 UI 元素组成
500
00:24:17,256 --> 00:24:20,391
things like that. And then there's the controller,
接着就是 Controller
501
00:24:20,393 --> 00:24:23,461
the controller is the glue between the model and
controller 是 model 与 view 之间的粘合剂
502
00:24:23,463 --> 00:24:26,130
the view. Okay, it's the thing that listens to what's
它会监听UI发生了些什么事情
503
00:24:26,132 --> 00:24:30,267
happening in the UI, makes decisions, updates the model,
做出决定,传达给 model,让 model 更新
504
00:24:30,269 --> 00:24:30,901
something changes in the model,
model 里的一些东西就会发生改变
505
00:24:30,903 --> 00:24:34,438
maybe it changes the UI. Okay, back and forth is really where
可能会改变UI,好的,说来说去
506
00:24:34,440 --> 00:24:37,608
the interface, the logic of our entire app really
我们 app 的整套逻辑生存在 controller 里
507
00:24:37,610 --> 00:24:41,879
lives in our controller, okay? So, this file right here,
okay? 所以,这有一个文件
508
00:24:41,881 --> 00:24:45,950
ViewController.swift, that is our controller, okay?
ViewController.swift,这就是我们的 controller,okay?
509
00:24:45,952 --> 00:24:51,355
Our app only has one MVC, okay? Which is the calculator,
我们的app仅有一个 MVC,okay? 就是这个计算器
510
00:24:51,357 --> 00:24:54,024
MVC and this is it's controller, okay,
MVC和它是这个app的 controller,okay?
511
00:24:54,026 --> 00:24:59,296
the C of MVC. Now, here is your first look at Swift. E,
MVC中的 C,现在,这里是你看到的第一个 swift 程序
512
00:24:59,298 --> 00:25:00,598
we're gonna, I'm gonna explain this little,
我等下将会解释一下这个东西
513
00:25:00,600 --> 00:25:04,168
real briefly. You can see that Xcode has thrown in a couple
非常简单,你可以看到 Xcode 里有一些方法
514
00:25:04,170 --> 00:25:07,571
of methods for me. Everyone knows what a method is.
大家都知道方法是什么
515
00:25:07,573 --> 00:25:09,874
They're so exciting that I'm gonna delete them, okay?
我要删除掉它们,它们显得非常 exciting! okay?
516
00:25:09,876 --> 00:25:12,910
That's because I, I don't wanna distract you with a lot
因为我不想你们被这些代码带走注意力
517
00:25:12,912 --> 00:25:15,513
of random code, so we're gonna keep down just to the minimal
所以我们只保留我们要用的代码
518
00:25:15,515 --> 00:25:16,547
code we're gonna use. We don't,
所以我们只保留我们要用的代码
519
00:25:16,549 --> 00:25:19,783
we will be using those methods later in the course.
我们在后续课程中会用到这些方法
520
00:25:19,785 --> 00:25:21,318
Those are part of what's called the view-controller
这些就是所说的 view-controller 的一部分
521
00:25:21,320 --> 00:25:25,189
lifecycle. But we don't need it for our calculator app. So
但我们的计算器里不需要它
522
00:25:25,191 --> 00:25:26,357
let's look at what we have left here.
所以我们看看留在这里的代码
523
00:25:26,359 --> 00:25:30,461
This import is kind of like an include in other languages or
这个 import 就像其它语言里的 include 一样
524
00:25:30,463 --> 00:25:33,864
import in other languages. But this is not a file,
或是其它语言里的 import,但这不是一个文件
525
00:25:33,866 --> 00:25:37,368
this is a module. And in Swift, you can group a whole
它是一个模块,在 swift 中,你可以把一堆类打包在一起
526
00:25:37,370 --> 00:25:42,306
bunch of classes together. And that's called a module. And
这就可以叫做一个模块
527
00:25:42,308 --> 00:25:45,976
when you import a module all those classes will be visible,
当你导入一个模块,里面所有的类都对你可见
528
00:25:45,978 --> 00:25:49,947
if they're public classes, to whatever is using it. So here,
当然如果它们是 public 类的话,不管谁用都如此
529
00:25:49,949 --> 00:25:53,751
UI kit has all these interface things, buttons, text fields,
所以这个 UIkit 里有所有的的界面,按钮,文本框
530
00:25:53,753 --> 00:25:56,620
all that stuff is in there. And so since we're building
所有的东西都在这个模块里,因为我们要做一个 UI app
531
00:25:56,622 --> 00:25:59,924
a UI app here, a calculator, we're gonna import UIkit, and
一个计算器,我们我们要导入 UIkit
532
00:25:59,926 --> 00:26:02,059
you're almost always gonna import UIkit,
你几乎总是需要导入这个模块
533
00:26:02,061 --> 00:26:04,828
okay? Now in our model, where it's UI independent,
现在在我们的 model 中,UI是独立的
534
00:26:04,830 --> 00:26:07,298
we're gonna probably be importing foundation.
我们也许需要导入 foundation 框架
535
00:26:07,300 --> 00:26:09,967
Foundation is that core services thing, okay?
Foundation 是一个核心框架,okay?
536
00:26:09,969 --> 00:26:11,268
Cuz we might be doing networking and
因为我们可能需要网络编程
537
00:26:11,270 --> 00:26:13,370
databasing in our model, but we're not doing buttons and
或者使用数据库,但我们不需要做一些按钮之类的东西
538
00:26:13,372 --> 00:26:17,374
stuff. So those are the two main things you import, okay.
所以这两个模块你需要导入
539
00:26:17,376 --> 00:26:21,078
Here is the definition, declaration really,
这里是 swift 里对一个类进行定义时看起来的样子
540
00:26:21,080 --> 00:26:24,248
of a class in Swift is what it looks like.
这里是 swift 里对一个类进行定义时看起来的样子
541
00:26:24,250 --> 00:26:28,052
You've got the class keyword. This is the name of the class,
你需要用到 class 关键字,这是这个类的名字
542
00:26:28,054 --> 00:26:30,020
ViewController. Now this is a pretty bad name,
ViewController,一个很差劲的名字
543
00:26:30,022 --> 00:26:33,190
okay, because its very generic, ViewController
因为 ViewController 这个名字不够具体
544
00:26:33,192 --> 00:26:36,160
probably be better called calculator controller, or
我们最好叫它计算器 controller
545
00:26:36,162 --> 00:26:37,394
calculator view controller.
或者计算器 view controller 之类的名字
546
00:26:37,396 --> 00:26:39,363
I'm not gonna show you how to rename that now.
我现在不会教你们怎么重命名
547
00:26:39,365 --> 00:26:42,766
But I will show you next week. But this is the name of our
在下周我会展示给你看,这是我们 controller 类的名字
548
00:26:42,768 --> 00:26:46,937
controller class. This colon UIViewController, means that
这个 UIViewController 意味着
549
00:26:46,939 --> 00:26:50,808
UIViewController is the class our controller inherits from.
我们的类继承于 UIViewController 类
550
00:26:50,810 --> 00:26:53,277
This is object oriented program, we have inheritance.
这是个面向对象程序,需要继承机制
551
00:26:53,279 --> 00:26:56,914
This is the inheritance. Single inheritance in Swift.
这就是一个 swift 里单继承的例子
552
00:26:56,916 --> 00:27:01,485
All controllers, all MVC controllers must inherit from
所有的 controller,所有的 MVC controller 都必须
553
00:27:01,487 --> 00:27:04,521
UIViewController either directly or indirectly from
直接或间接地继承于 UIViewController
554
00:27:04,523 --> 00:27:08,125
UIViewController. Okay, then we have a curly brace here,
Okay,这里我们有了大括号
555
00:27:08,127 --> 00:27:11,695
inside those curly braces goes all of our properties and
所有的属性和方法写在这个括号里
556
00:27:11,697 --> 00:27:14,131
methods. Properties are like instance variables.
比方说一些变量
557
00:27:14,133 --> 00:27:15,299
Everyone knows what an instance variable is,
每个人都该知道什么是实例变量
558
00:27:15,301 --> 00:27:18,636
right? So our property, we call them properties in Swift.
在 swift 中我们称 property 是属性
559
00:27:18,638 --> 00:27:21,438
They're very powerful in Swift, the instance variables.
实例变量在 swift 里面非常有用
560
00:27:21,440 --> 00:27:23,007
They can have behavior associated with them and
它们可以被一些有关联的行为改变
561
00:27:23,009 --> 00:27:26,543
things like that. But all that goes inside these curly
但所有的一切都发生在这对大括号里
562
00:27:26,545 --> 00:27:30,247
braces, okay? So that's your intro to Swift.
好吗? 所以这就是对 swift 的简短介绍
563
00:27:30,249 --> 00:27:33,350
What about this other file over here? Main.storyboard,
这个 Main.storyboard 文件又是怎么回事?
564
00:27:33,352 --> 00:27:39,089
what's that? That's the V for view of our MVC. That's
它是什么呢?就是 MVC 里面的 V
565
00:27:39,091 --> 00:27:42,559
the graphical representation of our calculator.
它代表着我们的计算器的图形界面
566
00:27:42,561 --> 00:27:44,962
So let's click on that, and see what it looks like.
所以我们点它一下, 看看会发生什么
567
00:27:44,964 --> 00:27:49,833
Right now, it's just a big square. Big empty square here.
现在,它只是一个大方块,大的空方块
568
00:27:49,835 --> 00:27:51,935
And I can zoom in and out, so I'll zoom to 50%,
我可以放大和缩小,那我就缩小50%
569
00:27:51,937 --> 00:27:55,572
see? Here's our UI, now, it's kinda interesting,
看,这就是我们的UI, 有点意思对不对
570
00:27:55,574 --> 00:28:02,946
this UI is square, okay? How many iOS products are square?
这个UI是方形的,okay? 有多少iOS程序是方形的?
571
00:28:02,948 --> 00:28:06,717
Zero, right? None of them are, they're all rectangular, okay?
零个,记住了吗?没有一个是方形的,它们都是长方形的
572
00:28:06,719 --> 00:28:09,520
So they're either landscape or portrait, you know, iPads,
而且他们不是横屏的就是竖屏的,你知道的
573
00:28:09,522 --> 00:28:11,388
whatever. None of them are square.
没有一个是方形的
574
00:28:11,390 --> 00:28:16,093
So how are we building our UI in a square UI? Well we're
所以如何在这个方形UI中建立我们自己的UI?
575
00:28:16,095 --> 00:28:19,263
doing that to remind us that we want to build our UI so
时刻谨记,我们要建立自己的UI
576
00:28:19,265 --> 00:28:23,901
that it stretches and expands and moves things around so
它能够伸缩自如,也得能移动UI里的东西
577
00:28:23,903 --> 00:28:26,470
that it will fit in any shape. Okay,
并且能够适应不同的设备
578
00:28:26,472 --> 00:28:29,039
so we're gonna build one UI, it's gonna work on iPad,
所以我们要做一个可以在各种iPad
579
00:28:29,041 --> 00:28:32,209
iPhone, tall iPhones, short iPhones, high resolution ones,
iPhone,长iPhone,短iPhone,高分辨率设备
580
00:28:32,211 --> 00:28:35,713
low resolution, all of them from this one UI. And this is
低分辨率设备,所有的设备只需要一个UI
581
00:28:35,715 --> 00:28:39,516
square to remind us that if this were vertical rectangle,
这个方形提醒我们 如果这是个垂直长方形
582
00:28:39,518 --> 00:28:42,186
probably our UI would look great in portrait, but
或许它更适合竖屏使用
583
00:28:42,188 --> 00:28:42,386
good in landscape. This way, square, it's constantly
这样,对于横屏使用来讲
584
00:28:42,388 --> 00:28:45,089
maybe not so
或许就不那么合适
585
00:28:45,091 --> 00:28:47,591
reminding us that we have to build this flexible UI.
不要忘了我们要做一个足够灵活的UI
586
00:28:47,593 --> 00:28:49,059
Now today, I'm not gonna do that.
今天我并不打算做这件事
587
00:28:49,061 --> 00:28:52,463
I'm gonna build the UI all up in the upper left corner right
我会在这个左上角上建立我们的UI
588
00:28:52,465 --> 00:28:54,531
here so that it looks good in any, or
或许好看
589
00:28:54,533 --> 00:28:58,569
not looks good, but at least shows up in any shape. But
或许不好看,但至少适用于任何设备
590
00:28:58,571 --> 00:29:01,472
on Wednesday, we'll put in the flexibility to make it,
星期三的时候,我们教你们一些方法
00:29:01,474 --> 00:29:06,577
do what it needs to do, okay. So, how do we build this UI?
来给它注入更多的灵活性。所以,我们如何建造这个UI?
592
00:29:06,579 --> 00:29:09,747
We build our entire UI graphically. Okay,
我们用图形拖拽的方式,Okay?
593
00:29:09,749 --> 00:29:12,116
with the mouse. Okay, we do not write code,
用鼠标,我们不需要写任何代码
594
00:29:12,118 --> 00:29:16,420
to say put button here, to put text field there. We just
把按钮放在这里,把文本框放在那里
595
00:29:16,422 --> 00:29:20,557
build it all graphically, and wire it up with our mouse. And
我们只需要用图形的方式来完成,用鼠标做这事
596
00:29:20,559 --> 00:29:22,559
so we're just going to dive right into doing that.
现在我们马上就开始做这个UI
597
00:29:22,561 --> 00:29:26,263
I'm building a calculator. A calculator needs some buttons.
我要做一个计算器,一个计算器需要一些按键
598
00:29:26,265 --> 00:29:27,464
So let's start with a button.
那么我们就从按键开始
599
00:29:27,466 --> 00:29:30,834
I told you that in this utilities window at the bottom
我告诉过你们在 utilities 窗口
600
00:29:30,836 --> 00:29:34,037
there's the palette of things you as an artist, and now we
有一个“调色板”提供给你这个“艺术家”
601
00:29:34,039 --> 00:29:37,975
are UI artists here, are going to build our UI out of. And
现在我们就是UI设计艺术家,将要创造我们的UI
602
00:29:37,977 --> 00:29:41,645
you can see there is whoops. Get that back out of the way.
你可以看见,whoops,把它放到后面去
603
00:29:41,647 --> 00:29:44,848
There we go. Lots and lots of things that we can build our
好了,这里面有很多很多东西我们可以拿来
604
00:29:44,850 --> 00:29:47,918
UI out of here, and we'll talk about actually most of these
构造我们的UI,接下面的几周这些东西
605
00:29:47,920 --> 00:29:50,654
in the ten weeks we have, but we're gonna start about
大多我们都会介绍,但从大概这个四分之一
606
00:29:50,656 --> 00:29:54,258
a quarter of the way down with this one right here button.
的位置这的这个按钮开始
607
00:29:54,260 --> 00:29:56,026
And I'm just gonna pick this up.
我将会点击它
608
00:29:56,028 --> 00:29:59,496
And drag it out, it's my UI. Now when I do that,
把它拖出来,这就是我的UI,当我做这事的时候
609
00:29:59,498 --> 00:30:02,566
notice that these blue lines appear and disappear.
注意下这些出现又消失蓝色的线条
610
00:30:02,568 --> 00:30:06,603
The blue lines help you put something in a theoretical or
这些蓝色线条帮你把东西放在合适的地方
611
00:30:06,605 --> 00:30:10,774
conceptual place. Like this would be putting the button
就像这个给我参考,让我把这个按钮放在
612
00:30:10,776 --> 00:30:13,477
in the conceptual center of my UI. So
我UI框架的中心
613
00:30:13,479 --> 00:30:16,079
if I am portrait it's going to be centered in portrait.
如果我竖屏,它会处于竖屏的中心位置
614
00:30:16,081 --> 00:30:18,882
If I go to landscape it will move over and be in landscape.
如果我横屏,它会移动到横屏的中心位置去
615
00:30:18,884 --> 00:30:22,853
Do you understand what I'm saying? These blue lines allow
你明白我在说什么吗?这些蓝色线条
616
00:30:22,855 --> 00:30:25,088
us to put things in various places and
让我们能把UI元素放在很多地方
617
00:30:25,090 --> 00:30:26,423
this is a big part of how we're going to
对于如何做出一个灵活的UI来说,这些
618
00:30:26,425 --> 00:30:29,159
make this flexible UI. But since I told you I'm not gonna
蓝色线条很关键,不过我已经说了周三再来展示
619
00:30:29,161 --> 00:30:31,094
show you that until Wednesday, we're not gonna do that.
所以今天我不会管它
620
00:30:31,096 --> 00:30:33,764
We're not gonna use the blue lines, okay. But you'll see me
我们不使用这些蓝色线条,okay,等到周三
621
00:30:33,766 --> 00:30:35,766
using the blue lines on Wednesday quite a bit and
我才会告诉你们怎么使用这些线条
622
00:30:35,768 --> 00:30:37,234
you'll want to always use the blue lines.
然后你们就离不开它了
623
00:30:37,236 --> 00:30:41,071
The blue lines are super important to building this
对于做一个足够灵活的UI来讲
624
00:30:41,073 --> 00:30:42,139
flexible adaptable UI.
这些蓝色线条特别重要
625
00:30:42,141 --> 00:30:44,608
All right, so we have our button right here, now we
好了,所有现在我们的按键摆在那了
626
00:30:44,610 --> 00:30:47,711
don't want it to say button on there. Okay, we want it to be
现在我不想要这个空白按键呆在那,我想要
627
00:30:47,713 --> 00:30:50,747
one of the numbers this is gonna be part of our keypad.
它变成我们的一个数字按键
628
00:30:50,749 --> 00:30:53,016
So I'm gonna make this be my five button. And
所以我要把它变成“5”这个按键
629
00:30:53,018 --> 00:30:55,552
to change the text you can just double click on it and
要改变它的文本信息你只需要双击它
630
00:30:55,554 --> 00:30:58,989
change it. So I'm gonna make it 5. You notice it also has
然后改成你要的文本,所以我把它改成“5”,
631
00:30:58,991 --> 00:31:01,825
these little handles around the edge. You can use those to
你可以看见这些边缘上的小框框,你可以用它改变按钮的大小
632
00:31:01,827 --> 00:31:05,229
resize it to whatever you want. So this is computer
这是一门计算机科学课
633
00:31:05,231 --> 00:31:09,466
science class, so we'll make it be 64 by 64, okay? Although
所以我们把它做成64*64大小的,okay?
634
00:31:09,468 --> 00:31:12,636
it really doesn't matter what size I make this, because when
不过我把它做成什么大小真的不重要
635
00:31:12,638 --> 00:31:15,072
it starts being flexible it's gonna start you know,
因为面对不同的环境的时,一个灵活的UI
636
00:31:15,074 --> 00:31:18,141
resizing it to fit depending on what's the environment, but
可以让这些按钮自动变换到合适的尺寸
637
00:31:18,143 --> 00:31:21,345
anyway we'll do that. Now other things I wanna change
不过我还是示范下,现在,这个按钮还有一些需要改变的地方
638
00:31:21,347 --> 00:31:23,480
about this button I don't do directly.
但我不直接做
639
00:31:23,482 --> 00:31:27,517
I need to use this top half of the utilities window here,
我需要用到 utilities 窗口的上半部分
640
00:31:27,519 --> 00:31:30,721
which is the Inspector. Okay, now there's different kinds of
这是一个检测窗口,Okay,现在这有一些
641
00:31:30,723 --> 00:31:33,557
inspectors along the top you'll see here. What you,
不同的种类的检测
642
00:31:33,559 --> 00:31:35,926
what's showing now is the Attributes Inspector.
现在你看到的是属性监测
643
00:31:35,928 --> 00:31:39,129
That, that is the attributes of whatever is selected.
无论我选中了什么属性都会展示在上边
644
00:31:39,131 --> 00:31:41,899
Here is the size inspector. So if I click that, for
这里时一个尺寸监测,所以如果我点击它
645
00:31:41,901 --> 00:31:45,269
example here's my 64 by 64 and I can edit it here as well.
这里就是我的64*64,我们同样也能在这里改变尺寸
646
00:31:45,271 --> 00:31:50,774
I can say 200 by 64, see how this got wide, all right?
我可以改成200*64,看这个怎么变得更宽,all right?
647
00:31:50,776 --> 00:31:53,443
We can go back to 64.
我们也可以改回64
648
00:31:53,445 --> 00:31:55,979
Okay. I'm going to go back to the app.
好了,回到我们的app
649
00:31:55,981 --> 00:31:58,415
And we will talk about these other ones the identity and
我们将会在之后再介绍身份监测,这之类些东西
650
00:31:58,417 --> 00:32:01,752
Spector later. But lets go back here to attributes and
回到我们的属性界面
651
00:32:01,754 --> 00:32:04,154
inspector this is actually object oriented. So
这是面向对象的,所以
652
00:32:04,156 --> 00:32:06,623
here I can set the attributes of a button because that's
所以我能设置这个按钮的属性
653
00:32:06,625 --> 00:32:10,027
what's selected here. And also I can set the attributes of
同样我也能设置这个 controls 属性
654
00:32:10,029 --> 00:32:13,830
a control, which is buttons super class, and
它是按键的父类
655
00:32:13,832 --> 00:32:17,634
then attributes of view, which is controls super class.
再设置 view 里面的属性,它是 controls 的父类
656
00:32:17,636 --> 00:32:20,537
So this goes right up the hierarchy of the inherit
所以它们遵循着继承的体系,一个类继承于另一个
657
00:32:20,539 --> 00:32:23,640
hierarchy, rather, and lets me set various things.
现在让我设置一些东西
658
00:32:23,642 --> 00:32:26,543
For example, I want this five to be bigger, so
比如,我想要这个“5”更大一点
659
00:32:26,545 --> 00:32:30,447
I look here in the buttons attributes and I see font,
所以我看看这个按钮的属性,我看到了“字体”
660
00:32:30,449 --> 00:32:33,850
system font, 15 point. I'm gonna click this and
系统字体,15号,我点击它一下
661
00:32:33,852 --> 00:32:37,154
change this to be 30 point system font. Okay,
把它变成30号的字体,Okay
662
00:32:37,156 --> 00:32:41,291
that looks a lot better. And if I go back and look at this
看起来好多了,如果我再回去看看这个计算器
663
00:32:41,293 --> 00:32:43,961
calculator, see how some of the buttons have a background,
看如何使这个按钮有不同颜色的背景
664
00:32:43,963 --> 00:32:46,763
like grey or orange? I want that too so I'm gonna set
比如绿色或者橘色,所以我设置一下
665
00:32:46,765 --> 00:32:49,599
the background of this to something, so I'm gonna
这个按钮的背景色
666
00:32:49,601 --> 00:32:52,803
go down that turns out to be something that button inherits
所以我往下滑,发现这个按钮的背景
667
00:32:52,805 --> 00:32:55,205
from view, right here background.
继承于 view,就在这里
668
00:32:55,207 --> 00:32:59,042
So I can set it by, you know, using crayons,
所以我可以用不同色的蜡笔来改变它
669
00:32:59,044 --> 00:33:02,045
okay, whatever you can use sliders or I can
okay, 无论你用什么都行
670
00:33:02,047 --> 00:33:05,782
use maybe recently used ones like here's light grey, okay?
我可以用我最近用过的颜色,比如浅灰,okay?
671
00:33:05,784 --> 00:33:08,185
So, I've substituted my nice little background here so,
这样我就更换了我们这个按钮的背景颜色
672
00:33:08,187 --> 00:33:10,721
you know, you can go and edit these things and make them
你们可以编辑这些东西,把按键变成任何你们想要的样子
673
00:33:10,723 --> 00:33:14,858
look however you want, what's interesting about what's going
有趣的地方在这
674
00:33:14,860 --> 00:33:18,261
on here is this...there's no code generation step gonna
这里并没有产生任何代码
675
00:33:18,263 --> 00:33:21,932
happen here. You're actually editing these objects live.
你只是改变了这个按键的样子
676
00:33:21,934 --> 00:33:25,669
Okay. By changing things in the inspector you're sending
通过改变检测窗口里的东西
677
00:33:25,671 --> 00:33:29,172
messages to this button to set the way it is and
你在给按键发送信息,把它设置成你想要的样子
678
00:33:29,174 --> 00:33:33,610
then the Xcode has a way to essentially freeze dry
接着 Xcode 就有办法完成你的请求
679
00:33:33,612 --> 00:33:35,545
all the attributes, and it's object oriented.
改变按钮的属性,这是面向对象
680
00:33:35,547 --> 00:33:37,147
In other words, the objects are participating and
换言之,Xcode 能够把这些操作
681
00:33:37,149 --> 00:33:42,419
freeze drying themselves. And in fact, the whole mechanism
转换成对按键对象的代码的操作,事实上
682
00:33:42,421 --> 00:33:45,856
of this part of Xcode which we call interface builder,
Xcode 的这套机制被我们叫做 ”interface builder”
683
00:33:45,858 --> 00:33:48,925
cuz we're building our user interface, is extensible and
因为我们在为用户做界面,它是可扩展的
684
00:33:48,927 --> 00:33:51,495
you can put your own objects in here, okay.
你也可以在这里加入自己的对象
685
00:33:51,497 --> 00:33:53,563
Your own things they draw, custom drawing or whatever.
UI上所画的一切你都可以自己控制
686
00:33:53,565 --> 00:33:56,633
And we'll show you all about that as the weeks go by.
过几周我就会给你们展示
687
00:33:56,635 --> 00:33:59,603
All right. So, I have one button here, that's good.
好的,现在我有一个按钮在这,非常好
688
00:33:59,605 --> 00:34:01,171
I could even run my app right now.
我甚至已经可以运行这个app了
689
00:34:01,173 --> 00:34:03,673
So how do I run this application? You
那么我如何运行它呢?
690
00:34:03,675 --> 00:34:07,544
go up here to the top, you see where it says iPhone 6s Plus.
看上来,在顶栏这,你能看到这上面写着 “iPhone 6s Plus”
691
00:34:07,546 --> 00:34:10,914
This is actually a list of simulators that you can choose
这实际上是你要选择的模拟器列表
692
00:34:10,916 --> 00:34:14,985
to run your app on, all right? So, you could also run it on
来运行你的app,明白了吗?当然你也可以在一个设备上运行它
693
00:34:14,987 --> 00:34:18,889
a device. If you connected a device and did that team thing
如果你连接了设备然后配好对
694
00:34:18,891 --> 00:34:22,626
I was talking about, you can run it on your device here but
我现在讲的是,你可以在你的设备上运行app
695
00:34:22,628 --> 00:34:23,460
I didn't connect a device so
但我没有连接任何设备
696
00:34:23,462 --> 00:34:25,529
I'm going to run it on an iPhone 6. Okay, so
所以我打算在 iPhone 6 模拟器上运行它
697
00:34:25,531 --> 00:34:30,000
I just pick iPhone 6 and then I just press Play right here,
我只需要选择 iPhone 6 接着按下这个运行键
698
00:34:30,002 --> 00:34:32,969
so we press play it's gonna bring up
好了我们按下运行键,它会产生一个
699
00:34:32,971 --> 00:34:34,571
an iPhone simulator okay and
iPhone模拟器
700
00:34:34,573 --> 00:34:36,206
then the iPhone simulator is gonna run this app.
接着我们的app就会在这个iPhone模拟器上运行
701
00:34:36,208 --> 00:34:39,309
Now this iPhone simulator that you see appearing right here,
现在你看到,这个iPhone模拟器上
702
00:34:39,311 --> 00:34:42,112
it's showing our app. This is more than just our app.
有了我们的app,当然模拟器有的不只是我们的app
703
00:34:42,114 --> 00:34:45,649
This is actually a full simulator of the iPhone.
它可以模拟iPhone的各种行为
704
00:34:45,651 --> 00:34:48,485
And in fact, if I go Hardware, Home, that's
实际上,我点击这个 “Hardware”,“Home”
705
00:34:48,487 --> 00:34:51,221
just like pressing the Home button. You know the little
它就会像真正按在 home 键上一样,就像
706
00:34:51,223 --> 00:34:55,058
Home button on your iPhone? Watch this. Press Home, and
按你iPhone上的 home 键,看,点击home
707
00:34:55,060 --> 00:34:57,227
it goes to here. And I got Settings, Photo,
它就回到主界面,这里就是些设置,相册
708
00:34:57,229 --> 00:35:02,499
Calendar, Safari, other apps, etc. Okay, and here's my app.
日历,Safari,各种app。Okay?这里是我的app
709
00:35:02,501 --> 00:35:05,669
This my assistive app, calculator has no app icon.
这就是我的计算机app,目前还没有一个图标
710
00:35:05,671 --> 00:35:08,038
But, there it is. Now, I can go back. Okay? So
但是看这里,我可以点进去,okay?
711
00:35:08,040 --> 00:35:11,508
it's really simulating the device here. Now,
所以它像一个真正的iPhone
712
00:35:11,510 --> 00:35:16,680
what happens if I touch this button? Nothing. Okay?
那么现在我按下这个按钮会怎样呢?什么都不会发生,okay?
713
00:35:16,682 --> 00:35:19,149
That's because we haven't really taught our
因为当这个按钮按下的时候
714
00:35:19,151 --> 00:35:23,487
calculator app what to do when this button is touched. So
我的计算器并不知道该做些什么,我还没有教它
715
00:35:23,489 --> 00:35:26,089
let's go do that. Now how are we gonna do that?
所以我们现在来教它,怎么做呢?
716
00:35:26,091 --> 00:35:28,959
Remember I told you that the V, the view here is kind of
记得我告诉过你们的, MVC 里的 V
717
00:35:28,961 --> 00:35:31,761
generic UI buttons and sliders and that there's a model which
这个按键是一个原生的按键,我们现在还没有
718
00:35:31,763 --> 00:35:34,931
we don't have yet okay? And then there's the controller.
一个 model 来控制它,okay?这是我们的 controller
719
00:35:34,933 --> 00:35:37,968
So the way that touching on buttons causes anything to
按下按按键发生的所有事情都是这个 controller 来完成的
720
00:35:37,970 --> 00:35:42,038
happen is via the controller. So somehow we have to connect
所以我们得把按钮和 controller 连接起来
721
00:35:42,040 --> 00:35:45,542
this button up to our controller. In fact,
所以我们得把按钮和 controller 连接起来
722
00:35:45,544 --> 00:35:47,544
what we wanna do is, when we touch this button,
所以,我现在需要做的,就是当我们按下这个按钮时
723
00:35:47,546 --> 00:35:51,882
we wanna invoke a method in our controller object, right?
我们想调用 controller 中的一个方法,好吗?
724
00:35:51,884 --> 00:35:53,183
Sounds nice and object oriented.
听起来非常面向对象
725
00:35:53,185 --> 00:35:56,286
That's what we wanna do. So how do we set that up, okay?
那么我们如何完成这件事呢?
726
00:35:56,288 --> 00:35:59,489
We're gonna set that up by getting the controller and
我们需要把 controller 和 view 放在
727
00:35:59,491 --> 00:36:02,592
the view on screen at the same time. Okay, and
同一个屏幕上,okay?
728
00:36:02,594 --> 00:36:05,362
then we're gonna wire it up graphically with the mouse.
然后我们用鼠标把它们连接起来
729
00:36:05,364 --> 00:36:06,630
So let's get both on there at the same time.
现在我们把它们两个放在屏幕上
730
00:36:06,632 --> 00:36:08,865
The way we do that is with this button right here this
怎么做呢?看见编辑栏右边的
731
00:36:08,867 --> 00:36:12,469
little round circle the assistant editor. This brings
小圈圈了吗?点击它可以
732
00:36:12,471 --> 00:36:17,207
up the controller and the view on screen at the same time
把 controller 和 view 放在同一个屏幕上
733
00:36:17,209 --> 00:36:20,277
again I have low resolution so it's all kinda smashed so
这个界面现在很小,那么现在关闭
734
00:36:20,279 --> 00:36:23,580
let's close our navigator and close our utilities. Okay,
左边和的导航栏和右边的工具栏,okay?
735
00:36:23,582 --> 00:36:27,584
and we can share this space however we want. Okay, so
我们可以随便改变,okay?
736
00:36:27,586 --> 00:36:28,251
we have plenty of room for our code.
现在我们有足够的空间展示代码了
737
00:36:28,253 --> 00:36:32,589
And here's our controller. So, now this is gonna seem wacky,
这是我们的 controller,这可能看起来会有一点怪异
738
00:36:32,591 --> 00:36:34,591
but it's actually really, really cool. Okay.
但实际上这非常,非常,酷,okay?
739
00:36:34,593 --> 00:36:37,694
The way that we hook up that button, to call a method in
我们现在把按钮和 controller 里的方法连接起来
740
00:36:37,696 --> 00:36:41,932
our controller, is by holding down the control key, okay.
只需要按住 control 键
741
00:36:41,934 --> 00:36:43,066
I'm gonna hold down the control key.
好,我现在按住了 control 键
742
00:36:43,068 --> 00:36:47,938
And drag, from the button [LAUGH] into my coat, okay.
然后拖动,从按钮拖动到我的代码里来,okay?
743
00:36:47,940 --> 00:36:50,774
Now I know it really seems weird but it's cool, okay.
我知道现在看起来非常怪异,但是也很酷,okay?
744
00:36:50,776 --> 00:36:53,843
So I can put it wherever I want in my class here. And
我可以随意拖动它到我的代码里面
745
00:36:53,845 --> 00:36:57,514
when I let go, it's gonna say okay, what kind of connection
然后当我松开鼠标,需要设置些东西
746
00:36:57,516 --> 00:37:00,817
do you wanna make between this user interface object and
比如我们UI对象和 controller 之间
747
00:37:00,819 --> 00:37:03,086
your controller? And there's really two choices,
需要什么样的连接?这里有2个选择
748
00:37:03,088 --> 00:37:04,521
one is called an outlet which I'm gonna talk
一个是调用 “outlet”,这个我稍后再讲
749
00:37:04,523 --> 00:37:07,290
about in a moment and the other one is called an action.
另一个叫做 “action”
750
00:37:07,292 --> 00:37:10,493
So an action is when you want something that happens in UI
“action” 就是当UI中发生了一些事件后,
751
00:37:10,495 --> 00:37:12,762
to invoke a method in your controller. Okay?
controller 里的方法将被调用,okay?
752
00:37:12,764 --> 00:37:15,999
Exactly what we want here, that's what an action is. So
这就是我们想要的,这就是 “action” 的作用
753
00:37:16,001 --> 00:37:18,702
here it's asking the name, it wants to know the name
那么这里需要一个名字,它需要知道这个方法的名字
754
00:37:18,704 --> 00:37:22,239
of the method. So here I'm touching a digit button,
因为我需要按下一个按钮
755
00:37:22,241 --> 00:37:24,908
so I'm gonna call this touchDigit. Okay.
所以我这在输入 “touchDigit”,okay?
756
00:37:24,910 --> 00:37:26,910
So this is just the name of the method that,
这就是在我的 controller 里
00:37:26,912 --> 00:37:29,479
is gonna be created in my controller.
建立的方法的名字
758
00:37:29,481 --> 00:37:31,514
Notice that my method can have arguments.
注意,我的方法可以有参数
759
00:37:31,516 --> 00:37:32,882
You see down here where it says arguments?
你看下面,这里写着 ”Arguments”
760
00:37:32,884 --> 00:37:37,053
It could have no arguments. Okay. Which is fine, right.
它可以不需要参数, 没有任何问题。
761
00:37:37,055 --> 00:37:38,622
I don't want an argument, that'd be fine.
我不需要参数,这样设置就好了
762
00:37:38,624 --> 00:37:42,225
It can have one argument which is the sender, in other words,
也可以有一个参数,叫做 sender,换言之
763
00:37:42,227 --> 00:37:44,728
the button that is sending me this method, okay.
这个按钮会把我送到这个方法里来,okay?
764
00:37:44,730 --> 00:37:47,297
That's really valuable argument, okay. Especially in
这是个很有价值的参数,okay?
765
00:37:47,299 --> 00:37:50,033
this case because I'm gonna have a whole bunch of buttons.
特别是当我有很多按键的时候
766
00:37:50,035 --> 00:37:52,235
They're each gonna be sending touch digit, and
每一个按键都会发送一个数字
767
00:37:52,237 --> 00:37:53,336
I wanna know which one sent it to me.
我会想知道哪个按键发送给我了信息
768
00:37:53,338 --> 00:37:56,506
So the fact that it's gonna pass itself as an argument to
事实上,按键会把它自己当作参数
769
00:37:56,508 --> 00:37:59,843
this method is great. The only problem is if you see this
传递给方法,有个问题就是
770
00:37:59,845 --> 00:38:03,246
type right here any object. That's going to be the type
你看见这里有一个 “any object”,它的类型就是一个 object
771
00:38:03,248 --> 00:38:05,982
of the object. It's this generic any [LAUGH] object
它可以是右边任意类型的对象
772
00:38:05,984 --> 00:38:08,184
thing right here. We don't want that. Okay?
我们不需要,okay?
773
00:38:08,186 --> 00:38:12,522
So when you're doing your homework don't miss this step.
当你做家庭作业的时候,不要错过这一步
774
00:38:12,524 --> 00:38:17,527
Okay? Change this from any object to UI button.
okay?把它从 “any object” 改成 “UIButton”
775
00:38:17,529 --> 00:38:20,630
Because of course the thing sending us this message is UI
因为发送给我们消息的显然是一个 UIbutton
776
00:38:20,632 --> 00:38:23,833
button. It's unfortunate that this is not the default but
不幸的是它不是默认设置
777
00:38:23,835 --> 00:38:27,304
you must do this. Okay? So, this I wanna click Connect,
所以你必须自己搞定,okay?现在,我来点击 “Connect”
778
00:38:27,306 --> 00:38:30,440
this is just what event is causing this a touch on
这是按下按键后会触发的事件
779
00:38:30,442 --> 00:38:34,277
the button that goes up inside the buttons bounds. Okay?
按键会重新“弹起来”
780
00:38:34,279 --> 00:38:36,046
So I'm gonna hit connect right here and
所以我现在点击一下
781
00:38:36,048 --> 00:38:37,614
it's gonna write the code for a method.
它会写下这个方法的代码
782
00:38:37,616 --> 00:38:40,917
You're gonna see your first Swift method here we go. Okay?
你会在这里看到第一个 swift 方法,okay?
783
00:38:40,919 --> 00:38:44,354
This is your first Swift method. Now this is actually
这是你的第一个 swift 方法
784
00:38:44,356 --> 00:38:46,923
not part of Swift. Okay, it's not part of this method.
这里不是 swift 的一部分,okay?它不是方法的一部分
785
00:38:46,925 --> 00:38:49,659
This is something Xcode puts in here. So that this little
这是 Xcode 放在这里的东西
786
00:38:49,661 --> 00:38:52,996
round circle appears. You see that round circle right there?
所以这里有一个小圈圈,你看了这个小圈圈了吗?
787
00:38:52,998 --> 00:38:54,764
If I mouse over that round circle,
如果我把鼠标放上去
788
00:38:54,766 --> 00:38:59,502
you see how it shows me what thing in my view is hooked up
你可以看见,方法和UI里的东西是怎样连接的
789
00:38:59,504 --> 00:39:02,072
to this method you see? Okay so that's what this is so
看见了吗? 这就是它的作用
790
00:39:02,074 --> 00:39:06,242
that's really not part of the Swift method. Okay so this is
它不是 swift 方法的一部分
791
00:39:06,244 --> 00:39:10,547
all the Swift method. Okay so what is Swift method syntax?
这才是 swift 里的方法,okay?swift 的方法语法是怎样的呢?
792
00:39:10,549 --> 00:39:14,184
Func, okay, func means this is a function on a class,
先是一个func,func 代表的是这个类里的一个函数
793
00:39:14,186 --> 00:39:17,420
a method is essentially a function on a class, right?
方法本质上就是类里的函数,好吗?
794
00:39:17,422 --> 00:39:21,858
I suppose this could be called Method. Like the breaking bad
这可以被叫做方法。
795
00:39:21,860 --> 00:39:25,261
version of what you'll call meth maybe. But func is good,
和《绝命毒师》里的冰毒发音类似,但是 func 是好的
796
00:39:25,263 --> 00:39:28,631
func is good because you can have global funcs also.
func 还好在你还可以写全局函数
797
00:39:28,633 --> 00:39:32,335
Okay, you can have the same syntax outside of a class,
使用同样的语法,你可以在类的外面写一个函数
798
00:39:32,337 --> 00:39:34,871
description outside of these braces.
定义在这些大括号的外面
799
00:39:34,873 --> 00:39:36,306
And it will be a global function.
这样它就是个全局函数
800
00:39:36,308 --> 00:39:37,540
And we're gonna see some of those.
我们之后会见到一些全局函数
801
00:39:37,542 --> 00:39:39,042
Like square root is a function I'm gonna
就像平方根函数
802
00:39:39,044 --> 00:39:42,345
use when I add square root to my calculator. All right, so
比如,当我在计算器里加上根号的时候
803
00:39:42,347 --> 00:39:44,748
that's the key words just like class is the key word for
“class”,这是创建一个类的的关键字
804
00:39:44,750 --> 00:39:47,684
creating a class. Func is the key word for adding a method.
“func” 是添加一个方法的关键字
805
00:39:47,686 --> 00:39:52,155
Touch digit is the name, obviously of this method.
“touchDight” 是它的名字,显然它是一个方法
806
00:39:52,157 --> 00:39:54,691
Parentheses has the arguments, okay?
括号内可以有参数
807
00:39:54,693 --> 00:39:58,561
We only have one argument called sender of type
我们只有一个叫做 “sender” 的参数
808
00:39:58,563 --> 00:40:02,599
UIButton, so that's what : UIButton means.
类型是 UIButton, 这就是 UIButton 的意思
809
00:40:02,601 --> 00:40:06,102
Any time you're defining a type of something in Swift,
当你在swift定义一个东西的类型的时候
810
00:40:06,104 --> 00:40:08,838
colon and the type after the name of the thing.
使用冒号,后面跟着它需要的类型的名字
811
00:40:08,840 --> 00:40:10,807
Okay, and that's true for arguments to methods,
方法中的参数也是这样
812
00:40:10,809 --> 00:40:12,609
it's also true for local variables, everything.
局部变量,所有东西,都是这样
813
00:40:12,611 --> 00:40:15,812
Colon and the type, okay? Now if we had more arguments,
冒号,然后类型名字,okay?现在如果我们有多个参数
814
00:40:15,814 --> 00:40:20,617
they would just be separated by comma, like otherArgument
它们应该被逗号分隔开,如同这个 otherArgument
815
00:40:20,619 --> 00:40:24,654
might be an Int, okay, so I put an Int there.
它可以是一个Int, okay?所以我写一个 Int 在这
816
00:40:24,656 --> 00:40:26,990
So, this is another argument, second argument here, and
所以这是第二个参数
817
00:40:26,992 --> 00:40:29,926
I can have as many as I want, and they each have to have
我需要多少个参数都可以,每一个都需要一个参数名
818
00:40:29,928 --> 00:40:34,030
a key word, which describes, what they are. If this had
来表示它们是干什么的
819
00:40:34,032 --> 00:40:39,068
a return value, it looks like this. Returns String. Okay, or
如果这个方法有返回值,就这样写,返回一个 String
820
00:40:39,070 --> 00:40:41,938
returns Double. Okay, something like that.
或者返回一个 Double, okay?就像这样
821
00:40:41,940 --> 00:40:45,809
That's how a return looks like, okay. Now I'm gonna
return 就这样写,okay?
822
00:40:45,811 --> 00:40:48,445
take a timeout here from writing our calculator and
现在我要开始写我们的计算器了
823
00:40:48,447 --> 00:40:50,180
just show you what its like to call a method.
教你如何调用一个方法
824
00:40:50,182 --> 00:40:52,916
Okay? This is we're declaring the method. What if I wanted
Okay? 我们在这里声明了一个方法
825
00:40:52,918 --> 00:40:55,919
to call this method on myself? Okay it would be recursive
如果我自己想调用这个方法呢?那么就会出现递归
826
00:40:55,921 --> 00:40:57,887
here because I'm inside touched as your butt.
因为这是自己调用自己
827
00:40:57,889 --> 00:41:00,089
I just wanna show you the syntax for calling okay?
我只是展示一下调用方法的语法
828
00:41:00,091 --> 00:41:02,325
So if I want to send something to myself.
如果我想给我自己发送点信息
829
00:41:02,327 --> 00:41:05,495
I use the key word self, and then to send a message it's
我用关键字 “self”,然后发送信息
830
00:41:05,497 --> 00:41:08,298
just like Java you press dot, okay?
如果JAVA一样,按下“.”,okay?
831
00:41:08,300 --> 00:41:10,333
The thing you want to send it to dot and
你想发送什么,按下一个“.”
832
00:41:10,335 --> 00:41:14,504
then you type the name of the method or of the property if
然后输入你需要调用的方法或属性
833
00:41:14,506 --> 00:41:17,207
you wanna access an instance variable exactly the same.
如果你想调用一个实例变量,也是相同的办法
834
00:41:17,209 --> 00:41:17,740
The name of the property or
对某个对象,你想调用的属性的名字
835
00:41:17,742 --> 00:41:20,043
the method that you wanna access in that object.
或者方法的名字
836
00:41:20,045 --> 00:41:22,645
So here I want to do touchDigit and you're going
在这我想调用 touchDigit
837
00:41:22,647 --> 00:41:25,014
to notice here that Xcode is helping me a lot as I type,
你会发现 Xcode 会帮助我输入
838
00:41:25,016 --> 00:41:27,817
it's giving me lists of things that I can choose that
给我一张列表供我选择
839
00:41:27,819 --> 00:41:30,987
are likely. Here touchDigit, it's putting that on the top
这里,touchDigit,在最顶上
840
00:41:30,989 --> 00:41:33,356
cuz that's the only method that is in my class.
因为这是这个类中的唯一的方法
841
00:41:33,358 --> 00:41:36,092
I inherit a lot of methods from my superclasses but
我从父类中继承了非常多的方法
842
00:41:36,094 --> 00:41:38,862
this is the only one in my class and in fact if I just
但这是这个类中的唯一的方法,事实上
843
00:41:38,864 --> 00:41:41,531
press Tab, it starts filling it out. I'm not even,
如果我按下 Tab,它会自动补全,不需要全部手打
844
00:41:41,533 --> 00:41:44,167
I don't even have to type it okay, cuz it knows that's what
因为 Xcode 知道我们想要什么
845
00:41:44,169 --> 00:41:48,771
I want. Now, it takes two argument. The first argument
现在它有两个参数
846
00:41:48,773 --> 00:41:51,508
you just put it in. So the first argument was a button so
这里的第一个参数是一个 button, 所以
847
00:41:51,510 --> 00:41:55,078
maybe, you know, some button or something like that. Okay,
我输入 someButton 之类的东西,okay
848
00:41:55,080 --> 00:41:56,446
some variable that would have to be a button.
某些变量会是一个 button
849
00:41:56,448 --> 00:41:59,115
I don't have one but that's what it would be.
现在我没有这个变量,不过以后会有的
850
00:41:59,117 --> 00:42:00,783
The second argument here, okay, and
这里的第二个参数
851
00:42:00,785 --> 00:42:03,753
I'm just hitting Tab to go to each argument, is an Int,
我按了 Tab 键来跳到每个参数,它是一个 Int
852
00:42:03,755 --> 00:42:06,256
so let's say I put 5, okay, so this is how I would
那么我放一个5在这里,okay,我就打算这么做了
853
00:42:06,258 --> 00:42:08,958
call this thing. Now what's interesting about this is,
现在,有趣的事
854
00:42:08,960 --> 00:42:12,662
notice that this keyword is actually included,
注意这个关键字也出现了
855
00:42:12,664 --> 00:42:15,532
in other languages this would look like this. All right?
在其它语言中会是这个样子,All right?
856
00:42:15,534 --> 00:42:17,934
You would just have the two arguments, sender and
你只需要两个参数,sender 和 argument
857
00:42:17,936 --> 00:42:22,272
argument, but here in Swift, sorry, in Swift,
但是在 swift 里
858
00:42:22,274 --> 00:42:27,844
we put the explicit, name of the argument here for
需要非常详尽,参数名也需要写出来
859
00:42:27,846 --> 00:42:30,680
readability and clarity except for we don't do it with
这是为了可读,清晰,但我们不写第一个参数的参数名
860
00:42:30,682 --> 00:42:35,151
the first one. Okay? The first one we don't do.
okay?不要写第一个参数的参数名
861
00:42:35,153 --> 00:42:37,620
That's because usually the first one is implicit in
因为 swift 里通常都这样做
862
00:42:37,622 --> 00:42:41,758
the name, okay? But, that's what, now there is a way to
okay?非得写出这个参数名的话
863
00:42:41,760 --> 00:42:44,394
make it so you have to do the first one, for but generally
也是有一个方法的,但是最好别这样做
864
00:42:44,396 --> 00:42:46,729
we don't. In other words we don't type sender this.
换言之不要把 sender 写出来
865
00:42:46,731 --> 00:42:48,598
And in fact, it's wrong to type sender that.
事实上,这样做是错误的
866
00:42:48,600 --> 00:42:50,967
>> Would that be an error?
这样会造成错误吗?
867
00:42:50,969 --> 00:42:51,000
>> It would be an error.
是的,这会造成错误
868
00:42:51,002 --> 00:42:55,705
It would not compile. Okay? So that's the syntax for sending.
它不会编译,okay?这就是发送一个消息的语法了
869
00:42:55,707 --> 00:42:58,041
And you'll have to get used to putting these in for
你们会逐渐习惯这样的写法
870
00:42:58,043 --> 00:43:00,944
all the subsequent arguments besides the first.
除了第一个,其他的参数都要写参数名
871
00:43:00,946 --> 00:43:04,447
Okay. So let's gret rid of all this stuff we added. So
okay,删掉这些我们加上的东西
872
00:43:04,449 --> 00:43:08,084
here's our touchDigit. let's just have touchDigit
这是我们的 touchDigit,现在让它
873
00:43:08,086 --> 00:43:10,587
print something out to the console. Okay?
打印个什么,输出到控制台里,okay?
874
00:43:10,589 --> 00:43:11,621
Great thing to know for debugging, and
这是 debug 的好方法,什么的
875
00:43:11,623 --> 00:43:14,958
stuff like that. You do that with the method print. And
在方法里写上打印语句
876
00:43:14,960 --> 00:43:17,026
I'm just going to say, print touchDigit.
我打印一个 “touchDigit”
877
00:43:17,028 --> 00:43:19,562
So it's gonna print touchDigit after the console.
它会在控制台里打印一个 “touchDigit” 出来
878
00:43:19,564 --> 00:43:22,899
Notice no semicolons on the end of lines or anything like
注意,句尾没有分号或其他东西
879
00:43:22,901 --> 00:43:27,270
that. In Swift the carriage return is the end of line.
swift 里,回车表示这一行的结束
880
00:43:27,272 --> 00:43:29,072
And if you wanna put two statements on the same line
如果你想把两条语句打在一行里
881
00:43:29,074 --> 00:43:30,440
you can use a semicolon in between, but
在语句中间加上分号就行了
882
00:43:30,442 --> 00:43:34,077
generally the carriage return is the end of line, okay? So
但一般情况,我们用回车作为语句结尾,okay?
883
00:43:34,079 --> 00:43:37,046
we print touch digit here. Let's go ahead and run and
所以把 print 语句写在这
884
00:43:37,048 --> 00:43:45,188
see if that works. Changes our simulator.
现在运行一下看它是否能正常工作,改变下我们的模拟器
885
00:43:45,190 --> 00:43:45,955
Now, where is the console?
现在,控制台在哪里?
886
00:43:45,957 --> 00:43:49,258
It's going to appear magically at the bottom here as soon as
当有东西出现在控制台里的时候
887
00:43:49,260 --> 00:43:51,060
something appears on the console. So, here we go.
它会神器地从底部出现,所以,here we go
888
00:43:51,062 --> 00:43:55,164
Ready? 5, there it is. There's the console in here. 5 again,
Ready? 5, 它出来了,这就是控制台,再按一下,5
889
00:43:55,166 --> 00:43:57,800
okay we got two of them. Okay, we press a whole bunch of
okay,我们现在有2个5了
890
00:43:57,802 --> 00:44:00,403
times, and get a whole bunch of them. Go back here, scroll
我们按几次5它就出现几次,回到这里来
891
00:44:00,405 --> 00:44:04,507
around. This bottom area has the console on the right,
滚动一下,底部区域里,右边是控制台
892
00:44:04,509 --> 00:44:07,944
and your debugger output on the left. This is where when
左边是调试器,debug 的时候你会用到
893
00:44:07,946 --> 00:44:09,712
we start to do debugging you'll see it on the left.
它会出现在左边
894
00:44:09,714 --> 00:44:13,416
You can hide one or the other, like we could hide our
也可以隐藏它们,我们可以隐藏调试器
895
00:44:13,418 --> 00:44:16,252
debugger and just see our console here. Okay, so
只留下我们的控制台,okay?
896
00:44:16,254 --> 00:44:19,222
this is cool, this is great. Except for we're gonna
这很酷,非常棒
897
00:44:19,224 --> 00:44:23,292
have a lot of buttons not just this one button, this 5.
但是如果有一堆按键而不是一个呢?这是个5
898
00:44:23,294 --> 00:44:26,195
Okay, so let's go back to our UI. By the way you can hide
现在回到我们的UI,顺便说一下
899
00:44:26,197 --> 00:44:29,565
this thing by either dragging it down or pressing this
你可以通过拖拽来隐藏它们,或者点击这个按钮
900
00:44:29,567 --> 00:44:32,935
button up here. Okay, just like you hide this thing
就像你点击这个隐藏这个
901
00:44:32,937 --> 00:44:36,105
with that you hide with this with that. All right, so we'll
就像你点击这个隐藏这个
902
00:44:36,107 --> 00:44:39,509
stop. Now I wanna make a whole keypad worth of buttons here.
停止运行,现在我想做出一个完整的键盘
903
00:44:39,511 --> 00:44:41,611
Instead of just having a 5, I want a whole bunch.
不只是一个5,而是所有数字
904
00:44:41,613 --> 00:44:42,979
So I'm gonna do that with copy and paste.
我通过复制粘贴来完成
905
00:44:42,981 --> 00:44:47,550
Copy, paste okay? There we go, paste another one here.
复制,粘贴,okay?好了,再粘贴一个
906
00:44:47,552 --> 00:44:51,387
Okay? Maybe this is our 4, 5 and 6.
okay?这个是我们的4,5,6
907
00:44:51,389 --> 00:44:55,525
I can actually select all three and copy paste. Okay.
我可以选中这三个,再复制和粘贴
908
00:44:55,527 --> 00:45:00,463
1, 2, and 3 and may you paste again.
1,2,3,然后再粘贴一次
909
00:45:00,465 --> 00:45:05,635
Go up here. 7, 8, 9 and we'll copy and
移到上面来,7,8,9,再复制
910
00:45:05,637 --> 00:45:11,407
paste one more to make our 0. Okay?
粘贴出我们的0,okay?
911
00:45:11,409 --> 00:45:15,511
So here's our nice little keypad right here. And,
这就是我们的小键盘了
912
00:45:15,513 --> 00:45:20,583
now we can run. And we'll notice
现在我们运行它
913
00:45:20,585 --> 00:45:24,287
that all of these buttons are sending touch digit. You see?
注意所有的按键都会发送信息给 touchDigit
914
00:45:24,289 --> 00:45:26,055
Okay, so they're all sending touch digit.
他们都会发送信息给 touchDigit
915
00:45:26,057 --> 00:45:28,991
Which is good, that's kind of nice. We can also tell they're
很棒,很不错,我们还能通过这个小圈圈
916
00:45:28,993 --> 00:45:30,259
all sending it with this little circle.
看到它们连接在一起
917
00:45:30,261 --> 00:45:32,395
Watch what happens when I mouse over. You see?
看看我移开鼠标会发生什么?看见了吗?
918
00:45:32,397 --> 00:45:35,898
It's selecting them all to say they're all sending it. Okay.
选中了全部按键,他们都能发送信息
919
00:45:35,900 --> 00:45:40,169
But, we really wanna know which one is sending it. Okay.
但,我们需要知道是那一个按键发送了信息,okay
920
00:45:40,171 --> 00:45:42,472
So we're gonna have to look at this sender argument,
所以我们需要看看这个 sender 参数
921
00:45:42,474 --> 00:45:45,775
to see which button is sending us this touch digit.
看看是哪个按键发给它了信息
922
00:45:45,777 --> 00:45:48,511
Okay. So I'm gonna create a local variable here.
okay,我打算建立一个局部变量
923
00:45:48,513 --> 00:45:51,714
This is how you create a local variable, var. The name,
这就是如何建立一个局部变量,var,然后是变量名
924
00:45:51,716 --> 00:45:55,451
which I'm gonna call digit. And I'm gonna set it equal to,
我打算叫它 digit,我打算把它设置成
925
00:45:55,453 --> 00:45:59,522
whatever I get from asking the sender, what its title is.
给被按下的按键的数字,按键的名字
926
00:45:59,524 --> 00:46:02,658
Okay? Now, how do I do that? Well, we know that we
okay?怎么做呢? 我们知道
927
00:46:02,660 --> 00:46:06,229
send a message to something by doing the object, right,
得到这个名字肯定和sender这个对象有关,对吗?
928
00:46:06,231 --> 00:46:11,434
sender and a dot, okay? Unfortunately, button has, mm,
sender 然后一个“.”,okay?不幸的是
929
00:46:11,436 --> 00:46:15,471
quite a few methods, okay? I mean probably hundreds, okay?
按键拥有很多方法,okay?可能几百个,okay?
930
00:46:15,473 --> 00:46:18,808
Because it's inheriting things from control and view and
因为它继承于 control,view
931
00:46:18,810 --> 00:46:18,875
stuff like that.
类似的东西
932
00:46:18,877 --> 00:46:21,978
So it's a pretty much, a bummer trying to find out how
所以找到按键名似乎还是很困难
933
00:46:21,980 --> 00:46:25,681
to know what to give this. So we need documentation here.
所以我们在这种时候
934
00:46:25,683 --> 00:46:28,184
We need some help, okay. So the way to do that
就需要文档的帮助,okay?怎么做呢?
935
00:46:28,186 --> 00:46:30,219
is you're gonna hold down the option key, okay.
按下你的键盘上的 option 键,okay?
936
00:46:30,221 --> 00:46:32,321
I already showed you Control is an important key for
我已经告诉你了 control 键很重要
937
00:46:32,323 --> 00:46:35,792
dragging between your view and your thing. Now Option is
特别是对UI拖拽来说,现在 option 键
938
00:46:35,794 --> 00:46:38,194
an important key. And when you hold down Option,
也非常重要,当你按下 option 键
939
00:46:38,196 --> 00:46:40,029
look what happens when you mouse over things.
再移动鼠标,看看会发生什么
940
00:46:40,031 --> 00:46:43,900
They get this dash blue underline going. And what that
下方出现了蓝色虚线,这意味着
941
00:46:43,902 --> 00:46:46,536
means is if you click on them Xcode's gonna tell you
如果你点击了它们,Xcode 会告诉你
942
00:46:46,538 --> 00:46:49,705
as much as it can about that thing. So let's click on
关于它们的一切信息,所以我们在
943
00:46:49,707 --> 00:46:53,309
UIButton and when we do we get this little window that
UIButton 这点击,我们得到了一个小窗口
944
00:46:53,311 --> 00:46:57,547
has a complete description of UIButton in here. And really
它拥有关于 UIButton 完整信息,非常重要的是
945
00:46:57,549 --> 00:47:01,150
importantly down at the bottom it has a reference link.
在最底下,有一个引用链接
946
00:47:01,152 --> 00:47:03,686
See this reference link? And if I click this, this is gonna
看见这个引用链接了吗?如果我点它一下
947
00:47:03,688 --> 00:47:07,757
take me to the documentation for UIButton. So there we go.
它会把我带到 UIButton 的文档里,所以我们点击它
948
00:47:07,759 --> 00:47:10,226
Here it is, UIButton. Here's all the methods and
这就是了,UIButton,这是它所有的方法
949
00:47:10,228 --> 00:47:14,530
properties in UIButton. Here's all the description of it.
所有的属性,所有的关于它的描述
950
00:47:14,532 --> 00:47:19,769
And there is all kinds of text here that describes all
各种各样的文本来告诉你这些方法
951
00:47:19,771 --> 00:47:22,171
the methods and how they work, okay.
是如何工作的,okay?
952
00:47:22,173 --> 00:47:26,375
Now, I need to find something about the title so
现在,我要找出关于 ”title”(标题) 的一些信息
953
00:47:26,377 --> 00:47:29,846
I could search here by just doing Cmd+f to search, and
我可以按下Cmd+f进行搜索
954
00:47:29,848 --> 00:47:32,982
I could search for title but unfortunately the title
我可以搜索一下 “title”,不幸的是
955
00:47:32,984 --> 00:47:37,286
is mentioned a lot, see? So that's no good. So
“title” 在文档里被提及的太多了,很不好
956
00:47:37,288 --> 00:47:39,522
I can go over here to the summary of all the methods and
然后我可以看下 summary 这里,它有所有的方法和属性
957
00:47:39,524 --> 00:47:42,792
properties. We could look here. Getting dimensions,
我们可以看过来,看看它包括些什么
958
00:47:42,794 --> 00:47:46,128
no I don't wanna do that. Configuring edge insets we
不,我不想这样做,我们甚至还不知道什么是
959
00:47:46,130 --> 00:47:48,998
don't even know what that is yet. Getting the current state
“Configuring edge insets”,“获取目前状态”这个听起来
960
00:47:49,000 --> 00:47:50,700
that sounds pretty good, getting the current state.
还不错,获取目前状态
961
00:47:50,702 --> 00:47:53,302
Let's see what we've got, button type okay here's how I
看看这里有什么?按键类型,okay,它在这
962
00:47:53,304 --> 00:47:54,670
can get the button's type that's good.
我现在可以得到按键的类型了,不错
963
00:47:54,672 --> 00:47:58,741
Current title, the current title displayed on the button,
“Current title”,按键上的名称
964
00:47:58,743 --> 00:48:00,643
victory, that's what we want. Okay,
胜利了!这就是我们要的,okay?
965
00:48:00,645 --> 00:48:03,012
now when we find something in the documentation we want,
现在,当我们需要在文档中找东西时
966
00:48:03,014 --> 00:48:06,115
you're gonna see that there is a Swift explanation
你可以看见这里有关于swift解释
967
00:48:06,117 --> 00:48:08,918
of it, and and objective C explanation. So we're always
或者关于 Objective C 的解释
968
00:48:08,920 --> 00:48:12,021
going to be looking at the Swift version in this class.
我们看看这个类的解释的 swift 版本
969
00:48:12,357 --> 00:48:13,322
So what is this thing, okay?
这是个什么?
970
00:48:13,324 --> 00:48:16,926
This is not a method because it would say func right here.
它不是一个方法,因为方法前面有一个 “func”
971
00:48:16,928 --> 00:48:19,595
If this were a method on UIButton you would have func.
如果它是 UIButton 里的方法,你会看到一个 ”func”
972
00:48:19,597 --> 00:48:22,265
Instead it has var, that means it's a property.
但这有一个 “var”,意味着它是一个属性
973
00:48:22,267 --> 00:48:25,034
So a property's like an instance variable, okay?
属性就像一个实例变量,okay?
974
00:48:25,036 --> 00:48:27,436
Now this is an interesting property because it's got this
这是个有趣的属性
975
00:48:27,438 --> 00:48:31,908
little terminology on the end, which means it's read only. So
因为在末尾有个这个东西,意味着它是只读的
976
00:48:31,910 --> 00:48:34,877
we can't set the button's title here with current title.
所以我们不能通过它来设置这个按键的名称
977
00:48:34,879 --> 00:48:37,413
Turns out that's because there's another method up here
但是呢还有一个方法
978
00:48:37,415 --> 00:48:40,750
called setTitle forState for setting the button's title.
叫 “setTitle forState“ 来设置按键的名称
979
00:48:40,752 --> 00:48:41,884
Because when you set the button's title,
因为当你设置一个按键名称的时候
980
00:48:41,886 --> 00:48:44,320
you wanna set the title for what state it's in,
你需要知道这个按键的状态
981
00:48:44,322 --> 00:48:46,589
highlighted, disabled, whatever. But luckily there's
高亮?禁用?无所谓
982
00:48:46,591 --> 00:48:50,493
a current title here that just gets you the current title.
幸运的是这里有一个 “current title” 让你获取当前按键名称
983
00:48:50,862 --> 00:48:52,995
In whatever state the button is currently in.
不管这个按键当前是什么状态的
984
00:48:52,997 --> 00:48:55,998
So, here is, so that's why it says var instead of func.
这就是这为什么是 var 而不是 func
985
00:48:56,000 --> 00:48:59,635
Here's the name of the property, currentTitle.
这里是属性的名字,currentTitle
986
00:48:59,637 --> 00:49:00,436
Here is the type.
这里是它的类型
987
00:49:00,438 --> 00:49:03,906
I promised you that it was always colon and type. Now,
我向你保证类型前总是有一个冒号
988
00:49:03,908 --> 00:49:09,011
the type of this is string? >> [LAUGH]
现在这个类型是 String??
989
00:49:09,013 --> 00:49:11,614
>> Wha? Maybe. Okay? We're
啥?可能是吧,okay
990
00:49:11,616 --> 00:49:14,984
gonna suspend our disbelief here because that question
现在刹住你们的疑问,因为这个问号
991
00:49:14,986 --> 00:49:18,721
mark actually means something. We're gonna kinda hope that
意味着些什么,我们希望这是一个 string
992
00:49:18,723 --> 00:49:22,158
this is a string, but it's not going to be. But that's okay.
但它不是,不过没有关系
993
00:49:22,160 --> 00:49:24,860
So we're gonna try and assume it is. All right? So anyway,
我们就假设它是,好吗?
994
00:49:24,862 --> 00:49:26,495
this is how we look things up in the documentation
无论如何,这就在文档里如何找东西的办法
995
00:49:26,497 --> 00:49:30,199
to see the way it works. Now, there's another way to do it,
现在,也有其他办法来做这事
996
00:49:30,201 --> 00:49:32,468
okay, which is even cooler. I type this dot.
更酷一点,我输入这个”.”
997
00:49:32,470 --> 00:49:35,538
I want the title so I'm just going to start typing title.
我想使用”title”,所以我就继续输入”title”
998
00:49:35,540 --> 00:49:37,840
Okay I don't know there is a method called title,
okay,我不知道这里是否有一个方法,名字是title
999
00:49:37,842 --> 00:49:40,843
in fact there isn't. But as soon as I started typing title
事实上是没有的,但只要我输入完之后
1000
00:49:40,845 --> 00:49:43,346
it shows me all the methods that start with title.
它会告诉我所有以 “title” 开头的方法的名字
1001
00:49:43,348 --> 00:49:48,617
All the methods that include title in the name. See?
所有名字里包含了 title 的方法,看见了吗?
1002
00:49:48,753 --> 00:49:54,156
And all the methods that have T-I-T-L-E in the order. Okay,
还有所有含有 T-I-T-L-E 这个字母顺序的方法
1003
00:49:54,158 --> 00:49:57,093
and it gives it to you in the order you might expect right?
这会按照你期望的顺序呈现
1004
00:49:57,095 --> 00:50:00,363
Title first and then. So this is kinda helping,
含有 title 这个词的优先,别的在后面,这提供了一种帮助
1005
00:50:00,365 --> 00:50:00,997
you can search basically.
你可以这么搜索
1006
00:50:00,999 --> 00:50:03,599
And when you do this you would quickly see current title,
当你这么做的时候,你快速浏览到 currentTitle
1007
00:50:03,601 --> 00:50:06,769
that looks like what I want. And you can double click here,
这看起来像是我想要的。你就在这双击
1008
00:50:06,771 --> 00:50:09,138
actually, and it will put in there. Okay? So
这样就会出现在这里。
1009
00:50:09,140 --> 00:50:12,942
there's our current title. Now instead of printing touch
这就是我们要的 currentTitle
1010
00:50:12,944 --> 00:50:17,313
digit we basically wanna print which one, which digit was,
现在我们想打印具体按到的某个数字,而不是 touchDigit
1011
00:50:17,315 --> 00:50:22,485
touched here. So I'm gonna say touched, we could say percent,
所以这里要换成
1012
00:50:22,487 --> 00:50:25,254
s digit. Comma digit.
“touched %s digit”, digit
1013
00:50:25,256 --> 00:50:27,790
This is what it would look like in C. For example,
这就看起来像是C语言
1014
00:50:27,792 --> 00:50:31,327
is everyone familiar with this print F like format?
所以你们对 printf 熟悉吗
1015
00:50:31,329 --> 00:50:33,696
Okay good, cuz you can't do it in Swift.
很好,但你不能在 Swift 里这样做
1016
00:50:33,698 --> 00:50:34,096
Okay. >> [LAUGH]
1017
00:50:34,098 --> 00:50:35,164
>> We don't do that in Swift.
我们在 Swift 里不这么做
1018
00:50:35,166 --> 00:50:37,633
Instead, we do backslash, open parentheses,
我们用反斜杠、括号代替
1019
00:50:37,635 --> 00:50:40,903
close parentheses. And then we can put anything we want in
然后里面放任何我们想放的
1020
00:50:40,905 --> 00:50:43,806
here. Okay clean digit. And we don't need this out here on
删掉 digit,引号外不需要它
1021
00:50:43,808 --> 00:50:47,510
the side. Okay, so this will be invaluated converted to
所以这会被转换成 string 类型
1022
00:50:47,512 --> 00:50:50,279
a string. Some things can't be converted to strings but
有的不能被转换,但大多数可以
1023
00:50:50,281 --> 00:50:53,215
most things can, so you put that in there. Okay,
所以你可以把它放在里面
1024
00:50:53,217 --> 00:50:56,652
if you can't you'll get a warning, okay? All right, so
如果不可以,你会被警告
1025
00:50:56,654 --> 00:50:58,054
that's how we do that. So, let's go ahead and
所以我们就这么做了
1026
00:50:58,056 --> 00:51:01,357
see what's going on. But now maybe we should start paying
继续看看接下来会发生什么
1027
00:51:01,359 --> 00:51:02,024
attention to our warnings.
我想现在应该开始注意一下这个警告了
1028
00:51:02,026 --> 00:51:05,428
Okay, when you submit your homework, no warnings please.
当你们交作业的时候,不准有警告
1029
00:51:05,430 --> 00:51:07,463
Okay, no warnings. And certainly no errors,
没有警告,当然也不能有错误
1030
00:51:07,465 --> 00:51:10,433
those are red things those won't build, no warnings. So
有错误的时候就不能 build
1031
00:51:10,435 --> 00:51:12,535
what is this warning, let's take a look at this warning.
这个警告是什么呢?我们来看看
1032
00:51:12,537 --> 00:51:14,170
You look at a warning by clicking on it.
我们通过点击来看一个警告
1033
00:51:14,172 --> 00:51:16,572
So I'm gonna click on it, and it says here,
我点击他,这里是这么写的
1034
00:51:16,574 --> 00:51:21,644
variable digit with never mutated, consider changing to
digit 这个变量没有被改变过
1035
00:51:21,646 --> 00:51:26,082
let constant. Okay, what does that mean?
是不是考虑把他变成 let 型常量。这是什么意思呢?
1036
00:51:26,084 --> 00:51:31,020
That means that instead of var here, it wants us to put let.
这里的意思是,希望我们用 let 而不是 var
1037
00:51:31,022 --> 00:51:34,623
And in fact, you should always do that. Anytime you declare
事实上我们应该这么做
1038
00:51:34,625 --> 00:51:37,793
a local variable, okay, or even a property that is
任何时候申明一个局部变量,或者一个属性
1039
00:51:37,795 --> 00:51:40,629
initialized at the beginning and never changes, in other
在初始化后就一直没被改变,换句话说,
1040
00:51:40,631 --> 00:51:43,599
words, it's a constant, use let instead of var.
就是个常量,我们就应该用 let 而不是 var
1041
00:51:43,601 --> 00:51:46,268
Now why do you want to do that? Two reasons really, one,
那么我们现在为什么要这么做?两个理由
1042
00:51:46,270 --> 00:51:48,804
it helps people reading your code realize this thing's
一、这帮助别人读你的代码的时候意识到
1043
00:51:48,806 --> 00:51:51,040
never going to change, I don't have to worry about it,
这个一直不会变,不需要去担心它
1044
00:51:51,042 --> 00:51:54,243
it's just like a constant. And two, if that thing were for
这只是个常量。二、如果那个东西,
1045
00:51:54,245 --> 00:51:57,613
example, an array, or a dictionary, and you use let,
比如说是一个数组或者字典,你用了 let
1046
00:51:57,615 --> 00:52:00,883
that means nothing can be put into that dictionary or
这就意味着没有什么可以放进字典或从字典拿出
1047
00:52:00,885 --> 00:52:04,553
taken out. Or into the array or taken out okay. So
也可以放进数组或从数组拿出
1048
00:52:04,555 --> 00:52:08,023
it's how you can create read only arrays and dictionaries
这就是你如何创建只读的数组或者是字典
1049
00:52:08,025 --> 00:52:12,628
okay. So, always use let when you are doing a constant.
所以申明常量的时候一定记得用 let
1050
00:52:12,630 --> 00:52:14,563
And in fact notice that XCode is saying,
注意 Xcode 这里说的
1051
00:52:14,565 --> 00:52:17,733
hey if you click here I'll fix it for you. Okay,
如果你点这里,我会为你修复这个问题
1052
00:52:17,735 --> 00:52:21,904
see how it says fix it replays var with let. Sure. Okay, and
看一下它如何用 let 代替 var 修正这个问题
1053
00:52:21,906 --> 00:52:24,773
it did it and now the warnings gone. Okay, so
所以它做到了,警告没了
1054
00:52:24,775 --> 00:52:29,678
let's run, see if this is working. We're crossing our
来跑一下,看看是不是能行
1055
00:52:29,680 --> 00:52:32,915
fingers about that whole string question mark thing.
我们越过我们的手指关于整串问号的事情
1056
00:52:32,917 --> 00:52:35,518
All right, so here we go, let's press 5.
好了,来按下 5
1057
00:52:36,921 --> 00:52:40,189
The heck is going on down there? Okay, touched
这下面就会显示数字
1058
00:52:40,191 --> 00:52:45,294
optional 5 digit? Okay so what is this optional business.
touched optional("5") digit?这里的 optional 是什么
1059
00:52:45,296 --> 00:52:47,163
Here you all wanna take a deep breath,
所以这里你们得深呼吸
1060
00:52:47,165 --> 00:52:50,332
clear your minds, and pay close attention okay. Because
清空你们的脑袋,集中全部注意力
1061
00:52:50,334 --> 00:52:52,935
this is a very important thing in Swift, it's ubiquitous
因为这是 Swift 里非常非常重要的东西
1062
00:52:52,937 --> 00:52:57,740
throughout the entire iOS API okay. Very cool feature, but
这在 iOS API 里无处不在,一个非常酷的特性
1063
00:52:57,742 --> 00:53:00,442
it requires a little bit of getting used to in terms of
但是需要一段时间来熟悉这个语法
1064
00:53:00,444 --> 00:53:05,381
its syntax okay? This is the feature of optionals. Okay?
这是一个叫 optional(可选类型)的特性
1065
00:53:05,383 --> 00:53:09,818
In Swift, there is a type called optional.
在Swift里,有一种类型叫 optional
1066
00:53:09,820 --> 00:53:12,388
It's a type like int or boolean or anything else,
这是一种类似于 int 或 bool 的类型
1067
00:53:12,390 --> 00:53:14,323
string, it's dictionary, array,
或者是 string、dictionary、array 这样的类型
1068
00:53:14,325 --> 00:53:18,360
optional. Optional is just a type. This type can only have
optional 只是一个类型,这个类型只有两个值
1069
00:53:18,362 --> 00:53:23,699
two values. One value is not set. Okay?
一个值是没有被赋值
1070
00:53:23,701 --> 00:53:28,070
That's expressed in Swift with the key word nil, N-I-L.
在 Swift 里表示它的关键字是 nil
1071
00:53:28,072 --> 00:53:31,473
Nil only means, this optional is not set.
nil 只表示这个 optional 没有被赋值
1072
00:53:31,475 --> 00:53:33,709
That's all it means in Swift. It doesn't mean zero.
这就是他在 Swift 里全部的含义,这不代表它为零
1073
00:53:33,711 --> 00:53:36,245
It doesn't mean a pointer that doesn't point anywhere, like
这不像别的语言,它不代表一个不指向任何地方的指针
1074
00:53:36,247 --> 00:53:39,882
in other languages. Nil means an optional that's not set.
nil 代表一个 optional 没有被赋值
1075
00:53:39,884 --> 00:53:45,120
The other state that an optional can be is set,
另一个状态就是一个 optional 被赋值了
1076
00:53:45,122 --> 00:53:48,691
okay? Now, if it's in the set state, it can have
如果它在被赋值状态
1077
00:53:48,693 --> 00:53:52,995
an associated value, okay? An associated value,
他就可以有一个关联值
1078
00:53:52,997 --> 00:53:55,698
which can be of any other type, okay? Actually it also
一个可以是其他任何类型的关联值
1079
00:53:55,700 --> 00:53:57,199
could also be an optional. You can have an optional,
实际上,它还可以是一个 optional 。你可以有一个 optional
1080
00:53:57,201 --> 00:54:00,970
optional, but that associated value is just associated with
但是关联值只和它赋值的状态关联
1081
00:54:00,972 --> 00:54:03,405
this set state, okay? In the not set state,
在未赋值状态
1082
00:54:03,407 --> 00:54:06,375
there is no associated value cuz it's not set. Okay?
它就没有关联值,因为它没有被赋值
1083
00:54:06,377 --> 00:54:09,011
Now what does it look like to declare it optional? Well,
那么把它申明为 optional 看起来是什么样呢?
1084
00:54:09,013 --> 00:54:12,615
let's go back with our option key and take a look at current
让我们回过头看一下我们的 option 键看一下 currentTitle
1085
00:54:12,617 --> 00:54:15,017
title. Okay, you remember this from the documentation?
还记得这段文档吗?
1086
00:54:15,019 --> 00:54:20,289
It says var currentTitle string? That means this is
这里写到 "var currentTitle: string?"
1087
00:54:20,291 --> 00:54:25,694
an optional whose associated value is a string. Okay?
这里的意思是这是个关联值为 string 的 optional
1088
00:54:25,696 --> 00:54:28,063
So if someone looks at this property and
所以如果有人看到这个属性
1089
00:54:28,065 --> 00:54:29,198
says, what type is that property?
当他问这个是什么类型的时候
1090
00:54:29,200 --> 00:54:32,701
And they say, it's a string but it's optional no, no.
他可能会说,这是个 string,但是是 optional。不!
1091
00:54:32,703 --> 00:54:37,973
This is an optional and it's associated values of string,
这是个 optional,他的关联值是 string
1092
00:54:37,975 --> 00:54:42,311
okay? Get used to saying, this type is optional, okay? So
要习惯于说这个类型是 optional
1093
00:54:42,313 --> 00:54:45,114
this is an optional string, that's what would say.
这是个 optional string(可选字符串),这才是正确的
1094
00:54:45,116 --> 00:54:47,516
It's an optional string, okay? But primarily,
这是个 optional string。主要是个 optional
1095
00:54:47,518 --> 00:54:51,053
it's an optional. Okay, it's associated value is a string.
关联值是 string
1096
00:54:51,055 --> 00:54:53,922
So this currentTitle, why is it an optional? Well,
所以这个 currentTitle 为什么是个 optional?
1097
00:54:53,924 --> 00:54:58,560
because we might have a button here that has no title set.
这是因为一个按钮的标题可能不会被赋值
1098
00:54:58,562 --> 00:55:02,064
Not set. Okay, so the title needs to be an optional,
没有赋值,所以它应该是个 optional
1099
00:55:02,066 --> 00:55:05,367
so it can represent the case where it's not set. Now,
所以它就可以表示没有被赋值的情况
1100
00:55:05,369 --> 00:55:08,304
anything can be an optional. Not just pointers, or objects.
所以任何东西都可以是 optional。不只是指针或对象
1101
00:55:08,306 --> 00:55:11,440
Ints could be optional. You could have an optional int,
int 可以是 optional,你可以有一个 optional int
1102
00:55:11,442 --> 00:55:15,077
okay? But here we are representing the title,
但这里我们代表了一个标题
1103
00:55:15,079 --> 00:55:18,414
the button, which is the string and it might be not set
这个按钮的标题是个 string,这可能不会被赋值
1104
00:55:18,416 --> 00:55:20,382
okay? So we need that to be an optional.
我们需要这个是个 optional
1105
00:55:20,384 --> 00:55:23,519
So how do I get the string, because I don't want
所以我怎么得到这个 string
1106
00:55:23,521 --> 00:55:27,690
this digit, which currently is also an optional string?
我不希望这个 digit,现在还是个 optional string
1107
00:55:27,692 --> 00:55:30,559
Isn't it kinda weird here when I said let digit equal,
当这里是 "let digit =" 的时候不是很奇怪吗
1108
00:55:30,561 --> 00:55:34,029
nobody said how come you didn't say colon string?
没人觉得你为什么不加上 ":string" 呢?
1109
00:55:34,031 --> 00:55:37,900
Okay? I didn't say the type. I declared a local variable.
我没有说类型,我申明一个局部变量
1110
00:55:37,902 --> 00:55:41,170
It didn't even have a type. How was that possible?
我甚至都没有一个类型,这怎么可能?
1111
00:55:41,172 --> 00:55:46,742
In Swift, Swift will infer the type all the time, okay? If
在 Swift 里,Swift 会一直推断类型
1112
00:55:46,744 --> 00:55:49,411
it's possible for it to know the type it will infer it.
如果它可以在这里知道他的类型,那么就会推断这个类型
1113
00:55:49,413 --> 00:55:54,183
And in fact it's wrong really bad coding style to put that
实际上,这里写上类型是个非常差的编程风格
1114
00:55:54,185 --> 00:55:58,220
type in there. It's better to just say let digit equal and
这里最好就这么写
1115
00:55:58,222 --> 00:56:01,190
let swift infer it. And here Swift has inferred.
让 Swift 来推断他的类型。这里 Swift 也做到了
1116
00:56:01,192 --> 00:56:03,325
The digit must be of type string question mark,
这里的 digit 一定会是个 "string?" 类型
1117
00:56:03,327 --> 00:56:06,228
that's because this is of type string question mark.
因为后面的也是个 "string?" 类型
1118
00:56:06,230 --> 00:56:09,365
So of course digit has to be that. You see what's happening
所以 digit 当然也应该是那个类型。你看到那发生什么了吗?
1119
00:56:09,367 --> 00:56:12,668
there? So you're going to see type inference all the time in
Swift 里你会时时看到类型推断的结果
1120
00:56:12,670 --> 00:56:14,203
Swift, it makes your code much more compact,
这让你代码更加简洁
1121
00:56:14,205 --> 00:56:18,374
a lot less repeated stuff, is that a question in the back?
很多更少重复的工作,那边的同学有问题吗?
1122
00:56:18,376 --> 00:56:19,875
>> Yeah, is that the same for
这对 var 是一样的吗?
1123
00:56:19,877 --> 00:56:20,209
var too. >> Is the question is
问题是:
1124
00:56:20,211 --> 00:56:22,277
that same for var too, absolutely it's the same for
这对 var 是一样的吗?
1125
00:56:22,279 --> 00:56:26,382
var, okay? So, how do I get that string though?
完全一样。所以我怎么获得那个 string 呢?
1126
00:56:26,384 --> 00:56:28,951
If this button title is set, how do I get that
如果这个按钮的标题已经有了,那我如何
1127
00:56:28,953 --> 00:56:32,721
associated value thing, okay, out of this optional?
从这个 optional 里面获取这个关联值?
1128
00:56:32,723 --> 00:56:36,525
And the answer is you put an exclamation point in the end,
方法是你在最后放一个感叹号
1129
00:56:36,527 --> 00:56:39,328
okay. Now, optional is question mark on the end of
optional 在定义的末尾有个问号
1130
00:56:39,330 --> 00:56:42,831
a declaration. Exclamation point is unwrap this thing and
感叹号是用来 unwarp(解包)这个 optional
1131
00:56:42,833 --> 00:56:46,402
give me the associative value. So this is very succinct,
然后给我这个关联值。这是很简明的
1132
00:56:46,404 --> 00:56:50,038
one character for each side of you using this, okay?
每次可以使用一种符号
1133
00:56:50,040 --> 00:56:51,774
Question. >> Can you briefly talk about
学生问题:
1134
00:56:51,776 --> 00:56:52,074
why you want the title to,
1135
00:56:52,076 --> 00:56:53,976
why you design the title to be optional instead of like
1136
00:56:53,978 --> 00:56:59,114
an empty string? >> Okay, so the question is,
问题是
1137
00:56:59,116 --> 00:57:02,384
why does Swift have this optional thing instead of just
为什么 Swift 不直接用空字符
1138
00:57:02,386 --> 00:57:05,120
doing empty string to mean a button without a title?
而是用 optional 来表示一个没有标题的 button?
1139
00:57:05,122 --> 00:57:10,492
Or maybe for an int minus 1. I don't know. Or for a point or
或者是一个 -1 的 int
1140
00:57:10,494 --> 00:57:14,463
0 memory address or something like that. The main reason for
又或者是一个指针、一个不占内存的地址或者类似的什么东西
1141
00:57:14,465 --> 00:57:18,100
that is for example, pointers to objects in Swift, you don't
这主要的原因是,Swift 里的指针
1142
00:57:18,102 --> 00:57:21,703
see the memory address. So you couldn't have it be 0, okay,
你是看不见内存地址的。所以你不能让他为 0
1143
00:57:21,705 --> 00:57:23,906
that's hidden from you. So there'd be no way to do it.
这对你是隐藏的。所以没法这样做
1144
00:57:23,908 --> 00:57:28,076
For an int, if you wanted an optional int, is it minus 1 or
对于 int,如果你想要一个 optional int
1145
00:57:28,078 --> 00:57:31,280
0, max int? What is it, it don't know what it is.
它到底是 -1?是 0?还是别的什么?他不知道应该是多少。
1146
00:57:31,282 --> 00:57:34,817
So Swift is trying to be consistent across all things.
所以Swift 想涵盖所有情况
1147
00:57:34,819 --> 00:57:38,854
Ints, pointers to objects, whatever to make sure that
Ints ,指向对象的指针
1148
00:57:38,856 --> 00:57:40,456
we can tell which ones are not set. Okay, so
无论是什么,要保证我们能知道他有没有被赋值
1149
00:57:40,458 --> 00:57:42,591
that's the thought behind it. It's really great actually,
这就是 optional 背后的思考,非常棒
1150
00:57:42,593 --> 00:57:46,094
once you get used to it it's fantastic. All right, so
一旦你习惯这种做法,真的非常赞
1151
00:57:46,096 --> 00:57:47,830
we unwrap it with this exclamation point.
我们用感叹号对它进行 unwarp
1152
00:57:47,832 --> 00:57:51,767
Now what would happen if that current title value was
如果这里的 currentTitle 没有被赋值,会发生什么?
1153
00:57:51,769 --> 00:57:54,903
not set? And this exclamation point tried to pull out
这个感叹号试着去把值取出来
1154
00:57:54,905 --> 00:57:57,873
the associated value? What do you think would happen?
你觉得会发生什么
1155
00:57:57,875 --> 00:57:58,674
>> Error.
错误
1156
00:57:58,676 --> 00:58:00,576
>> Error, yeah, bad error.
错误,是的大错特错
1157
00:58:00,578 --> 00:58:02,644
It would crash my app, okay? So
这会让你的应用崩溃
1158
00:58:02,646 --> 00:58:05,247
exclamation point crashes your app if you tried to
如果你试着 unwarp 一个没有被赋值的 optional
1159
00:58:05,249 --> 00:58:08,750
unwrap an optional that's in the not set state, okay,
感叹号会让你的应用崩溃
1160
00:58:08,752 --> 00:58:12,921
if it's nil. Now some of you conservative folks might be
如果它为空。有些保守的人会说
1161
00:58:12,923 --> 00:58:15,257
saying I'm never using exclamation point, that's so
他从来不用感叹号
1162
00:58:15,259 --> 00:58:18,927
easy to crash my app. Okay? And the answer to that is yes
因为太容易让应用崩溃。但答案是
1163
00:58:18,929 --> 00:58:22,498
it is, but sometimes crashing your app is a good thing.
是的,确实容易让应用崩溃,但有时候这是好事
1164
00:58:22,500 --> 00:58:23,999
It helps you find a bug, okay?
这会帮助你找到 bug
1165
00:58:24,001 --> 00:58:26,869
Because something that you expect to be set by a certain
因为有的时候你希望他被赋予一个确定的值
1166
00:58:26,871 --> 00:58:28,737
point isn't, and it crashes your app, and
如果没有,那就让应用崩溃
1167
00:58:28,739 --> 00:58:31,206
you find it before it goes out to your customer. And some
你在 app 发布给你的用户之前就找到这个 bug
1168
00:58:31,208 --> 00:58:33,842
unexpected thing is happening because something's not set.
因为有些没被赋值,会发生一些预期之外的事情
1169
00:58:33,844 --> 00:58:36,311
Now, I'm gonna show you a little later how you can
我稍后会给你们展示
1170
00:58:36,313 --> 00:58:38,046
unwrap the optional and get the associated value
如何不让应用崩溃的情况下 unwarp optional 并获取关联值
1171
00:58:38,048 --> 00:58:41,250
without crashing. Basically, test to see if it's set and
总的来说,自测一下它有没有被赋值
1172
00:58:41,252 --> 00:58:44,286
do it, okay? So I'm gonna show you that in a little bit.
我过一会会做给你们看
1173
00:58:44,421 --> 00:58:45,954
All right, so we've unwrapped this, optional.
我们已经 unwarp 了这个 optional
1174
00:58:45,956 --> 00:58:49,224
We're gonna crash if it's not set, that's fine. And so
如果没有被赋值会让应用崩溃,这没问题
1175
00:58:49,226 --> 00:58:52,661
now, notice when I do the option click on digit,
注意我按住 option 键 然后点这个 digit
1176
00:58:52,663 --> 00:58:57,299
what's the type? String, not optional string,
什么类型呢?不是 optional string
1177
00:58:57,301 --> 00:59:00,569
string, that's good because we unwrapped it. So now when we
就是 string,这很棒,因为我们 unwarp 了
1178
00:59:00,571 --> 00:59:05,240
run, instead of saying touched optional 6 digit down there,
现在跑一下,就不会说 "touched optional 6 digit" 了
1179
00:59:05,242 --> 00:59:10,579
it's going to say touch the 6 digit, touch 2, touch 4, 8, 9,
而是说 "touch the 6 digit" 或者 "touch 2、4、8、9"
1180
00:59:10,581 --> 00:59:17,019
okay? Got it? Okay, now, this is going good, but we
明白了吗?这很棒
1181
00:59:17,021 --> 00:59:21,123
don't really want this to be printing out in the console.
但我们不想它在控制台打印出来
1182
00:59:21,125 --> 00:59:23,725
We want our calculator to have a display and
我们希望我们的计算器有个显示
1183
00:59:23,727 --> 00:59:26,762
we want the digits we typed to go in the display, right. So
我们希望我们按下的 digits 能显示出来
1184
00:59:26,764 --> 00:59:29,665
let's go do that. Let's add a display to our
所以现在来做这个。加一个显示
1185
00:59:29,667 --> 00:59:32,534
user interface here. We're gonna put it at the top, okay?
到 UI 上。我们会把这个放在顶部
1186
00:59:32,536 --> 00:59:35,537
I'm talking about this thing right up here where this 0 is.
我指的是这个右上角零的这个
1187
00:59:35,539 --> 00:59:37,439
And I wanna it to kinda look similar to that,
我希望看起来很像这个
1188
00:59:37,441 --> 00:59:39,241
maybe some different colors but kinda like that.
可能是不同颜色,但是类似吧
1189
00:59:39,243 --> 00:59:44,479
So I'm gonna go back to my utilities area over here and
现在回到组件的区域
1190
00:59:44,481 --> 00:59:45,547
go to the bottom. And this time,
到最底部,这次
1191
00:59:45,549 --> 00:59:48,617
instead of grabbing a button, I'm gonna grab one of these,
不是抓一个 button,而是抓一个这个
1192
00:59:48,619 --> 00:59:52,921
a label. So a label is Read only text,
一个 label,label 是一个只读的文本
1193
00:59:52,923 --> 00:59:55,691
okay? The user can't touch on it, and then type
用户不能碰这个
1194
00:59:55,693 --> 00:59:58,527
with the little keyboard on the iPhone, okay? This is read
然后用键盘输入
1195
00:59:58,529 --> 01:00:01,229
only text which is what we want on my calculator. So
这是一个我们计算器上需要的只读的文本
1196
01:00:01,231 --> 01:00:04,099
I'm gonna bring it over here, bring it out, again same way
我会让它到这里,拖出来
1197
01:00:04,101 --> 01:00:08,503
as before. I can resize it, I can change what's in there,
跟之前一样,我可以重新定义它的大小
1198
01:00:08,505 --> 01:00:11,273
I can change the font over here. Let's make this one
可以改变文字。现在让它字变大
1199
01:00:11,275 --> 01:00:15,978
really big, how about 40 point. I can even make it
差不多 40pt 吧。我还可以让它右对齐
1200
01:00:15,980 --> 01:00:18,747
right aligned, you know how in calculators usually
你知道一般计算器都是右对齐
1201
01:00:18,749 --> 01:00:21,316
the text is on the right so there's an alignment thing for
这里有个 label 对齐选项
1202
01:00:21,318 --> 01:00:23,952
the label here, so we'll do that. Maybe I want to change
我们可以这么做。
1203
01:00:23,954 --> 01:00:27,189
the background color for this one too, how about blue, okay
我也想换一下背景颜色,蓝色就不错
1204
01:00:27,191 --> 01:00:30,158
there's blue. I don't really like black on blue, here's
但我不喜欢蓝底黑字
1205
01:00:30,160 --> 01:00:33,829
a color of the text, we'll go white on blue. Okay, so
这里是文字颜色,蓝底白字就挺好
1206
01:00:33,831 --> 01:00:37,099
I can basically set it however I want, okay, got this UI,
我可以随意设置这些
1207
01:00:37,101 --> 01:00:39,001
I could use the blue lines by the way,
也可以用蓝线辅助
1208
01:00:39,003 --> 01:00:43,405
to line it up nicely here, but again not so important because
这样就可以很整齐的放置
1209
01:00:43,407 --> 01:00:46,742
I'm going to do the flexible UI thing on Wednesday, but
不是特别重要的是,我会在周三教自适应的 UI 布局
1210
01:00:46,744 --> 01:00:51,780
I could do it. All right, so now, how about connecting this
我可以做这些。
1211
01:00:51,782 --> 01:00:55,050
to our controller? This is a little different because this
那么如何把这个 label 跟 controller 联系起来?
1212
01:00:55,052 --> 01:00:58,220
is not a case where the user's gonna touch this thing and
这有点不太一样,因为这不是那种用户点一下
1213
01:00:58,222 --> 01:00:58,887
it's gonna invoke a method.
就调用一个方法的情况
1214
01:00:58,889 --> 01:01:02,524
It's more the other way around. I wanna talk to it.
这是另外一种情况,我想跟它交互
1215
01:01:02,526 --> 01:01:04,359
Okay, I wanna put things in the display.
我想把东西放进去展示
1216
01:01:04,361 --> 01:01:07,863
So this is communication coming from the controller to
这是一个从 controller 到显示的通信
1217
01:01:07,865 --> 01:01:11,500
the display instead of from a button to the controller. So
而不是一个按钮到 controller 的通信
1218
01:01:11,502 --> 01:01:15,771
we still connect them up in the same way using control and
我们还是一样用按住 control 拖拽的方式
1219
01:01:15,773 --> 01:01:20,275
drag, okay, to put something into our controller. But
把一些东西放进 controller
1220
01:01:20,277 --> 01:01:24,179
this time instead of action up here we're going to do outlet.
但这次我们选 outlet 而不是 action
1221
01:01:24,181 --> 01:01:27,149
And what outlet means is instead of creating a method,
outlet 的意思是新建一个属性
1222
01:01:27,151 --> 01:01:30,285
create a property. Right? An instance variable,
而不是方法。一个实例变量
1223
01:01:30,287 --> 01:01:32,721
just like currentTitle is an instance variable, or
就像 currentTitle 是一个实例变量
1224
01:01:32,723 --> 01:01:34,256
property, we're gonna have a property here.
或者一个属性,这里我们有了一个属性
1225
01:01:34,258 --> 01:01:36,992
And this property is going to point to this display for
这个属性会指向显示屏
1226
01:01:36,994 --> 01:01:39,628
us so we can access it any time we want. So
这样我们就可以在任何需要的时候获取它
1227
01:01:39,630 --> 01:01:44,299
I'm gonna call this property display, not that, display,
我准备叫这个属性 display
1228
01:01:44,301 --> 01:01:47,469
okay? Luckily it got the type right here, it didn't say any
碰巧这里有类型,这没说有任何对象
1229
01:01:47,471 --> 01:01:50,338
objects. It's good, it knows it was a UI label. And don't
这很好,它知道是个 UILabel
1230
01:01:50,340 --> 01:01:52,974
worry about this storage, weak or strong, we'll talk about
也不用担心这个 storage 是 strong 或是 weak
1231
01:01:52,976 --> 01:01:55,043
that next week, so you can just ignore that for now.
我们下周再说,所以你现在就可以忽略他
1232
01:01:55,045 --> 01:01:58,213
Okay, so I hit connect and it creates,
我点击 connect,它就被创建了
1233
01:01:58,215 --> 01:02:02,250
instead of creating a method here, it created a property.
这里创建了一个属性而不是一个方法
1234
01:02:02,252 --> 01:02:06,722
Now again, this IB outlet is just something that Xcode puts,
再一次,这个 IBOutlet 只是 Xcode 放这的。
1235
01:02:06,724 --> 01:02:07,989
put this little circle here. So
放了一个小圆圈在这里
1236
01:02:07,991 --> 01:02:11,159
we can see what it's connected to, so you can ignore that.
我们可以看到这个链接到了什么,所以你可以忽略那个
1237
01:02:11,161 --> 01:02:12,060
I told you to ignore weak and
我说了可以忽略 weak 和 strong
1238
01:02:12,062 --> 01:02:14,730
strong, so you can ignore that. And so hopefully
所以可以忽略它
1239
01:02:14,732 --> 01:02:18,533
this is familiar to you, it looks like current title, var.
这个看起来很熟悉,有点像 currentTitle
1240
01:02:18,535 --> 01:02:21,636
This is a property on our controller. Okay,
这是你 controller 里的一个属性
1241
01:02:21,638 --> 01:02:24,005
an instance variable, if you wanna think of it that way.
一个实例变量,如果你想这么认为的话
1242
01:02:24,007 --> 01:02:29,544
It's called display. Its type is UI label exclamation point.
这叫 display。类型是 UILabel!
1243
01:02:29,546 --> 01:02:33,648
Okay, now what the heck is that, exclamation point? Okay,
这里的感叹号又是什么意思呢?
1244
01:02:33,650 --> 01:02:35,784
I thought exclamation point was unwrapping optionals,
我认为感叹号是用来 unwrapping optional 的
1245
01:02:35,786 --> 01:02:38,153
why is there an exclamation point here, it makes no sense.
这里为什么有个感叹号?这不科学。
1246
01:02:38,155 --> 01:02:42,424
And in fact you can put a question mark here because
事实上你可以在这里放个问号
1247
01:02:42,426 --> 01:02:44,860
exclamation point also means optional.
因为感叹号也代表了 optional
1248
01:02:44,862 --> 01:02:47,329
It means the same kinda optional as question mark.
这跟问号表示的 optional 是一个意思
1249
01:02:47,331 --> 01:02:48,830
And I'm gonna leave this question mark, and
我会先在这放一个问号
1250
01:02:48,832 --> 01:02:50,599
I'm gonna keep working on this demo, and
继续完成这个 demo
1251
01:02:50,601 --> 01:02:51,099
then I'm gonna go back and
然后回过头来
1252
01:02:51,101 --> 01:02:52,167
change it to an exclamation point, and
把它改成感叹号
1253
01:02:52,169 --> 01:02:55,604
show you what exclamation point means. Okay? But
告诉你们感叹号是什么意思。
1254
01:02:55,606 --> 01:02:58,073
exclamation point and question mark both mean optional,
当你定义的时候,
1255
01:02:58,075 --> 01:03:02,744
when you're declaring. Okay, so now I have this display.
感叹号和问号都表示 optional。所以现在你有了这个 display
1256
01:03:02,746 --> 01:03:06,515
When numbers get touched here, I want to, basically, append
当数字被按下的时候,我想
1257
01:03:06,517 --> 01:03:09,684
these digits onto whatever's already in the display,
不管显示了什么都把 digit 加到 display 上
1258
01:03:09,686 --> 01:03:12,287
all right? So let's go ahead and do that.
所以现在来做这个
1259
01:03:12,289 --> 01:03:14,556
We can get rid of this print to the console.
删掉这个打印到控制台的
1260
01:03:14,558 --> 01:03:16,658
We don't need to do that anymore. So I'm gonna create
我们不再需要它
1261
01:03:16,660 --> 01:03:19,294
another local variable which is also gonna be a constant,
我需要再新建一个局部变量,当然也是个常量
1262
01:03:19,296 --> 01:03:24,366
so it's let. I'm gonna call it textCurrentlyInDisplay, okay?
所以是let 。我叫它 textCurrentlyInDisplay
1263
01:03:24,368 --> 01:03:28,904
So I wanna get the text that's in this display currently. So
我想获得现在 display 里的字符
1264
01:03:28,906 --> 01:03:33,241
I'm going to send a message to display here, right? Display.
我要在这给 display 发一个消息
1265
01:03:33,243 --> 01:03:35,377
And the message, we can look it up in the documentation.
这个信息,我们可以从文档中得知
1266
01:03:35,379 --> 01:03:37,913
But the message that I want to send is text. Okay?
但是我想发的消息是文字
1267
01:03:37,915 --> 01:03:41,416
That gives me the text in the display. This is a UI label
这会给我 display 里的文字。这是个 UILabel 方法
1268
01:03:41,418 --> 01:03:45,453
method. But this is giving me an error. Okay, and
但这里却告诉我是个错误
1269
01:03:45,455 --> 01:03:48,690
the error that it's giving me here is that I haven't
这个错误说我这里没有 unwarp display
1270
01:03:48,692 --> 01:03:51,927
unwrapped display. You see that display is an optional
你看 display 是个 optional label
1271
01:03:51,929 --> 01:03:55,730
label. You can't send text to the type optional Okay, and
你不能把一个 text 赋给一个 optional
1272
01:03:55,732 --> 01:03:58,633
optional doesn't understand the message text.
optional 不理解什么是文字
1273
01:03:58,635 --> 01:04:02,003
UILabel does. So, we need to get that associated value
但 UILabel 知道。所以我们要获得 display 的 label 的关联值
1274
01:04:02,005 --> 01:04:05,207
of label, of a display, which is the UILabel, and
是一个 UILabel
1275
01:04:05,209 --> 01:04:07,843
we do that with the exclamation point, here.
我们会在这放一个感叹号
1276
01:04:07,845 --> 01:04:10,645
And, again, notice it's offering, here, to fix it. So,
注意这里,提供了修复选项
1277
01:04:10,647 --> 01:04:13,515
I'm gonna fix it, and i put the exclamation point in
所以我来修复他,就会放一个感叹号在这
1278
01:04:13,517 --> 01:04:17,752
there. Now, this is a UILabel. It's an unwrapped,
现在这里是个 UILabel,这一个 unwarpped
1279
01:04:17,754 --> 01:04:20,589
optional UILabel, right? This is a UILabel,
optional UILabel,对吗?这是一个 UILabel
1280
01:04:20,591 --> 01:04:22,624
and I'm sending it the message text,
我把那个文字值发给他
1281
01:04:22,626 --> 01:04:24,626
actually text is a property, just like currentTitle
实际上 text 是个属性, 就像 currentTitle
1282
01:04:24,628 --> 01:04:29,030
is a property on button, text is a property on UILabel. And,
是一个 button 的属性,text 是 UILabel 的一个属性
1283
01:04:29,032 --> 01:04:33,001
in fact, if I option+click on it, you'll see var text is an
如果我按住 option 点它,你可以看见 var text
1284
01:04:33,003 --> 01:04:37,806
optional string. Why? Because UILabel might be not set.
是一个 optional string。为什么?因为 UILabel 可能没有被赋值
1285
01:04:37,808 --> 01:04:40,275
The text in it might not be set. So it also is an option,
它里面的 text 可能没有被赋值,所以这也是个 optional
1286
01:04:40,277 --> 01:04:45,647
just like currentTitle. So I need to unwrap that as well.
就像 currentTitle 一样,我需要同样需要 unwarp
1287
01:04:46,316 --> 01:04:48,950
Okay? And so now I have a string,
所以我现在有个 string
1288
01:04:48,952 --> 01:04:51,052
which is the string that's in there. Now you're probably
就是这里这个
1289
01:04:51,054 --> 01:04:52,654
getting tired of these exclamation points and so
现在你可能对这个感叹号感到有点疲惫,我也是
1290
01:04:52,656 --> 01:04:54,422
am I, and we're gonna be able to get rid of them.
我们将要去掉它
1291
01:04:54,424 --> 01:04:56,358
Some in a second here, so not to worry.
一会就去掉,不用担心
1292
01:04:56,360 --> 01:04:58,693
Anyway, I have this text currently in display.
不管怎样,我现在在 display 里拿到了这个文字
1293
01:04:58,695 --> 01:05:02,697
So now I'm just gonna say that the displays, okay, and
所以现在,display
1294
01:05:02,699 --> 01:05:07,002
I'm unwrapping it, text = textCurrentlyInDisplay. I'm
unwarp 它,"text = textCurrentlyInDisplay"
1295
01:05:07,004 --> 01:05:11,706
just hitting tab here, okay, + the digit that was pressed.
我在这按了 tab 键,然后代码写 "+ digit"
1296
01:05:11,708 --> 01:05:14,809
Okay, notice that you can use + to concatenate strings.
注意你用加号来串联字符
1297
01:05:14,811 --> 01:05:17,913
That's really fun. Okay, some language allow that, some not.
这很有趣,有的语言允许这么做,有的不允许
1298
01:05:17,915 --> 01:05:21,816
So it does. Notice also that when I'm setting the value of
Swift 可以。同时注意我在给 optional 赋值的时候
1299
01:05:21,818 --> 01:05:25,120
an optional, remember text here is an optional string.
记住这里的 text 是个 optional string
1300
01:05:25,122 --> 01:05:28,123
I can set it to be a string, and that means,
我可以把它赋值为 string,这意味着
1301
01:05:28,125 --> 01:05:32,294
by definition, set this optional to set and
根据定义,是赋值给 optional 来设定
1302
01:05:32,296 --> 01:05:37,332
use this as its associated value. Right? Now,
把它赋为这个的关联变量
1303
01:05:37,334 --> 01:05:41,736
how do you think I would set this optional to not be set?
那么你认为我怎么才能给这个 optional 赋一个不赋值的状态?
1304
01:05:41,738 --> 01:05:45,206
Anyone know? You would say,
有人知道吗?
1305
01:05:45,208 --> 01:05:49,878
set this thing to be nil. This keyword means opp,
把它赋值为 nil,这个关键字
1306
01:05:49,880 --> 01:05:52,781
optional not set. So that would set this text.
表示 optional 没有被赋值。所以会赋这个值
1307
01:05:52,783 --> 01:05:56,051
It would blank out this text. It would set it to be not set.
这个栏里为空,这会把它赋为没有被赋值
1308
01:05:56,053 --> 01:05:58,119
Okay, so an optional you can, when you're setting it,
所以一个 optional,在你给它赋值的时候
1309
01:05:58,121 --> 01:06:02,190
you can set it to either nil or to an associated value.
你可以把它赋为 nil 或者是 关联值
1310
01:06:03,060 --> 01:06:06,261
Okay? Sound good? So let's run.
听起来不错?让我继续
1311
01:06:11,435 --> 01:06:13,401
All right, so hopefully when we press these buttons,
所以,理想情况下当我们按下按钮
1312
01:06:13,403 --> 01:06:15,971
it's going to append it on to the end, here. So let's try
数字就会被加到末尾
1313
01:06:15,973 --> 01:06:20,976
five, it did it. Eight, nine, although I'm not really a fan
5,它做到了。8,9,但是我不喜欢
1314
01:06:20,978 --> 01:06:25,313
of a zero at the beginning there. Okay, that zero is not
前面有个 0.所以
1315
01:06:25,315 --> 01:06:27,849
really how a calculator would work. Okay, when I press that
这不是一个计算器应该有的样子。当我按五的时候
1316
01:06:27,851 --> 01:06:30,452
five, I'm essentially typing a new number. It would get rid
实际上我是按了一个新数字。
1317
01:06:30,454 --> 01:06:33,021
of the zero. So, we really need a way to keep track of
它应该取代 0。所以需要一个方法
1318
01:06:33,023 --> 01:06:35,757
whether we're in the middle of typing a number or not. Cuz if
来追踪我们是不是在输入中
1319
01:06:35,759 --> 01:06:37,425
we're in the middle of typing a number,
因为如果我们在输入中
1320
01:06:37,427 --> 01:06:37,926
we do the apend thing, but
那就得把数字加到尾部
1321
01:06:37,928 --> 01:06:39,260
if we're not in the middle of typing a number,
如果不是
1322
01:06:39,262 --> 01:06:42,163
then we just replace whatever is in that display, right? So,
那就把之前的数字替换掉,对吗?
1323
01:06:42,165 --> 01:06:46,368
I'm going to add a new var, okay, a new instance variable,
我要加一个变量,实例变量
1324
01:06:46,370 --> 01:06:47,769
a new property, called,
一个新的属性
1325
01:06:47,771 --> 01:06:51,706
userIsInTheMiddleOfTyping. >> [LAUGH]
叫做 userIsInTheMiddleOfTyping(笑
1326
01:06:51,708 --> 01:06:52,474
>> Okay, now you laugh,
我知道你们为什么笑
1327
01:06:52,476 --> 01:06:54,609
you laugh because that seems like a lot of typing, but
你们笑是因为这个变量名字太长了
1328
01:06:54,611 --> 01:06:57,479
I will never have to type that again. Okay?
但我不会再做第二遍
1329
01:06:57,481 --> 01:06:59,948
Because Xcode is always gonna escape complete things for
因为 Xcode 总会帮我自动补全
1330
01:06:59,950 --> 01:07:02,751
me. So, long names like that are actually good.
所以长变量名挺好的
1331
01:07:02,753 --> 01:07:05,286
Okay, you wanna pick the shortest name that, you know,
你可以选一个短一点的名字
1332
01:07:05,288 --> 01:07:08,123
it conveys what you want but you want your names,
这名字要能体现出你想要什么
1333
01:07:08,125 --> 01:07:10,258
as you know in computer science naming is everything,
计算机科学里命名就是一切,你懂的
1334
01:07:10,260 --> 01:07:14,429
you want your names to be very, very, very good for
你希望你的命名对读你代码的人
1335
01:07:14,431 --> 01:07:17,432
the reader to understand what's going on, okay? Now,
非常非常非常的好理解
1336
01:07:17,434 --> 01:07:20,702
this userIsInTheMiddleOfTyping is a Bool, okay?
userIsInTheMiddleOfTyping 是个 Bool
1337
01:07:20,704 --> 01:07:21,603
So, I'm giving you it's type,
我给它一个 Bool 类型
1338
01:07:21,605 --> 01:07:23,905
Bool. It's not an optional Bool. It's just a Bool. So
这不是个 optional Bool,就是个 Bool
1339
01:07:23,907 --> 01:07:27,442
no question mark here. Okay, it's either true or false.
所以这里没有问候,这里无论 true 还是 false
1340
01:07:27,444 --> 01:07:28,777
I'm either in the middle of typing or I'm not.
我不是在输入中或没输入
1341
01:07:28,779 --> 01:07:32,313
Now as soon as I put that in there I got an error.
一旦我把这些放在这,就报错了
1342
01:07:32,315 --> 01:07:34,282
See that error up there? What? That's not fair.
看到上面那个报错符合了吗?这不科学啊
1343
01:07:34,284 --> 01:07:38,753
I didn't do anything up there. What is that? Let's click it.
我在上面什么都没做啊,那是什么?点一下看看
1344
01:07:38,755 --> 01:07:41,456
Class ViewController has no initializers.
Class ViewController has no initializers
1345
01:07:41,458 --> 01:07:43,725
Okay, now you're doing your homework and you're like,
这里如果你们正在做作业
1346
01:07:43,727 --> 01:07:46,161
what the, there's nothing to do, what I just did.
你会发现,我们什么都没做
1347
01:07:46,163 --> 01:07:48,496
I just typed in var here, what does that mean? Okay,
我只是刚才声明了一个变量,这是什么意思?
1348
01:07:48,498 --> 01:07:51,366
well the reason you're getting that error is cuz in Swift,
你在这报错的原因是
1349
01:07:51,368 --> 01:07:55,336
all properties, every single one, have to have an initial
在 Swift 里,所以的属性,每一个,都必须有一个初始化的值
1350
01:07:55,338 --> 01:07:59,340
value. There's no, you are not allowed to have
如果你不这么做
1351
01:07:59,342 --> 01:08:02,710
any properties that don't have an initial value. Okay?
就不能有一个未初始化的变量
1352
01:08:02,712 --> 01:08:04,612
So how do we give this thing an initial value?
所以我们怎么给它一个初始值?
1353
01:08:04,614 --> 01:08:07,715
Well, there's two ways. One, we create an initializer.
有两种方式,一种,对它进行初始化
1354
01:08:07,717 --> 01:08:08,683
That's why you're getting this warning.
这就是为什么会报错
1355
01:08:08,685 --> 01:08:11,486
Now I'm not gonna talk about initializers til next week. So
但下周前我不会讲怎么初始化
1356
01:08:11,488 --> 01:08:13,822
we can't do that because I'm not gonna talk about it yet,
所以我们不能做,因为我还没讲
1357
01:08:13,824 --> 01:08:16,324
okay? What's the other way that we can do it?
所以另一种是什么方式呢?
1358
01:08:16,326 --> 01:08:21,729
We can just say = false. Okay, so we just initialize that,
我们直接给它赋一个 false 值,我们这就对它初始化了
1359
01:08:21,731 --> 01:08:24,933
and the write error goes away. Woo hoo, were winning, and
那个报错就消失了~
1360
01:08:24,935 --> 01:08:26,067
I know some of you are saying, yeah,
我知道你们可能会说
1361
01:08:26,069 --> 01:08:30,839
what about this one? Okay, you didn't initialize that. Okay,
那么这个呢?你没对它初始化啊
1362
01:08:30,841 --> 01:08:33,741
that's because optionals are treated specially.
因为 optional 是很特殊的
1363
01:08:33,743 --> 01:08:39,047
They are always automatically initialized to not set.
他们会自动被初始化为空
1364
01:08:39,049 --> 01:08:42,550
Okay? So this is basically always happening
这就是你申明一个 optional 的时候会发生的
1365
01:08:42,552 --> 01:08:44,686
when you have an optional, or var that's an optional.
或者是一个 var 的 optional
1366
01:08:44,688 --> 01:08:46,287
Which makes sense, right? You have an optional.
能理解吗?你有一个 optional
1367
01:08:46,289 --> 01:08:47,589
It's not set until you set it to something.
它没有被赋值,直到你给它赋一个值
1368
01:08:47,591 --> 01:08:51,960
So it starts out not set. Okay, so that's why it is set.
所以它会以没有赋值开始。这就是为什么它被赋值了
1369
01:08:51,962 --> 01:08:55,630
It's set to not set. Okay? All right, so
它被赋值为没有赋值
1370
01:08:55,632 --> 01:08:57,665
we have this thing now, we have this. Let's go ahead and
所以我们现在有了这个,继续
1371
01:08:57,667 --> 01:09:00,635
use this thing we created. Actually, I'm going to do one
所有现在用我们刚刚创建的这个
1372
01:09:00,637 --> 01:09:05,940
more thing here. You see this : Bool? Do we need that? No.
我还要在这多做一件事。: Bool 我们真的需要这个吗?不
1373
01:09:05,942 --> 01:09:09,611
Okay, why don't we need that? Because false can only be
为什么我们不需要这个呢?
1374
01:09:09,613 --> 01:09:15,750
a Bool, so Swift can infer that the type of this is Bool.
因为false 只能是个Bool 型。所以Swift 可以推导它就是个Bool 型
1375
01:09:15,886 --> 01:09:19,821
See that? Okay? All right. So, we have this
看到了吗?所以我们现在有了 userIsInTheMiddleOfTyping
1376
01:09:19,823 --> 01:09:23,057
userIsInTheMiddleOfTyping. We only wanna do this business
我们只想它处理
1377
01:09:23,059 --> 01:09:25,493
right here if the user is in the middle of typing.
用户是不是正在输入中的问题
1378
01:09:25,495 --> 01:09:26,995
So now we're gonna see in if statement.
所以我接下来输入
1379
01:09:26,997 --> 01:09:28,263
If the user is in the middle of typing,
If userIsInTheMiddleOfTyping
1380
01:09:28,265 --> 01:09:31,232
now again, I'm just hitting Tab here. Look Mom, no hands,
我按下 Tab 键,这一串就输入好了。
1381
01:09:31,234 --> 01:09:34,302
or one hand. If the user is in the middle of typing,
If userIsInTheMiddleOfTyping
1382
01:09:34,304 --> 01:09:36,938
then we're gonna do this thing. Okay.
然后我们做这些
1383
01:09:36,940 --> 01:09:40,742
If the user's not in the middle of typing,
如果用户不是在输入中
1384
01:09:40,744 --> 01:09:43,378
then we're gonna set the displays text
那么就让 display 的文本
1385
01:09:43,380 --> 01:09:46,481
equal to the digit we just typed. Okay, the five, or
等于 digit 的数字。比如 5,或者别的什么
1386
01:09:46,483 --> 01:09:49,417
whatever, we're gonna replace what's in the display. And,
我们会替换 display 里的文本
1387
01:09:49,419 --> 01:09:52,921
of course, in either case, the user is definitely, no long,
当然,另一种情况,用户
1388
01:09:52,923 --> 01:09:56,024
is definitely, now, in the middle of typing. Okay,
一定是在输入中
1389
01:09:56,026 --> 01:09:59,561
cuz you just touched a digit, we're clearly in the middle of
因为你刚刚按了输入按钮,我们显然是在输入中
1390
01:09:59,563 --> 01:10:03,097
typing. Now I'm getting pretty tired of typing display
我现在真的对每次都
1391
01:10:03,099 --> 01:10:05,466
exclamation point all the time, okay?
输入display! 感到疲惫
1392
01:10:05,468 --> 01:10:08,303
All the time typing display exclamation point, when in
每次都输入 display! ,实际上
1393
01:10:08,305 --> 01:10:12,307
fact, I know that that display is pretty much always set.
我知道 display 一定是被赋值的状态
1394
01:10:12,309 --> 01:10:14,776
Okay, the only time that this is not set is for
它唯一没被赋值的时间就是
1395
01:10:14,778 --> 01:10:18,546
a nanosecond when this starts to come on screen,
当他一出现在屏幕上的时候
1396
01:10:18,548 --> 01:10:22,650
before iOS has a chance to wire it up to this UILabel.
在 iOS 把他变成 UILabel 类型之前
1397
01:10:22,652 --> 01:10:25,119
But once it's wired up, it's set forever. And
一旦他被连接上,那就永远被赋值了
1398
01:10:25,121 --> 01:10:27,589
certainly by the time users are touching on digits,
而且很确定的是,当用户点在数字上时
1399
01:10:27,591 --> 01:10:31,226
the UIs completely on screen, and wired up, and so I know
UI 已经在屏幕上创建好、链接好了,
1400
01:10:31,228 --> 01:10:36,097
that's never going to be nil. Never, okay, in this code. So
所以我知道它用不会为空
1401
01:10:36,099 --> 01:10:38,199
why am I forced to type display, exclamation point,
所以我为什么非要
1402
01:10:38,201 --> 01:10:39,801
display, exclamation point, over and over, okay?
重复输入 display! 呢?
1403
01:10:39,803 --> 01:10:42,670
And the answer is you don't have to because we're
答案是你不需要,
1404
01:10:42,672 --> 01:10:46,007
gonna go back and change this question mark to be
我们只要把一开始这个问号
1405
01:10:46,009 --> 01:10:48,910
exclamation point. Basically if you put the exclamation
改成感叹号。总的来说,
1406
01:10:48,912 --> 01:10:53,481
point where you declare it, it says that anyone can use
在你定义的时候放个感叹号,就表示所有对象都可以用这个
1407
01:10:53,483 --> 01:10:57,252
this thing, and it will implicitly unwrap it, okay?
就 implicitly unwarp(注:隐式解包)了这个对象
1408
01:10:57,254 --> 01:10:57,986
It will automatically unwrap it.
这就会自动 unwarp
1409
01:10:57,988 --> 01:10:59,988
Now its still unwrapping it, so if that was nil,
现在它还是会 unwarp,如果他是 nil
1410
01:10:59,990 --> 01:11:04,292
it will still crash, okay? But it allows me to get rid of all
它还是会让你的应用崩溃。但是它允许我
1411
01:11:04,294 --> 01:11:08,529
of these exclamation points. Okay? Because it's implicit
去掉这里所有的感叹号
1412
01:11:08,531 --> 01:11:10,365
that there's an exclamation point on all of them,
因为这里的感叹号 implicit unwarp(隐式解包)了所有的对象
1413
01:11:10,367 --> 01:11:12,200
because instead of putting a question mark here,
因为我在这放了个感叹号
1414
01:11:12,202 --> 01:11:13,468
I put the exclamation point up there.
而不是问号
1415
01:11:13,470 --> 01:11:15,837
Everyone got that? >> [INAUDIBLE].
都懂了吗
1416
01:11:15,839 --> 01:11:16,471
>> It would crash down here,
可能会在下面这里崩溃
1417
01:11:16,473 --> 01:11:18,906
in this code down here. As soon as you access this,
这一块代码,一旦你跑到这
1418
01:11:18,908 --> 01:11:21,709
if that were nil, boom, it would be unwrapping it,
如果是个 nil,就会 unwarp
1419
01:11:21,711 --> 01:11:24,445
cuz it's implicitly unwrapped >> And again,
因为它 implicitly unwarp 了
1420
01:11:24,447 --> 01:11:25,980
if I change this back to a question mark,
如果我把它改回问号
1421
01:11:25,982 --> 01:11:28,416
now I'm gonna get all kinds of warnings down here.
现在就会出现好多警告、错误
1422
01:11:28,418 --> 01:11:30,485
See errors, because I'm not unwrapping it, but
因为我没有 unwarp 它
1423
01:11:30,487 --> 01:11:31,719
if I put the exclamation point,
如果我在这放一个感叹号
1424
01:11:31,721 --> 01:11:33,821
now I'm in implicitly unwrapping it everywhere.
这样我就在所有地方 implicitly unwarp 了
1425
01:11:33,823 --> 01:11:36,658
Okay, that's called an implicitly unwrap optional.
这样就可以叫它 implicitly unwarp 的 optional
1426
01:11:36,660 --> 01:11:39,694
Very important,
非常重要
1427
01:11:39,696 --> 01:11:43,765
Okay, so now let's go see if that fixed everything?
我们现在看看它是不是修复了所有问题
1428
01:11:47,404 --> 01:11:47,602
>> All right, so
所以
1429
01:11:47,604 --> 01:11:48,536
now hopefully when we press five,
当我们按下5
1430
01:11:48,538 --> 01:11:50,772
it will get rid of the zero. Five. There it is, and
就会去掉 0 显示 5
1431
01:11:50,774 --> 01:11:53,808
this is still working. Okay? So that's good. We're
我们做的很好
1432
01:11:53,810 --> 01:11:56,778
doing great. Our keyboard is basically fully functional at
这下我们的键盘功能完全了
1433
01:11:56,780 --> 01:11:59,147
this point. So now we want to add a,
现在要加一个操作
1434
01:11:59,149 --> 01:12:02,216
an operation. Now, because of our technical thing,
因为技术上的原因
1435
01:12:02,218 --> 01:12:04,319
we're running out of time, but I have 3 minutes left.
我们快没时间了,但还有三分钟
1436
01:12:04,321 --> 01:12:07,021
I'm gonna add the pi button. Okay, pi is a very
我要加一个 π 按钮
1437
01:12:07,023 --> 01:12:10,024
simple operation. It's just gonna put pi in the display.
π 是个很简单的操作。只是放个 π 在 display 上
1438
01:12:10,026 --> 01:12:13,461
So let's do that. I'm gonna do that by taking the 7 button
来做这个吧。我要选中 7 这个按钮
1439
01:12:13,463 --> 01:12:14,362
and copying and pasting it.
复制粘贴它
1440
01:12:14,364 --> 01:12:17,665
That's a really bad idea, as you'll see in a moment. But,
现在看来这很不好
1441
01:12:17,667 --> 01:12:20,768
I'm just going to do that and call it pi, okay? Now,
但我就这么做了,叫它 π
1442
01:12:20,770 --> 01:12:24,706
of course, pi is not a digit button, it's an operation, so
当然,π 不是个数字,是一个操作
1443
01:12:24,708 --> 01:12:30,044
I'm going to Ctrl drag down here to create another action,
我要按住 ctrl 键拖到这里,创建另外一个 action
1444
01:12:30,046 --> 01:12:30,878
a different action.
一个不同的 action
1445
01:12:30,880 --> 01:12:34,082
This action is performOperation because pi
这是个 performOperation 因为 π 是个操作
1446
01:12:34,084 --> 01:12:36,517
is an operation. I'm gonna have all my operation buttons
我会把所有操作的按钮叫做 performOperation
1447
01:12:36,519 --> 01:12:39,721
called performOperation. And of course I'm not gonna forget
当然我不会忘了
1448
01:12:39,723 --> 01:12:41,289
to change that to UI button, okay,
把它换成 UIButton
1449
01:12:41,291 --> 01:12:43,491
either are you on your homework, right? Okay?
你们做作业的时候也别忘了
1450
01:12:43,493 --> 01:12:47,095
And here it is, so there's perform operation right there,
所以 performOperation 就在这了
1451
01:12:47,097 --> 01:12:50,064
okay. And perform operation, I'm gonna do the exact same
对 performOperation 我要做跟
1452
01:12:50,066 --> 01:12:52,734
thing that I did at the beginning of touch digit,
之前按 digit 一样的事情
1453
01:12:52,736 --> 01:12:55,002
which is I'm gonna ask the sender which
要问 sender
1454
01:12:55,004 --> 01:13:00,208
operation are you? So I'm gonna let mathematicalSymbol =
你是哪个操作
1455
01:13:00,210 --> 01:13:05,580
the sender.currentTitle. Now of course
let mathematicalSymbol = sender.currentTitle
1456
01:13:05,582 --> 01:13:09,083
I want the string so I could do the exclamation point,
我需要 string,我可以用感叹号
1457
01:13:09,085 --> 01:13:13,321
okay? But if this were blank, what if I just wanted to
如果这是空的,如果我只想忽略
1458
01:13:13,323 --> 01:13:17,658
ignore that operation press? Okay, instead of crashing.
这个按下的动作呢?而不是让它崩溃
1459
01:13:17,660 --> 01:13:20,027
Okay, what if it was legal to have that
如果让它为空是合法的
1460
01:13:20,029 --> 01:13:24,165
be blank. Okay? I basically want to unwrap
我只想它被赋值的时候 unwarp
1461
01:13:24,167 --> 01:13:29,036
only if this is set. So the way you do that is
你做这个的方式
1462
01:13:29,038 --> 01:13:32,240
you don't put the exclamation point there. Instead,
就是不要在这放个感叹号
1463
01:13:32,242 --> 01:13:37,412
you put if at the beginning. And what's nice about this is
如果你在前面放个 if
1464
01:13:37,414 --> 01:13:42,116
very little typing but also look how it reads, okay? If I
这很好,也看看这是怎么写的。
1465
01:13:42,118 --> 01:13:47,422
can let mathematicalSymbol = sender .currentTitle, then I'm
if let mathematicalSymbol = sender.currentTitle
1466
01:13:47,424 --> 01:13:50,458
gonna do something. Okay, so it reads just like English,
然后再做一些什么。读起来像是英文
1467
01:13:50,460 --> 01:13:54,462
it's a really nice way to unwrap. So inside this if,
这是 unwarp 的很好的方式
1468
01:13:54,464 --> 01:13:57,064
this will be a string. It'll be the associated
在这个 if 里,这会是个 string
1469
01:13:57,066 --> 01:14:00,701
value of currenttype, Title. Outside of this if,
这会是 currentTitle 的关联值
1470
01:14:00,703 --> 01:14:01,302
it's not even defined.
if 之外,它甚至没有被定义
1471
01:14:01,304 --> 01:14:04,071
mathematicalSymbol won't even be defined. Okay? Super
mathematicalSymbol 甚至没被定义
1472
01:14:04,073 --> 01:14:10,011
simple. So here I can just say if mathematicalSymbol = pi,
超简单,所以我们只要 let mathematicalSymbol == π
1473
01:14:10,013 --> 01:14:14,782
by the way Alt + P to make pi, okay, then I can set my
按alt + P 就是π ,然后设置display
1474
01:14:14,784 --> 01:14:18,619
displays. I don't even need the exclamation point anymore.
我甚至不再需要感叹号
1475
01:14:18,621 --> 01:14:22,924
Text = pi, which _pi is pi if we
Text = M_PI
1476
01:14:22,926 --> 01:14:27,762
Alt click on this. This is a symbol. See? M under bar PI.
按 alt 点它。这是个符号。M_PI
1477
01:14:27,764 --> 01:14:30,865
It's a double. It's a double vision value of PI. Now,
是个 double。这是个 double 类型的 π 值
1478
01:14:30,867 --> 01:14:33,301
we have an error here. Why do we have an error?
这里报错了,为什么呢?
1479
01:14:33,303 --> 01:14:36,504
Because it says it cannot assign a type double to
因为不能把一个 double 赋值给
1480
01:14:36,506 --> 01:14:39,073
something that is a string question mark, right?
一个 string?
1481
01:14:39,075 --> 01:14:42,577
We know the display.text right here is a string question mark
我们知道这是个 display.text 是个 string?
1482
01:14:42,579 --> 01:14:45,780
so we can't do that. So, how do we convert this double to
我们不能这么做,所以我们怎么把这个 double
1483
01:14:45,782 --> 01:14:49,317
a string? And the answer is we have to create a new string.
转换成一个 string?答案是新建一个 string
1484
01:14:49,319 --> 01:14:51,886
Okay, and now you're gonna see how to create new
这样你会看到我们如何
1485
01:14:51,888 --> 01:14:56,290
instances of objects in Swift, and the answer is you just put
在 Swift 里新建一个实例。答案是
1486
01:14:56,292 --> 01:14:59,694
the name of the class and then parentheses and then inside
打出这个类型名,然后把
1487
01:14:59,696 --> 01:15:02,263
the parentheses you can put anything that the class
任何允许你转换的值
1488
01:15:02,265 --> 01:15:06,501
will allow you to create one of itself with. Okay, and
放在括号里
1489
01:15:06,503 --> 01:15:07,702
you can have multiple of these.
你可以多次创建
1490
01:15:07,704 --> 01:15:10,538
These are the initializers referred to earlier. And
这是个早前的初始化方式
1491
01:15:10,540 --> 01:15:14,141
string happens to be able to take a double as one of
string 可以把一个 double
1492
01:15:14,143 --> 01:15:16,444
the things that it knows how to create a new string. So
创建成一个新的 string
1493
01:15:16,446 --> 01:15:19,947
this creates a new string, okay, from a double. So
所以它从一个 double 创建一个新的 string
1494
01:15:19,949 --> 01:15:22,683
the string class knows how to look at a double, turn it into
string 类知道如何把一个 double
1495
01:15:22,685 --> 01:15:27,455
a bunch of characters that are a double, okay? So
转换成一个字符串
1496
01:15:27,457 --> 01:15:34,161
we'll run this. Okay,
所以再跑一下
1497
01:15:34,163 --> 01:15:37,732
so here we go, this is still working and if we press Pi,
这还是好用的,当我们按下 π
1498
01:15:37,734 --> 01:15:40,568
we get Pi [SOUND], we got a problem, okay?
我们得到 π 值,但有个问题
1499
01:15:40,570 --> 01:15:41,836
It's not fitting the whole Pi,
没有显示全 π
1500
01:15:41,838 --> 01:15:43,971
all the digits. We can fix that really easily.
我们可以很简单的修复一下
1501
01:15:43,973 --> 01:15:47,475
I'm gonna go back to here. And if we look at the inspector,
我们回到这里,看一下 inspector
1502
01:15:47,477 --> 01:15:51,245
there's a nice feature in label called autoshrink.
有个非常好的 UILabel 特性叫 autoshrink
1503
01:15:51,247 --> 01:15:53,881
And I'm gonna make it so it autoshrinks but
我会让这个 autoshrink
1504
01:15:53,883 --> 01:15:59,320
no smaller than 9 point, okay. That's gonna fix that one.
不小于 9 点。这就会修复
1505
01:15:59,322 --> 01:16:02,757
We have one more problem. Okay, so we have,
我们还有个问题
1506
01:16:02,759 --> 01:16:05,960
this is working, pi, what's that?
这个 π 又是啥
1507
01:16:05,962 --> 01:16:09,196
We got a little pi on the end there and also look at this,
我们在最后有一个 π,还有,看这个
1508
01:16:09,198 --> 01:16:12,600
that, what a mess. So we got two problems there. For
这很不好。我们现在这里有两个问题了
1509
01:16:12,602 --> 01:16:15,436
some reason when we press pi, it puts pi on the end.
因为一些原因,当我们按 π,最后有个 π 在末尾
1510
01:16:15,438 --> 01:16:19,173
Well,why is that? Okay. That's because, let's look at
为什么呢?因为看这个
1511
01:16:19,175 --> 01:16:22,276
this little thing. Okay. Pi is sending that, right?
π 会发送这个
1512
01:16:22,278 --> 01:16:26,480
How about this one? Pi is sending that too.
这个呢?π 也会发送这个
1513
01:16:26,482 --> 01:16:31,252
You see, it's sending touch digit and perform operation.
它同时响应两个方法
1514
01:16:31,254 --> 01:16:33,754
That's because I copied and pasted the 7
因为我复制黏贴了7
1515
01:16:33,756 --> 01:16:37,358
which sent touch digit and then I added this one. Okay,
所以也发送上面的这个方法
1516
01:16:37,360 --> 01:16:40,061
now how do we fix that? You fix that by right-clicking on
那我怎么修复呢?
1517
01:16:40,063 --> 01:16:42,663
the button. If you right-click on anything in your UI, you'll
可以右击这个按钮
1518
01:16:42,665 --> 01:16:45,266
see all of the connections that it has. Very important to
可以看到所有按钮关联的东西
1519
01:16:45,268 --> 01:16:47,668
understand right-click, okay. So you right-click and
理解右击很重要
1520
01:16:47,670 --> 01:16:49,203
you can see it only has two connections here.
右击,你可以在这看到两个链接
1521
01:16:49,205 --> 01:16:52,239
It's connected to touch digit and to perform operation, so
这链接了两个方法
1522
01:16:52,241 --> 01:16:55,509
I'm just gonna get rid of the touch digit by clicking this,
我只是需要去掉 touchDigit
1523
01:16:55,511 --> 01:16:57,178
gone. Now it only does perform digit,
这样就只想响应下面这个方法了
1524
01:16:57,180 --> 01:17:00,715
or perform operation, okay. So we fixed that problem where
所以我们修复了
1525
01:17:00,717 --> 01:17:02,149
we're just putting pi on the end, all right.
这个末尾有π 的问题
1526
01:17:02,151 --> 01:17:05,453
And the other problem was that when we have the pi and
另一个问题是,当我们有π 的时候
1527
01:17:05,455 --> 01:17:05,987
I started typing numbers,
再按数字
1528
01:17:05,989 --> 01:17:07,922
it thought I was in the middle of typing a number and
会认为你在输入中
1529
01:17:07,924 --> 01:17:11,025
it was adding them on. So that's simple, I'm just gonna
会在末尾加数字,这很简单
1530
01:17:11,027 --> 01:17:14,228
say userIsInTheMiddleofTyping number = false.
userIsInTheMiddleofTyping = false
1531
01:17:14,230 --> 01:17:16,364
Okay, whenever we perform an operation,
无论我什么时候操作
1532
01:17:16,366 --> 01:17:19,767
it's gonna wipe out what's in my display so I'm obviously
就会去掉之前 display 上的东西
1533
01:17:19,769 --> 01:17:22,703
not in the middle of typing a number anymore and we have
因为我很显然不是在输入中
1534
01:17:22,705 --> 01:17:28,242
a fully functional calculator with one operation. 45 pi 89,
现在我有了个全功能的计算器按钮
1535
01:17:28,244 --> 01:17:32,780
okay? So on Wednesday, we'll pick up
周三,我们会继续
1536
01:17:32,782 --> 01:17:35,516
with some more, make a more powerful calculator. Sorry to
做个更强的计算器
1537
01:17:35,518 --> 01:17:38,486
run over a little bit because of technical difficulties,
抱歉,因为技术难题拖课了一会
1538
01:17:38,488 --> 01:17:40,054
see you then. >> For
下回见
1539
01:17:40,056 --> 01:17:40,087
more please visit us at stanford.edu.
更多内容详见:stanford.edu
================================================
FILE: subtitles/10. Core Data.srt
================================================
1
00:00:00,001 --> 00:00:03,469
[MUSIC]
2
00:00:03,471 --> 00:00:09,508
Stanford University. >> Welcome to Lecture Number
3
00:00:09,510 --> 00:00:13,746
10 of Stanford CS193P. It's the Spring of 2016.
4
00:00:13,748 --> 00:00:18,250
Today we have one topic only, which is Core Data, okay,
5
00:00:18,252 --> 00:00:22,221
which is an object oriented database. Sometimes when
6
00:00:22,223 --> 00:00:24,957
you're building an app you need to store a huge amount of
7
00:00:24,959 --> 00:00:27,926
data like you're doing some kind of twitter thing where
8
00:00:27,928 --> 00:00:29,294
you're collecting all the tweets and
9
00:00:29,296 --> 00:00:29,995
you've got all these tweets and
10
00:00:29,997 --> 00:00:31,030
you want to search through them and
11
00:00:31,032 --> 00:00:33,632
do some kind of queries or something like that. And
12
00:00:33,634 --> 00:00:36,735
of course there's lots of technologies out there for
13
00:00:36,737 --> 00:00:40,005
doing that kind of thing. SQL databases and
14
00:00:40,007 --> 00:00:42,274
things like that. But really when we're programming,
15
00:00:42,276 --> 00:00:45,778
we want obviously all of our stuff to be object oriented.
16
00:00:45,780 --> 00:00:49,648
So iOS has this awesome object oriented database system
17
00:00:49,650 --> 00:00:53,252
called Core Data. It's very powerful.
18
00:00:53,254 --> 00:00:55,020
It's actually based on SQL underneath so
19
00:00:55,022 --> 00:00:58,090
it's got all the full kind of power of that.
20
00:00:58,325 --> 00:01:01,393
And yet it interacts with your code just like all your other
21
00:01:01,395 --> 00:01:02,761
object oriented stuff that you do,
22
00:01:02,763 --> 00:01:04,997
especially if you're doing things like table and
23
00:01:04,999 --> 00:01:07,666
stuff like that that has a lot of good integration,
24
00:01:07,668 --> 00:01:10,269
with that. So it's essentially a way of
25
00:01:10,271 --> 00:01:14,039
creating an object graph that's backed by a database,
26
00:01:14,041 --> 00:01:14,840
a SQL database in this case.
27
00:01:14,842 --> 00:01:17,776
It can actually be backed by other kinds of databases
28
00:01:17,778 --> 00:01:19,678
like XML or just in memory stuff but
29
00:01:19,680 --> 00:01:23,015
primarily we're using Core Data with SQL as our,
30
00:01:23,017 --> 00:01:25,050
backing store. And the way it works,
31
00:01:25,052 --> 00:01:29,054
is you're going to create this visual mapping, okay? And
32
00:01:29,056 --> 00:01:32,024
then you're gonna create objects in your code. Visual
33
00:01:32,026 --> 00:01:33,892
mapping's gonna basically describe your database,
34
00:01:33,894 --> 00:01:36,061
then you're gonna create objects in your code,
35
00:01:36,063 --> 00:01:38,831
that are in that database. And you're gonna access
36
00:01:38,833 --> 00:01:41,800
the columns in the database, and the rows, and
37
00:01:41,802 --> 00:01:42,000
the columns and
38
00:01:42,002 --> 00:01:44,069
the tables if you want to think of it that way, but
39
00:01:44,071 --> 00:01:46,738
you can access the attributes just using vars.
40
00:01:46,740 --> 00:01:49,141
So you're gonna have objects and vars on them and
41
00:01:49,143 --> 00:01:52,211
those are gonna be stored in the database, okay.
42
00:01:52,213 --> 00:01:54,813
So I'm gonna walk you through all that. Let's start with
43
00:01:54,815 --> 00:01:57,916
creating that visual mapping of the database, okay.
44
00:01:57,918 --> 00:02:01,253
Now just like anything, that you do when you're
45
00:02:01,255 --> 00:02:04,189
adding a new file to x code, whether it's a swift file or
46
00:02:04,191 --> 00:02:07,593
something like that, you're going to start with new file.
47
00:02:07,595 --> 00:02:08,527
But in this case, we're not going
48
00:02:08,529 --> 00:02:11,196
to be adding a code file, we're actually going to be
49
00:02:11,198 --> 00:02:15,134
adding a database mapping file. So, you say new file,
50
00:02:15,136 --> 00:02:18,570
and you're going to go down to iOS Core Data, okay? You see
51
00:02:18,572 --> 00:02:23,075
where it says Core Data down there instead of, iOS source,
52
00:02:23,077 --> 00:02:25,611
and inside of there, you're gonna pick data model.
53
00:02:25,613 --> 00:02:30,115
Don't pick mapping model, you want data model, okay? And
54
00:02:30,117 --> 00:02:32,818
it's gonna ask you what you wanna call this mapping.
55
00:02:32,820 --> 00:02:33,986
Okay, this mapping, by the way,
56
00:02:33,988 --> 00:02:37,089
what we're mapping between is these objects that we're gonna
57
00:02:37,091 --> 00:02:40,692
describe in this database and what's gonna be in our code.
58
00:02:40,694 --> 00:02:44,796
And usually if you only have one database in your app
59
00:02:44,798 --> 00:02:47,599
you'll call it model, because it's usually the model for
60
00:02:47,601 --> 00:02:49,601
a lot of your view controllers, but you could
61
00:02:49,603 --> 00:02:52,571
also call it the name or your app or if you had multiple of
62
00:02:52,573 --> 00:02:55,307
them you could kind of name them appropriately, right, so
63
00:02:55,309 --> 00:02:59,845
any name will, is fine here. When you create it,
64
00:02:59,847 --> 00:03:03,415
looks like this, okay? It's called Model.xcdatamodeld
65
00:03:03,417 --> 00:03:07,319
right here. And this is the contents of it, right?
66
00:03:07,321 --> 00:03:10,022
We're looking at the contents of it. And,
67
00:03:10,024 --> 00:03:14,026
it's kind of like a storyboard for the database, okay?
68
00:03:14,028 --> 00:03:14,626
You're gonna graphically,
69
00:03:14,628 --> 00:03:19,198
visually edit your database schema right here. So
70
00:03:19,200 --> 00:03:21,934
the database lets you store things obviously.
71
00:03:21,936 --> 00:03:25,003
The main things that you're storing are called entities,
72
00:03:25,005 --> 00:03:28,407
okay? And they're kinda analogous to objects,
73
00:03:28,409 --> 00:03:31,076
all right? So here I'm gonna create an entity so
74
00:03:31,078 --> 00:03:33,645
I'm gonna click on this add entity button at the bottom.
75
00:03:33,647 --> 00:03:37,249
And just say add entity. When I do, it creates one up here.
76
00:03:37,251 --> 00:03:39,218
And I'm gonna change the name of it to Tweet.
77
00:03:39,220 --> 00:03:42,321
So for my examples today, let's say we're building
78
00:03:42,323 --> 00:03:44,523
a database that's storing some Twitter stuff.
79
00:03:44,525 --> 00:03:45,524
We're all in kinda Twitter mode
80
00:03:45,526 --> 00:03:48,660
right now with your assignment stuff. So we'll store some
81
00:03:48,662 --> 00:03:54,333
tweets and some Twitter users into this database, okay? Now
82
00:03:54,335 --> 00:03:57,769
this entity Tweet that we're gonna store in the database,
83
00:03:57,771 --> 00:04:01,673
they're gonna appear in our code as NSManagedObject
84
00:04:01,675 --> 00:04:05,978
instances. So there's a class in iOS NSManagedObject, okay.
85
00:04:05,980 --> 00:04:09,381
And so all these entities will appear in our code as
86
00:04:09,383 --> 00:04:14,953
NSManagedObject, okay. All right so in addition
87
00:04:14,955 --> 00:04:19,024
to entities these entities have attributes. So those
88
00:04:19,026 --> 00:04:21,727
are kind of like properties on a class right there.
89
00:04:21,729 --> 00:04:25,030
They have relationships. Relationships are kind of like
90
00:04:25,032 --> 00:04:29,668
vars that point to other objects in the database, okay.
91
00:04:29,670 --> 00:04:32,337
And then there also have this thing down here called fetch
92
00:04:32,339 --> 00:04:35,340
properties. Which I'm not gonna really talk about but
93
00:04:35,342 --> 00:04:36,708
kind of the name implies what they are.
94
00:04:36,710 --> 00:04:40,045
They're essentially properties that are, that basically
95
00:04:40,047 --> 00:04:43,382
represent fetching in the database. So pulling objects
96
00:04:43,384 --> 00:04:46,018
out that meet some query or something like that. You can
97
00:04:46,020 --> 00:04:48,353
make it look like that's just a property on you're entity.
98
00:04:48,355 --> 00:04:50,422
Unfortunately, don't have time to talk about that.
99
00:04:50,424 --> 00:04:53,525
We're gonna focus here on these attributes and
100
00:04:53,527 --> 00:04:55,560
relationships, okay?
101
00:04:55,562 --> 00:04:58,897
So to add an attribute, we're going to hit this little plus
102
00:04:58,899 --> 00:05:00,399
sign right here under the attributes and
103
00:05:00,401 --> 00:05:03,502
it's going to add one. Here, I'm doing a Tweet, right,
104
00:05:03,504 --> 00:05:06,371
that's my entity as a Tweet, so I'm going to put the text
105
00:05:06,373 --> 00:05:10,342
of the Tweet. Notice as soon as I type, as I added this and
106
00:05:10,344 --> 00:05:12,444
typed it in, I get start getting an error right here.
107
00:05:12,446 --> 00:05:15,847
See, red error up in the, upper corner there, okay.
108
00:05:15,849 --> 00:05:17,816
And that's because this Tweet or
109
00:05:17,818 --> 00:05:21,053
this text attribute on the Tweet has no type, okay.
110
00:05:21,055 --> 00:05:24,089
Everything that goes in there obviously has to have a type,
111
00:05:24,091 --> 00:05:26,525
so you just click on this type to set it and
112
00:05:26,527 --> 00:05:30,896
I'm gonna set the text to be a string. Okay, so these are all
113
00:05:30,898 --> 00:05:34,566
the types that the attributes, the vars in your Tweet entity
114
00:05:34,568 --> 00:05:38,737
can be basically. All the ones like integers, decimal,
115
00:05:38,739 --> 00:05:42,541
double, float, even Boolean, those are actually stored as
116
00:05:42,543 --> 00:05:46,645
an NS number. Those appear in your code as an NS number.
117
00:05:46,647 --> 00:05:50,015
Okay, this binary data is in NS data.
118
00:05:50,017 --> 00:05:55,420
The date is an NSDate, and the string here is an NSString.
119
00:05:55,422 --> 00:05:58,690
Now remember that you get the automatic bridging, right?
120
00:05:58,692 --> 00:06:01,660
From NSString to string, things like that,
121
00:06:01,662 --> 00:06:04,363
even from double and int to NSNumbers, right?
122
00:06:04,365 --> 00:06:07,232
So, even though these are NSNumbers and NSString,
123
00:06:07,234 --> 00:06:09,601
you're still gonna get your Swift-like things when you're
124
00:06:09,603 --> 00:06:14,005
using them, in your code. Now, you can set any of these
125
00:06:14,007 --> 00:06:17,876
things, like this text attribute using these two
126
00:06:17,878 --> 00:06:21,480
methods right here on NSManagedObject, okay?
127
00:06:21,482 --> 00:06:25,217
Value for key which gets the value, and set value for
128
00:06:25,219 --> 00:06:28,720
key. Okay, so valueForKey returns any object,
129
00:06:28,722 --> 00:06:32,457
could be an NSNumber or an NSDate or an NSData, right.
130
00:06:32,459 --> 00:06:36,561
And setValue(forKey:) the key is a string like text and
131
00:06:36,563 --> 00:06:41,466
the value is, again, NSNumber, NSData, NSDate, whatever,
132
00:06:41,468 --> 00:06:45,203
okay? So that's how you can set the data on your object
133
00:06:45,205 --> 00:06:47,005
once you get a hold of one of these NSManagedObject.
134
00:06:47,007 --> 00:06:50,041
Now I haven't shown you how to get those NSManagedObject yet,
135
00:06:50,043 --> 00:06:51,209
but I will soon.
136
00:06:51,578 --> 00:06:54,813
See the error's gone because I set this to be a string. So,
137
00:06:54,815 --> 00:06:57,716
here's I'm going to add some more attributes, an ID,
138
00:06:57,718 --> 00:07:00,585
which is some unique identifier for this Tweet.
139
00:07:00,587 --> 00:07:03,588
Also created, which is the date the Tweet was created,
140
00:07:03,590 --> 00:07:06,758
you can see that's an NSDate right there. By the way,
141
00:07:06,760 --> 00:07:10,529
this ID you're probably going to want that in your homework.
142
00:07:10,531 --> 00:07:10,862
Just a little hint.
143
00:07:10,864 --> 00:07:13,665
I like to give you hints sometimes in the lecture.
144
00:07:13,667 --> 00:07:17,436
So you'll need your unique ID there at some point.
145
00:07:17,704 --> 00:07:20,906
We're looking at these attributes on this entity in
146
00:07:20,908 --> 00:07:24,943
kind of a table format here. But we can also do it in
147
00:07:24,945 --> 00:07:27,746
a graphical way by clicking on this little button down here,
148
00:07:27,748 --> 00:07:31,416
this editor style. It changes the style of this editor and
149
00:07:31,418 --> 00:07:34,719
it looks like this. So the graphical one basically has
150
00:07:34,721 --> 00:07:37,889
all of my entities and my attributes, but in kind of
151
00:07:37,891 --> 00:07:41,026
on a graph paper graphical format. And this is going to
152
00:07:41,028 --> 00:07:44,596
make sense when I start having a lot of relationships between
153
00:07:44,598 --> 00:07:45,263
entities, right.
154
00:07:45,265 --> 00:07:48,333
Then I'm gonna be able to see them here on the graph paper
155
00:07:48,335 --> 00:07:49,267
all pointing to each other so
156
00:07:49,269 --> 00:07:51,603
I can see what their relationships are. Okay, so
157
00:07:51,605 --> 00:07:55,140
let's add another entity in this. We can add entities and
158
00:07:55,142 --> 00:07:58,376
attributes in this view just as much as in the table view.
159
00:07:58,378 --> 00:08:00,645
So I'm gonna go down here and add another entity.
160
00:08:00,647 --> 00:08:04,549
This is gonna be a Twitter user, okay? And you can see it
161
00:08:04,551 --> 00:08:07,419
added the Twitter user here. It has no attributes or
162
00:08:07,421 --> 00:08:09,221
relationships yet. What's really cool,
163
00:08:09,223 --> 00:08:12,224
if you move these around and they do have wires pointing
164
00:08:12,226 --> 00:08:14,960
to each other, it'll rearrange the wires all over the place
165
00:08:14,962 --> 00:08:19,231
to look nice, okay? So that's kinda fun. I can also add
166
00:08:19,233 --> 00:08:20,832
attributes from here. So I'm just going down here and
167
00:08:20,834 --> 00:08:23,869
say add attribute to add an attribute to my Twitter user.
168
00:08:23,871 --> 00:08:27,305
I'm gonna call this attribute screen name, okay,
169
00:08:27,307 --> 00:08:30,442
that's like the at sign, whatever, screen name
170
00:08:30,444 --> 00:08:33,745
of the user. Notice that if I have this selected,
171
00:08:33,747 --> 00:08:37,716
then the Inspector over here can be used to inspect things
172
00:08:37,718 --> 00:08:41,753
about this attribute. So let's do that. Here's a screen name,
173
00:08:41,755 --> 00:08:43,688
its attributes. For example, here's its type.
174
00:08:43,690 --> 00:08:46,458
I'm going to say this type to be a string. It has some other
175
00:08:46,460 --> 00:08:48,860
attributes here which I'm not really going to have time to
176
00:08:48,862 --> 00:08:51,429
talk about. But you'll definitely, you can look up.
177
00:08:51,431 --> 00:08:53,765
You won't need it for any of your homework obviously, but
178
00:08:53,767 --> 00:08:55,066
for your final project you might. So
179
00:08:55,068 --> 00:08:58,670
you can look those up in the documentation for core data.
180
00:08:58,672 --> 00:09:02,207
Mkay I'm gonna add another attribute here which is name.
181
00:09:02,209 --> 00:09:03,341
So that's the user's real name.
182
00:09:03,343 --> 00:09:06,278
Okay not their at time whatever but their actual real
183
00:09:06,280 --> 00:09:09,714
name. So I'll add that. And now I'm gonna add
184
00:09:09,716 --> 00:09:13,685
a relationship between these two entities and of course we
185
00:09:13,687 --> 00:09:17,522
know that a Twitter user Is the one who tweets the tweet.
186
00:09:17,524 --> 00:09:21,259
Right? So, there's a relationship between this two.
187
00:09:21,261 --> 00:09:23,795
And to create a relationship between these two,
188
00:09:23,797 --> 00:09:25,764
I just Ctrl-drag from one to the other.
189
00:09:25,766 --> 00:09:27,699
And I can Ctrl-drag from either to the other.
190
00:09:27,701 --> 00:09:29,034
It really doesn't matter which direction.
191
00:09:29,036 --> 00:09:32,070
And when I do that, it's going to create
192
00:09:32,072 --> 00:09:36,608
a new thing over here called a relationship on both sides,
193
00:09:36,610 --> 00:09:38,577
it called it new relationship by default,
194
00:09:38,579 --> 00:09:41,112
we're going to change the name of that, okay, on both sides,
195
00:09:41,114 --> 00:09:44,049
and you can see it's got an arrow that points both ways.
196
00:09:44,051 --> 00:09:46,651
So now we know there's a relationship on both sides.
197
00:09:46,653 --> 00:09:49,287
Now, we want to rename these relationships
198
00:09:49,289 --> 00:09:51,756
just like the attributes have names that are meaningful,
199
00:09:51,758 --> 00:09:55,961
we want these to be meaningful names too. So the tweet okay,
200
00:09:55,963 --> 00:09:58,530
if you look at this relationship to this guy.
201
00:09:58,532 --> 00:10:01,466
This is the tweeter. Okay this is the tweeter for
202
00:10:01,468 --> 00:10:04,336
this tweet so I'm gonna call the relationship tweeter
203
00:10:04,338 --> 00:10:07,672
on this side. Okay I could probably call it user or
204
00:10:07,674 --> 00:10:10,842
something as well but tweeter is kind of fun.
205
00:10:10,844 --> 00:10:13,378
On the other side this is tweet. Okay.
206
00:10:13,380 --> 00:10:19,117
So this is the tweet that this user has tweeted. Okay.
207
00:10:19,119 --> 00:10:22,921
Now notice when you do this if you inspect either one,
208
00:10:22,923 --> 00:10:24,956
like if you inspect tweets right there,
209
00:10:24,958 --> 00:10:26,891
it's showing me that the inverse is tweeter.
210
00:10:26,893 --> 00:10:29,494
So it knows the inder-, inverse right there,
211
00:10:29,496 --> 00:10:32,197
okay. Now there's something different though about
212
00:10:32,199 --> 00:10:36,901
this tweets relationship to the tweeter relationship. Cuz,
213
00:10:36,903 --> 00:10:41,940
there are multiple tweets per Twitter user, right?
214
00:10:41,942 --> 00:10:45,210
So user could have tweeted hundreds of times. So
215
00:10:45,212 --> 00:10:46,911
this basically is multiple things,
216
00:10:46,913 --> 00:10:50,548
this is a to-many relationship to use database jargon there
217
00:10:50,550 --> 00:10:53,385
are many tweets per user even though there's only one user
218
00:10:53,387 --> 00:10:57,455
per tweet. And you define that up here in the inspector,
219
00:10:57,457 --> 00:10:58,423
if you have tweet selected.
220
00:10:58,425 --> 00:11:01,793
You go here to the type of relationship it is. And
221
00:11:01,795 --> 00:11:05,930
you can say it's a too many relationship. And when I do
222
00:11:05,932 --> 00:11:09,668
that notice I get this little double arrow right here. Okay?
223
00:11:09,670 --> 00:11:13,672
That's telling me that there's many tweets per Twitter user.
224
00:11:13,674 --> 00:11:16,241
Now, this is gonna show up differently in my code.
225
00:11:16,243 --> 00:11:19,678
This one right here is gonna show up in my code, okay,
226
00:11:19,680 --> 00:11:21,613
this tweet thing that I have, this
227
00:11:21,615 --> 00:11:24,115
tweet entity is gonna have a var, right here,
228
00:11:24,117 --> 00:11:28,386
called tweeter. It's gonna be of type NSManagedObject,
229
00:11:28,388 --> 00:11:31,289
because I told you all of these entities, this, and
230
00:11:31,291 --> 00:11:34,559
this, are gonna show up in your code as NSManagedObject,
231
00:11:34,561 --> 00:11:36,227
so that's gonna be the type of this var.
232
00:11:36,229 --> 00:11:42,233
The type of this var over here is gonna be ns set. Okay?
233
00:11:42,235 --> 00:11:45,136
And NSSet I can't remember if we talked about that earlier
234
00:11:45,138 --> 00:11:49,074
in the quarter but it's very similar to NSArray, okay?
235
00:11:49,076 --> 00:11:53,745
But it's unordered and unique. Ok, so an NS
236
00:11:53,747 --> 00:11:56,881
array can have the same object in it multiple times and
237
00:11:56,883 --> 00:11:59,684
an NS array is also in order. And in a set,
238
00:11:59,686 --> 00:12:02,120
it's just a bunch of objects in there, and
239
00:12:02,122 --> 00:12:05,256
if you add in the same object again, it would do nothing.
240
00:12:05,258 --> 00:12:06,991
Okay, so it's basically a unique set and
241
00:12:06,993 --> 00:12:10,895
there's no order to it's just a big jumble of them. Okay,
242
00:12:10,897 --> 00:12:14,132
and since these Tweets are basically a big jumble of
243
00:12:14,134 --> 00:12:17,168
tweets done by this user in no particular order, that's why
244
00:12:17,170 --> 00:12:23,208
it's an NS set. Now, there is in Swift a struck called set,
245
00:12:23,210 --> 00:12:27,178
and is automatically bridged just like NS array,
246
00:12:27,180 --> 00:12:28,813
automatically bridged to an array. Okay?
247
00:12:28,815 --> 00:12:31,382
So you can think of this as just being a set, but in fact,
248
00:12:31,384 --> 00:12:35,286
it's an NSSet. Okay? And what's in that set? Of course,
249
00:12:35,288 --> 00:12:39,190
ManageObject, NSManageObject, cuz these are in there. Okay?
250
00:12:39,192 --> 00:12:43,595
So it's exactly what you would think. All right? Now.
251
00:12:43,597 --> 00:12:46,464
There's lots of things you can do, okay, in core data.
252
00:12:46,466 --> 00:12:49,601
We're gonna focus on a small part of it, but
253
00:12:49,603 --> 00:12:51,770
the core of it, which is entities, attributes, and
254
00:12:51,772 --> 00:12:54,706
relationships, these things that I just told you about.
255
00:12:54,708 --> 00:12:58,176
Okay. How do you access all this stuff in the code?
256
00:12:58,178 --> 00:13:00,612
You need an instance of one of these.
257
00:13:00,614 --> 00:13:04,082
NSManagedObjectContext. You can think of that as kind of
258
00:13:04,084 --> 00:13:11,523
the window into the world of your database of objects,
259
00:13:11,525 --> 00:13:13,958
okay? So you need one of these to be able to do anything
260
00:13:13,960 --> 00:13:17,862
from the database. All right? So how do I get one of these?
261
00:13:17,864 --> 00:13:20,765
[LAUGH] Okay? I need one of these, how do I get it? Well,
262
00:13:20,767 --> 00:13:23,434
there's really two ways to get it, and you use them kind
263
00:13:23,436 --> 00:13:26,404
of about equally. One's not really necessarily preferred
264
00:13:26,406 --> 00:13:29,841
over the other. One of them is when you create your project,
265
00:13:29,843 --> 00:13:31,609
do you remember when we create a project there was a little
266
00:13:31,611 --> 00:13:34,078
button at the bottom, there's three switches at the bottom.
267
00:13:34,080 --> 00:13:37,949
One was You, are you gonna do testing or UI testing? And
268
00:13:37,951 --> 00:13:40,418
the other one on the top of the three buttons was
269
00:13:40,420 --> 00:13:44,656
I'm using Core Data. Do you remember that button? Anyway,
270
00:13:44,658 --> 00:13:45,857
it's there, I promise you. And
271
00:13:45,859 --> 00:13:49,861
if you click that button when you create your application,
272
00:13:49,863 --> 00:13:52,330
then you're going to get a bunch of code
273
00:13:52,332 --> 00:13:56,034
Provided to you.. One of which is a method that gives you one
274
00:13:56,036 --> 00:14:00,038
of these shared in your whole application. Okay, and
275
00:14:00,040 --> 00:14:02,941
I'm going to talk about how that works in a second.
276
00:14:02,943 --> 00:14:05,977
The second way you can get one of these little guys
277
00:14:05,979 --> 00:14:09,180
is to create a U-I managed document, okay?
278
00:14:09,182 --> 00:14:12,350
The U-I managed document has a var on it called managed
279
00:14:12,352 --> 00:14:16,421
object context, which will give you one of these. Okay?
280
00:14:16,423 --> 00:14:19,357
So, I'm going to talk about both of these mechanisms for,
281
00:14:19,359 --> 00:14:21,926
for getting this. All right. So, the first one,
282
00:14:21,928 --> 00:14:25,363
where you click the switch: When you flip that switch,
283
00:14:25,365 --> 00:14:28,366
you're going to, essentially get some code
284
00:14:28,368 --> 00:14:31,035
put in that AppDelegate.Swift file. Remember
285
00:14:31,037 --> 00:14:34,038
the AppDelegate.Swift? It's one of the files we always
286
00:14:34,040 --> 00:14:35,240
move in to supporting files.
287
00:14:35,242 --> 00:14:37,141
One of the first things I ever do, is okay,
288
00:14:37,143 --> 00:14:41,045
let's take image assets and AppDelegate and info.plist.
289
00:14:41,047 --> 00:14:43,114
Let's move them into a little folder called supporting
290
00:14:43,116 --> 00:14:45,550
files. Well, we haven't looked at that AppDelegate cuz
291
00:14:45,552 --> 00:14:49,420
there's really not much in there at this point but
292
00:14:49,422 --> 00:14:52,757
in there will be a bunch of code for core data, okay,
293
00:14:52,759 --> 00:14:56,127
if you flip that switch. Okay, and one of the things in that
294
00:14:56,129 --> 00:14:58,763
bunch of code is a method called managedObject,
295
00:14:58,765 --> 00:15:00,765
it's a var actually called managedObjectContext.
296
00:15:00,767 --> 00:15:04,102
And all you need to do is call that var and boom, you'll have
297
00:15:04,104 --> 00:15:05,837
a managedObjectContext to the database for
298
00:15:05,839 --> 00:15:09,440
this app. And App Delegate is kinda a global resource but
299
00:15:09,442 --> 00:15:11,542
your database is often a global resource, right?
300
00:15:11,544 --> 00:15:14,712
All your view controllers want to be able to see the data in
301
00:15:14,714 --> 00:15:17,348
the database, so it's reasonable. Now, the way you
302
00:15:17,350 --> 00:15:20,418
get at it, you get this AppDelegate. It's a little,
303
00:15:20,420 --> 00:15:22,687
it seems a little complicated, but it's not too bad.
304
00:15:22,689 --> 00:15:25,924
We're going to call this, this, class method on new
305
00:15:25,926 --> 00:15:29,260
application called Shared Application. And this returns
306
00:15:29,262 --> 00:15:33,331
you the one and only instance of UI application,
307
00:15:33,333 --> 00:15:36,467
which is just a class that represents your app.
308
00:15:36,469 --> 00:15:39,637
Ok now that object, UI application dot shared
309
00:15:39,639 --> 00:15:42,707
application, has a delegate. You all know what delegation
310
00:15:42,709 --> 00:15:47,278
is. It has a delegate. That delegate is that app delegate,
311
00:15:47,280 --> 00:15:51,282
the place where that manage object code got thrown.
312
00:15:51,284 --> 00:15:54,719
So you're going to cast it though as app delegate.
313
00:15:54,721 --> 00:15:57,689
You could do question mark here and then If it comes
314
00:15:57,691 --> 00:16:01,492
backs nil, I'm guess you're SOL you have any database, but
315
00:16:01,494 --> 00:16:04,896
here I'm gonna force cast it because if I can't get this
316
00:16:04,898 --> 00:16:08,066
managed object contacts. Then I can't do anything with my
317
00:16:08,068 --> 00:16:10,902
app, so I'm going to let it crash here for
318
00:16:10,904 --> 00:16:14,339
some reason my app delegate is not my delegate of
319
00:16:14,341 --> 00:16:17,241
my UI application. So anyway, you do this line of code and
320
00:16:17,243 --> 00:16:20,044
this is going to give you back your managed object context.
321
00:16:20,046 --> 00:16:22,046
So, now you have the portal that you need.
322
00:16:22,048 --> 00:16:24,782
To do all the database stuff. Okay? So that's one way to get
323
00:16:24,784 --> 00:16:28,286
this portal. The other way is UIManagedDocument. Okay,
324
00:16:28,288 --> 00:16:31,689
UIManagedDocument is a class it inherits from UIDocument
325
00:16:31,691 --> 00:16:35,059
and it's only job really is to encapsulate a core data
326
00:16:35,061 --> 00:16:38,129
database, that's what it does. It encapsulates a core data
327
00:16:38,131 --> 00:16:41,866
database, I mean, a file on disk Alright?
328
00:16:42,369 --> 00:16:45,036
And, the way you create a UIManagedDocument,
329
00:16:45,038 --> 00:16:47,305
UIManagedDocument is actually really easy. And you might
330
00:16:47,307 --> 00:16:50,274
ask, why would I ever do that other clicking the switch and
331
00:16:50,276 --> 00:16:53,044
the- creating my project in AppDelegate? All that.
332
00:16:53,046 --> 00:16:55,646
Why would I do that when I have this UIManagedDocument?
333
00:16:55,648 --> 00:16:58,549
I just create one and get the managed object context.
334
00:16:58,551 --> 00:17:02,120
Well, the reason it's a little more complicated here is
335
00:17:02,122 --> 00:17:04,355
because it does it's work asynchronously.
336
00:17:04,357 --> 00:17:07,558
You guys are only just getting used to asynchrony, so
337
00:17:07,560 --> 00:17:09,027
it might seem a little complicated to you. Once
338
00:17:09,029 --> 00:17:12,263
you're used to it it won't seem complicated at all, but
339
00:17:12,265 --> 00:17:13,998
when asynchrony is new to you,
340
00:17:14,000 --> 00:17:16,601
UIManagedDocument might seem a little
341
00:17:16,603 --> 00:17:19,570
Kind of how does that work? Here is how it works. Okay,
342
00:17:19,572 --> 00:17:23,241
here's a UIManagedDocument, I'm going to create it.
343
00:17:23,243 --> 00:17:26,811
First I'm going to get an NS File Manager here.
344
00:17:26,813 --> 00:17:29,447
This is a thing we haven't talked about yet, which lets
345
00:17:29,449 --> 00:17:32,150
you access the file system. And of course, I'm going to
346
00:17:32,152 --> 00:17:34,519
store my UIManagedDocument in the file system. So,
347
00:17:34,521 --> 00:17:37,722
I obviously need this little file manager guy right here.
348
00:17:37,724 --> 00:17:40,425
Then, I also need to know where to put my managed
349
00:17:40,427 --> 00:17:43,327
document. And I'm gonna put it in the documents
350
00:17:43,329 --> 00:17:47,632
directory for my app. So we're gonna learn later that
351
00:17:47,634 --> 00:17:48,733
your app has a documents directory,
352
00:17:48,735 --> 00:17:52,303
it has a caches directory, it has a temporary directory.
353
00:17:52,305 --> 00:17:55,740
It has all these kind of named magic directories
354
00:17:55,742 --> 00:17:59,143
where you put stuff, and the document directory right here
355
00:17:59,145 --> 00:18:02,447
is the one where you probably wanna put your users data.
356
00:18:02,449 --> 00:18:04,282
Okay, that's what that directory's really for.
357
00:18:04,284 --> 00:18:06,117
So you're gonna do this thing URLs for
358
00:18:06,119 --> 00:18:08,553
directory which returns the document directory and
359
00:18:08,555 --> 00:18:09,487
then you can get the first one.
360
00:18:09,489 --> 00:18:13,591
This returns an array of URLs like there might be multiple
361
00:18:13,593 --> 00:18:15,193
document directories out there.
362
00:18:15,195 --> 00:18:16,494
In iOS, there's always only one.
363
00:18:16,496 --> 00:18:19,764
If you're on the Mac, some of these directories can return
364
00:18:19,766 --> 00:18:21,632
multiple things because there might be one on the network,
365
00:18:21,634 --> 00:18:23,201
there might be one on your local machine, etc. But
366
00:18:23,203 --> 00:18:26,304
in iOS, there's always only the users, so it's always only
367
00:18:26,306 --> 00:18:29,574
one. So we just get the first one out of there. Okay now, I
368
00:18:29,576 --> 00:18:32,777
have the document's directory. Now I'm just going to append
369
00:18:32,779 --> 00:18:35,413
to the end of that, it's a URL. So, I'm gonna append
370
00:18:35,415 --> 00:18:38,516
to the end of that URL the name of the document.
371
00:18:38,518 --> 00:18:41,018
Okay, so the URL is pointing to the document's directory.
372
00:18:41,020 --> 00:18:44,689
If I append the name of the document now I've got a URL to
373
00:18:44,691 --> 00:18:47,658
the document. Now that I have that I can just create a new
374
00:18:47,660 --> 00:18:51,696
UIManagedDocument. Right? Just tell at the URL where it is.
375
00:18:51,698 --> 00:18:55,233
Boom, so now I have UIManagedDocument. Okay, and
376
00:18:55,235 --> 00:18:57,902
I could go get the managed object context from it,
377
00:18:57,904 --> 00:19:02,406
except that managed object context is no good unless it's
378
00:19:02,408 --> 00:19:06,677
open. Okay? So here's where the asynchrony comes in. Okay,
379
00:19:06,679 --> 00:19:09,480
we have to open this ui managed document okay?
380
00:19:09,482 --> 00:19:12,116
Usually we check first to see if it's already open.
381
00:19:12,118 --> 00:19:15,887
Okay, you can ask the document is your document state normal?
382
00:19:15,889 --> 00:19:18,055
That means it's already open and ready to go. So
383
00:19:18,057 --> 00:19:20,291
if that if it's in that state then you're good to go.
384
00:19:20,293 --> 00:19:23,494
You can just start using that managed object context thing,
385
00:19:23,496 --> 00:19:26,097
but it might be in this state closed right here,
386
00:19:26,099 --> 00:19:28,533
which means it has not yet been opened. In that case,
387
00:19:28,535 --> 00:19:31,469
you cannot use the managed object context yet. Now,
388
00:19:31,471 --> 00:19:32,803
what if it's closed? How do you open it?
389
00:19:32,805 --> 00:19:35,439
Well, you have to use this asynchronous method here.
390
00:19:35,441 --> 00:19:40,211
Okay? It's called open with completion handler, all right?
391
00:19:40,213 --> 00:19:42,813
First you can find out if the file exists right here by
392
00:19:42,815 --> 00:19:46,217
using this file manager thing, file exists at path. Okay,
393
00:19:46,219 --> 00:19:48,452
see if you Document's already there.
394
00:19:48,454 --> 00:19:50,254
If it does exist then you're gonna open it,
395
00:19:50,256 --> 00:19:51,622
by calling openWithCompletionHandler,
396
00:19:51,624 --> 00:19:54,091
if it doesn't exist you're gonna create it by using
397
00:19:54,093 --> 00:19:57,461
saveToURL. These are both methods in UIMangedDocument
398
00:19:57,463 --> 00:20:02,433
right? Notice that they take, both of them, a closure.
399
00:20:02,435 --> 00:20:06,737
See that? Okay? Why do they take that closure?
400
00:20:06,739 --> 00:20:09,840
That's because the open, or save, happens on another
401
00:20:09,842 --> 00:20:13,578
thread. Why is that? Well, because UIManageDocument is
402
00:20:13,580 --> 00:20:16,714
really awesome. It works great with iCloud for
403
00:20:16,716 --> 00:20:17,648
example, okay? And
404
00:20:17,650 --> 00:20:20,418
so, it might actually make a quick check on the network
405
00:20:20,420 --> 00:20:23,054
to see what the status of this document is. Okay,
406
00:20:23,056 --> 00:20:26,457
you do not want that block in your main queue, okay.
407
00:20:26,459 --> 00:20:29,360
So these things, these closures, get called
408
00:20:29,362 --> 00:20:31,462
after the document has been opened, asynchronously.
409
00:20:31,464 --> 00:20:34,565
Now usually these documents open almost instantaneously,
410
00:20:34,567 --> 00:20:38,169
but you're still going to get them open with this callback
411
00:20:38,171 --> 00:20:41,005
and it tells you whether it was successful in doing it.
412
00:20:41,007 --> 00:20:43,741
Okay? So the fact that this is asynchronous is
413
00:20:43,743 --> 00:20:45,810
gonna make your code a little trickier, okay?
414
00:20:45,812 --> 00:20:47,645
Cuz you wanna open the document and start
415
00:20:47,647 --> 00:20:50,314
using ManagedObjectContext right away, but you can't.
416
00:20:50,316 --> 00:20:52,383
You have to wait until this closure gets called,
417
00:20:52,385 --> 00:20:56,654
then you can start using your ManagedObjectContext, okay?
418
00:20:56,656 --> 00:20:59,090
That's the only trickiness about it. All right, and
419
00:20:59,092 --> 00:21:01,726
it's a little tricky that you have to see if it exists or,
420
00:21:01,728 --> 00:21:04,695
to know whether to open it or to save it. Okay,
421
00:21:04,697 --> 00:21:07,131
when you save it for the first time you're gonna do
422
00:21:07,133 --> 00:21:11,636
forSaveOperation .ForCreating, okay? You can actually save
423
00:21:11,638 --> 00:21:14,572
your document if you want on top of the existing one by
424
00:21:14,574 --> 00:21:19,510
doing forSaveOperation for overriding. Okay.
425
00:21:19,512 --> 00:21:23,281
So it's all asynchronous so be careful about that.
426
00:21:23,283 --> 00:21:25,049
There's other states besides closed and
427
00:21:25,051 --> 00:21:28,085
normal that you might run into. These are pretty rare
428
00:21:28,087 --> 00:21:30,221
states I'm not gonna talk about any of them.
429
00:21:30,223 --> 00:21:31,856
Some of them have Have to do with iCloud,
430
00:21:31,858 --> 00:21:34,692
like in conflict. Remember iCloud let's you see this
431
00:21:34,694 --> 00:21:36,527
document on two different devices, your iPad and
432
00:21:36,529 --> 00:21:39,363
your iPhone, let's say. And you might change it on one and
433
00:21:39,365 --> 00:21:41,365
then try to change it on the other and now they conflict,
434
00:21:41,367 --> 00:21:44,902
the two changes might conflict in the database, right? So
435
00:21:44,904 --> 00:21:46,504
they can be in that state.
436
00:21:46,506 --> 00:21:49,307
I'm not gonna ask you to handle any iCloud or
437
00:21:49,309 --> 00:21:50,641
anything like that for this class,
438
00:21:50,643 --> 00:21:52,576
but you need to know if you're gonna do iCloud.
439
00:21:52,578 --> 00:21:55,112
Then you might have some of these other states cropping
440
00:21:55,114 --> 00:21:59,483
up. I thought it might be a fun thing to do in your final
441
00:21:59,485 --> 00:22:02,286
project. One of the things in your final project you have to
442
00:22:02,288 --> 00:22:04,288
do some feature I didn't go over in lecture.
443
00:22:04,290 --> 00:22:06,824
Well, iCloud might be one of those. So maybe that would be
444
00:22:06,826 --> 00:22:10,795
something you want to do. All right, let's talk about saving
445
00:22:10,797 --> 00:22:13,497
the document, because I got this managed object context.
446
00:22:13,499 --> 00:22:16,467
And I'm going to be getting these in as managed objects,
447
00:22:16,469 --> 00:22:16,867
like tweets and
448
00:22:16,869 --> 00:22:18,769
twitter users aren't going to be changing them.
449
00:22:18,771 --> 00:22:21,906
When does is actually get saved? Okay, and for
450
00:22:21,908 --> 00:22:25,109
UIManagedDocument, it autosaves, which is really
451
00:22:25,111 --> 00:22:28,245
cool. Basically autosaves when it thinks it's a good time, so
452
00:22:28,247 --> 00:22:31,615
you really don't ever have to save a UIManagedDocument.
453
00:22:31,617 --> 00:22:34,352
You can, by doing that ForOverwriting that I was
454
00:22:34,354 --> 00:22:38,456
talking about. But you really don't ever have to do that,
455
00:22:38,458 --> 00:22:40,624
okay? It's just gonna automatically autosave for
456
00:22:40,626 --> 00:22:47,798
you. Closing the document, it also automatically closes,
457
00:22:47,800 --> 00:22:50,101
so as soon nobody has a strong pointer to it and
458
00:22:50,103 --> 00:22:53,137
it wants to leave the heap, it'll close automatically and
459
00:22:53,139 --> 00:22:56,607
then leave the heap. So that's cool too. But if you wanted to
460
00:22:56,609 --> 00:23:00,244
forcible close it and still keep a strong pointer to it,
461
00:23:00,246 --> 00:23:02,246
you could do close with completion handler,
462
00:23:02,248 --> 00:23:04,382
again here's a closure, it takes time for
463
00:23:04,384 --> 00:23:08,753
it to close. Probably micro seconds but anyway this will
464
00:23:08,755 --> 00:23:12,289
tell you when it is actually closed. And then it's state,
465
00:23:12,291 --> 00:23:16,961
document state will go back to dot closed okay.
466
00:23:16,963 --> 00:23:21,098
All right so now we have an NSManagedObjectContext.
467
00:23:21,100 --> 00:23:23,634
We either call that method in our app delegate to get it or
468
00:23:23,636 --> 00:23:26,537
we create new unmanaged document, opened it. And
469
00:23:26,539 --> 00:23:29,673
in the closure once the closure's executed, bam, now
470
00:23:29,675 --> 00:23:33,210
we have this managed object context that we can access our
471
00:23:33,212 --> 00:23:37,715
databases, okay? So, what do we do with it? How do we make
472
00:23:37,717 --> 00:23:41,051
it work, okay? Well, let's talk about putting objects
473
00:23:41,053 --> 00:23:43,220
in the database cuz until we've put something in there,
474
00:23:43,222 --> 00:23:44,989
we can't really do anything else with it.
475
00:23:44,991 --> 00:23:47,925
How do we do that? Well, we create objects
476
00:23:47,927 --> 00:23:52,096
in the database by using this method right here called
477
00:23:52,098 --> 00:23:54,999
InsertNewObjectForEntityForN- ame, okay?
478
00:23:55,001 --> 00:23:58,068
This is a static method, a class method.
479
00:23:58,070 --> 00:23:59,336
In the class NSEntityDescription.
480
00:23:59,338 --> 00:24:02,873
NSEntityDescription is kind of a simple little class,
481
00:24:02,875 --> 00:24:05,042
That just describes the entities okay,
482
00:24:05,044 --> 00:24:07,978
knows what all the properties are and all that.
483
00:24:07,980 --> 00:24:12,416
We only really use it for this one thing which is to create
484
00:24:12,418 --> 00:24:14,652
one of these things in the database okay. But
485
00:24:14,654 --> 00:24:17,855
notice that it takes in managedObjectContext, so
486
00:24:17,857 --> 00:24:19,490
you have to have a managedObjectContext or
487
00:24:19,492 --> 00:24:23,527
you cannot create a new object. Okay now when you
488
00:24:23,529 --> 00:24:26,197
create this document or this object in there,
489
00:24:26,199 --> 00:24:30,434
all of its properties or either nil or you can actually
490
00:24:30,436 --> 00:24:33,838
go in the inspector. In the little visual mapper.
491
00:24:33,840 --> 00:24:38,209
You can set defaults for some of the properties. Okay, so
492
00:24:38,211 --> 00:24:39,543
it's either gonna be the default you set there or
493
00:24:39,545 --> 00:24:41,812
if you didn't set a default, it'll just be nil. Okay, and
494
00:24:41,814 --> 00:24:44,682
remember they're all objects, they're NSNumbers, NSDates,
495
00:24:44,684 --> 00:24:49,420
all these properties. So that'll just be nil, okay. So
496
00:24:49,422 --> 00:24:52,756
this creates one. So now you've got a Tweet, it's of
497
00:24:52,758 --> 00:24:56,594
class, look what its class is, NSManagedObject, right.
498
00:24:56,596 --> 00:24:59,096
Tweet is an NSManagedObject. I just created one, so
499
00:24:59,098 --> 00:25:03,534
I've got an empty Tweet. All it's Properties, it's text and
500
00:25:03,536 --> 00:25:07,071
everything is nil, okay? All right, so
501
00:25:07,073 --> 00:25:10,374
now I've got one of these. How do I set the values?
502
00:25:10,376 --> 00:25:12,109
Okay, well I already told you how to do that,
503
00:25:12,111 --> 00:25:14,445
it's with this set value for key. Right,
504
00:25:14,447 --> 00:25:18,415
so I can do setValue, some string, for key text, and I
505
00:25:18,417 --> 00:25:22,353
can set the text of the Tweet for example, okay? Same thing,
506
00:25:22,355 --> 00:25:25,256
I kept the value for the key. One thing that's kind of cool,
507
00:25:25,258 --> 00:25:29,226
there's also this method called valueForKeyPath, okay?
508
00:25:29,228 --> 00:25:32,296
There's valueForKey and valueForKeyPath and
509
00:25:32,298 --> 00:25:36,166
valueForKeyPath, the string you pass to it can have dots
510
00:25:36,168 --> 00:25:41,105
in it to follow relationships. So, if you said on a Tweet,
511
00:25:41,107 --> 00:25:45,042
valueForKeyPath Tweeter dot name,
512
00:25:45,044 --> 00:25:48,979
it would give you the name of the Twitter user. It would
513
00:25:48,981 --> 00:25:52,983
follow that relationship over to the user and get the name.
514
00:25:52,985 --> 00:25:55,920
Even though I'd be saying valueForKeyPath on a Tweet,
515
00:25:55,922 --> 00:25:58,556
tweeter dot name on a Tweet is followed through
516
00:25:58,558 --> 00:26:03,060
the relationship so that's what the keyPath will do,
517
00:26:03,062 --> 00:26:07,064
got that? Now that sounds exciting I'm sure, but
518
00:26:07,066 --> 00:26:08,999
we actually almost never use these methods and
519
00:26:09,001 --> 00:26:12,136
you'll find out, in a moment why that is, okay.
520
00:26:12,138 --> 00:26:15,606
So that's the key that's the value I already talked about
521
00:26:15,608 --> 00:26:18,909
what all the values are right NSData NSDate,
522
00:26:18,911 --> 00:26:22,046
NSSet if it's a to-many relationship.
523
00:26:22,048 --> 00:26:24,381
An NSManagedObject if it's not, okay?
524
00:26:24,383 --> 00:26:28,485
All right, now changes, when you're changing something
525
00:26:28,487 --> 00:26:31,355
in the database, that only happens in memory, okay?
526
00:26:31,357 --> 00:26:34,425
Doesn't actually get stored in the database on disk until
527
00:26:34,427 --> 00:26:37,661
it saves. Now, we know that UIManagedDocument autosaves,
528
00:26:37,663 --> 00:26:40,598
so we're good to go there. But what if you use that other
529
00:26:40,600 --> 00:26:43,968
ManagedObjectContext? It's not in the UIManagedDocument, so
530
00:26:43,970 --> 00:26:47,605
there's no autosave there. You have to save it, okay. And
531
00:26:47,607 --> 00:26:51,008
you do that with this method called save, you send it to
532
00:26:51,010 --> 00:26:53,310
the ManagedObjectContext, right, so here's that
533
00:26:53,312 --> 00:26:55,946
ManagedObjectContext I got from my app delegate.
534
00:26:55,948 --> 00:26:59,283
And I wanna save it so I'm gonna say contact.save.
535
00:26:59,285 --> 00:27:00,317
Now, that looks simple, right,
536
00:27:00,319 --> 00:27:04,121
simple little four-letter function name there saved. But
537
00:27:04,123 --> 00:27:06,156
actually it's gonna cause me to have to give you two or
538
00:27:06,158 --> 00:27:09,660
three slides worth of information about swift, okay?
539
00:27:09,662 --> 00:27:12,863
Because, this is the method that I'm showing you in this
540
00:27:12,865 --> 00:27:17,401
class that can throw an error, okay?
541
00:27:17,403 --> 00:27:21,805
And why might save throw an error? Eh, disk could be full,
542
00:27:21,807 --> 00:27:24,875
there could be some problem the database that it can't be
543
00:27:24,877 --> 00:27:26,377
saved, there's lots of reasons,
544
00:27:26,379 --> 00:27:29,079
okay that it could so, throw an error, but
545
00:27:29,081 --> 00:27:31,649
what you need to know is how do I deal with that?
546
00:27:31,651 --> 00:27:33,784
Okay, if it throws an error, what do I do,
547
00:27:33,786 --> 00:27:36,553
how do I look at the error, etc. And to do that, you need
548
00:27:36,555 --> 00:27:39,890
to learn about how throw works in Swift. So let's take
549
00:27:39,892 --> 00:27:42,559
a little time out from Core Data, you probably, you're
550
00:27:42,561 --> 00:27:44,928
supposed to read about this in your reading assignment, but
551
00:27:44,930 --> 00:27:46,397
I'm sure it probably went right over your.
552
00:27:46,399 --> 00:27:47,898
How many people in this room feel like yeah,
553
00:27:47,900 --> 00:27:51,201
I pretty much understand throw? See, zero.
554
00:27:51,203 --> 00:27:53,804
Okay, so that's why I'm gonna take a couple slides here and
555
00:27:53,806 --> 00:27:56,407
explain it to you. So all right, this is throwing errors
556
00:27:56,409 --> 00:27:59,743
in Swift, a little side, you know, okay. So any function in
557
00:27:59,745 --> 00:28:03,714
Swift, like the save method in MSManagedObjectContext if
558
00:28:03,716 --> 00:28:06,850
it can throw an error, it will have the word throws,
559
00:28:06,852 --> 00:28:10,554
at the end of its declaration, okay? So, the functions,
560
00:28:10,556 --> 00:28:12,690
you read that when you're reading the closing,
561
00:28:12,692 --> 00:28:17,094
the function save throws, all right?
562
00:28:17,096 --> 00:28:19,463
So, if you have a function that throws,
563
00:28:19,465 --> 00:28:24,501
you must deal with that. You cannot just ignore the fact
564
00:28:24,503 --> 00:28:28,305
that it throws, you must catch the error usually, okay?
565
00:28:28,307 --> 00:28:29,773
There's other ways to deal with it, but
566
00:28:29,775 --> 00:28:33,110
generally you must catch it. So, how do you catch it? Well,
567
00:28:33,112 --> 00:28:36,880
you just put the word try in front of it. The try just
568
00:28:36,882 --> 00:28:40,584
means please try this. Because I know it might throw but
569
00:28:40,586 --> 00:28:44,388
try it. Okay, so that's why it's called try. And if it's
570
00:28:44,390 --> 00:28:47,825
successful fine, if it doesn't then it throws an error.
571
00:28:47,827 --> 00:28:51,161
Now when it throws the error you have to catch it and
572
00:28:51,163 --> 00:28:55,866
the way you catch it is you put this try inside of a do.
573
00:28:55,868 --> 00:28:58,736
See it says do open curly brace closed curly brace?
574
00:28:58,738 --> 00:29:03,474
Anything inside this do that you try that throws,
575
00:29:03,476 --> 00:29:08,345
will allow you to catch the error right after the do,
576
00:29:08,347 --> 00:29:11,381
okay? And so I can take catch let error here,
577
00:29:11,383 --> 00:29:14,218
I can actually catch different kinds of errors by having
578
00:29:14,220 --> 00:29:18,288
multiple catches here. Catch this, catch that, catch this,
579
00:29:18,290 --> 00:29:21,225
as many catches as I want, okay. And
580
00:29:21,227 --> 00:29:23,560
when I catch it you see I have this let error,
581
00:29:23,562 --> 00:29:26,930
that basically lets this local variable here that's gonna be
582
00:29:26,932 --> 00:29:32,202
in this context error, it lets it be equal to the error
583
00:29:32,204 --> 00:29:36,340
that was thrown, okay. So now you have this error and
584
00:29:36,342 --> 00:29:38,709
you, inside here inside this second curly brace here,
585
00:29:38,711 --> 00:29:41,211
you can look at that error and find out what it is and
586
00:29:41,213 --> 00:29:45,249
why clean up or try again or whatever you want to do,
587
00:29:45,251 --> 00:29:48,819
you can do it in here. Now these errors that get thrown
588
00:29:48,821 --> 00:29:54,424
they are all implementors of this protocol ErrorType.
589
00:29:54,426 --> 00:29:56,527
And this ErrorType protocol doesn't have much in it.
590
00:29:56,529 --> 00:29:59,630
It's mostly just a way to identify that this is an error
591
00:29:59,632 --> 00:30:04,835
that got thrown, okay. Now iOS has a very important class
592
00:30:04,837 --> 00:30:08,539
called NSError which implements this protocol.
593
00:30:08,541 --> 00:30:10,908
When iOS throws an error it's always gonna
594
00:30:10,910 --> 00:30:15,045
be an NSError. Okay, and that class you should go look at.
595
00:30:15,047 --> 00:30:19,049
Okay, it has methods in there like localized description of
596
00:30:19,051 --> 00:30:21,485
the error. Things like that that
597
00:30:21,487 --> 00:30:24,621
you can use to put errors up to the user even or certainly
598
00:30:24,623 --> 00:30:26,723
put things on the console to say what's going on or
599
00:30:26,725 --> 00:30:29,793
just to look at it yourself, like what did that error etc.,
600
00:30:29,795 --> 00:30:32,963
okay. So NSError is kind of iOS's thing.
601
00:30:32,965 --> 00:30:35,833
But if you had something that you thought
602
00:30:35,835 --> 00:30:36,633
would be good to throw an error,
603
00:30:36,635 --> 00:30:39,870
you can invent your own thing that gets thrown as long as
604
00:30:39,872 --> 00:30:42,472
it implements this ErrorType protocol,
605
00:30:42,474 --> 00:30:45,042
okay. Now usually the things that implement the ErrorType
606
00:30:45,044 --> 00:30:48,812
protocol are enums. Because when you have something that
607
00:30:48,814 --> 00:30:51,014
can throw an error it usually can throw two or
608
00:30:51,016 --> 00:30:52,616
three different kinds of things. And so
609
00:30:52,618 --> 00:30:56,954
that each of those is an enum case with associated data that
610
00:30:56,956 --> 00:31:00,324
goes along with the error. All right, that's the great thing
611
00:31:00,326 --> 00:31:02,860
about enums, if you've got this associated data so
612
00:31:02,862 --> 00:31:05,729
if there's an error you can, you know, hand along some
613
00:31:05,731 --> 00:31:08,432
interesting information about the error. Okay,
614
00:31:08,434 --> 00:31:13,270
now if inside the catch you can't deal with this error,
615
00:31:13,272 --> 00:31:16,740
you could re-throw it by just saying throw error.
616
00:31:16,742 --> 00:31:18,575
In fact in general anytime you wanna throw an error,
617
00:31:18,577 --> 00:31:20,077
that's how you do it, you say throw error and
618
00:31:20,079 --> 00:31:23,447
again this has to be a type error type protocol there.
619
00:31:23,449 --> 00:31:24,314
You can throw it and re-throw it now,
620
00:31:24,316 --> 00:31:26,950
the only thing about this is if you're gonna re-throw here
621
00:31:26,952 --> 00:31:31,622
maybe then your whole method also has to say throws at
622
00:31:31,624 --> 00:31:36,827
the end, because you might be re-throwing, okay? Okay,
623
00:31:36,829 --> 00:31:39,229
so that's basically how fundamentally works.
624
00:31:39,231 --> 00:31:43,200
Now, there's an extra little interesting thing here which
625
00:31:43,202 --> 00:31:45,469
try with an exclamation point.
626
00:31:45,471 --> 00:31:48,805
Okay, if you try with an exclamation point, that means
627
00:31:48,807 --> 00:31:51,341
forget all this junk. I'm not gonna put in a do.
628
00:31:51,343 --> 00:31:54,645
I'm not gonna catch anything. And if this throws,
629
00:31:54,647 --> 00:31:57,547
crash my app. Okay, we know that exclamation point
630
00:31:57,549 --> 00:32:00,550
usually means force this thing and crash if it doesn't work.
631
00:32:00,552 --> 00:32:03,787
That's what for implicit for Optional unwrapping right,
632
00:32:03,789 --> 00:32:06,924
we do exclamation point boom that means crash if this thing
633
00:32:06,926 --> 00:32:10,460
is nil. So same thing here you can say try and it'll crash.
634
00:32:10,462 --> 00:32:13,230
Now you would never wanna do that with context save because
635
00:32:13,232 --> 00:32:16,767
context save can get errors unpredictably right.
636
00:32:16,769 --> 00:32:17,834
So you never wanna do that here but
637
00:32:17,836 --> 00:32:20,871
other methods sometimes it's like this method has to
638
00:32:20,873 --> 00:32:24,074
succeed or I'm just doomed. So I'm going to
639
00:32:24,076 --> 00:32:26,977
try exclamation point. There's one other way which I'm going
640
00:32:26,979 --> 00:32:29,579
to talk about in a few slides which is try question mark.
641
00:32:29,581 --> 00:32:34,551
Okay? Try question mark means try this, I'm not going to
642
00:32:34,553 --> 00:32:38,755
catch the error, but return nil if there was a thrown
643
00:32:38,757 --> 00:32:41,391
error. Okay? That's right, question mark and I'm gonna
644
00:32:41,393 --> 00:32:43,694
show you an example of that in a couple slides. Okay, so
645
00:32:43,696 --> 00:32:46,930
that's throwing errors. You got that. There's not
646
00:32:46,932 --> 00:32:48,498
a lot of things that throw errors, but
647
00:32:48,500 --> 00:32:52,536
you are gonna run across them. Both in some of the homeworks
648
00:32:52,538 --> 00:32:56,506
and also certainly in your final project. Okay, so let's
649
00:32:56,508 --> 00:33:01,478
get back to core data here. Now, calling valueForKey and
650
00:33:01,480 --> 00:33:04,047
setValue(forKey:) is pretty ugly. Okay,
651
00:33:04,049 --> 00:33:06,917
there's no type-checking there, because it's any object
652
00:33:06,919 --> 00:33:08,352
is the type of the argument. Okay, and
653
00:33:08,354 --> 00:33:12,255
you've got all these literal strings [LAUGH] like text and
654
00:33:12,257 --> 00:33:16,994
created and id inside your code, which
655
00:33:16,996 --> 00:33:20,564
you probably are gonna put in a struct static let like you
656
00:33:20,566 --> 00:33:23,000
do all your constants. But it's just a mess, okay.
657
00:33:23,002 --> 00:33:26,370
What you really want is vars. You want these Tweets,
658
00:33:26,372 --> 00:33:28,638
you wanna be able to have a var on there called text.
659
00:33:28,640 --> 00:33:31,441
And you just wanna say that tweet.text equals,
660
00:33:31,443 --> 00:33:33,343
that's what you want, okay? An object-oriented.
661
00:33:33,345 --> 00:33:36,747
So, of course, we can do that. And the way we do that is
662
00:33:36,749 --> 00:33:41,118
we're gonna create a sub class of ns managed object. Okay? So
663
00:33:41,120 --> 00:33:43,620
for each thing a tweet, Twitter user whatever, we're
664
00:33:43,622 --> 00:33:46,223
gonna create a sub class of ns object managed object for
665
00:33:46,225 --> 00:33:50,193
it and it's gonna have vars which are all the properties.
666
00:33:50,195 --> 00:33:52,796
Okay? Couldn't be easier. Really simply.
667
00:33:52,798 --> 00:33:56,833
And x code will even generate this sub class for
668
00:33:56,835 --> 00:33:59,569
us. Okay, so let's look at how you do that. So I'm
669
00:33:59,571 --> 00:34:02,239
gonna select the entities that I wanna generate these little
670
00:34:02,241 --> 00:34:05,375
sub classes for. Okay, so I'm selecting both in this case
671
00:34:05,377 --> 00:34:08,478
right here. Then I go up to the editor menu and
672
00:34:08,480 --> 00:34:14,551
I pick Create NSManagedObject Subclass. When I do
673
00:34:14,553 --> 00:34:17,254
that it's gonna say okay for which of your models do you
674
00:34:17,256 --> 00:34:19,456
want to do that because you might have multiple models,
675
00:34:19,458 --> 00:34:22,492
so I'll pick the only model I have here.
676
00:34:22,494 --> 00:34:24,428
Then it says okay which entities?
677
00:34:24,430 --> 00:34:27,164
Well, I selected both so it's got these both pre-selected.
678
00:34:27,166 --> 00:34:31,201
I wanna create a menus managed object subclass for tweet and
679
00:34:31,203 --> 00:34:34,204
one for twitter users. It can be two different classes.
680
00:34:34,206 --> 00:34:37,140
Okay now, it's saying where do you wanna put it, it's also
681
00:34:37,142 --> 00:34:39,509
asking what language? Be careful right here sometimes
682
00:34:39,511 --> 00:34:42,379
this comes up Objective-C even if you're in a Swift project.
683
00:34:42,381 --> 00:34:44,414
So make sure that says Swift otherwise you're gonna get an
684
00:34:44,416 --> 00:34:49,653
Objective- C class. Which is a subclass of that's a swift.
685
00:34:49,655 --> 00:34:52,189
The other thing is there's this uscaler properties for
686
00:34:52,191 --> 00:34:55,258
primitive data types. Be careful of this one,
687
00:34:55,260 --> 00:34:56,460
if you turn this on, then for
688
00:34:56,462 --> 00:34:59,830
example NSDate, the property it creates, or the var,
689
00:34:59,832 --> 00:35:01,031
is going to be an NSTime interval.
690
00:35:01,033 --> 00:35:05,235
Which is going to be the number of seconds since 1970,
691
00:35:05,237 --> 00:35:08,138
okay. So you probably don't want that most of the time,
692
00:35:08,140 --> 00:35:11,708
right, you want it to be an NSDate object. Also you're
693
00:35:11,710 --> 00:35:14,211
gonna put it, this is gonna say where you're gonna put it.
694
00:35:14,213 --> 00:35:16,780
By default it usually says to put it at the very top
695
00:35:16,782 --> 00:35:19,249
level, but you usually actually want it down where
696
00:35:19,251 --> 00:35:22,853
all the rest of your files are down here like in core data
697
00:35:22,855 --> 00:35:23,954
example, right?
698
00:35:23,956 --> 00:35:26,690
So make sure you get that right too. So, be,
699
00:35:26,692 --> 00:35:28,158
pay attention to this thing and
700
00:35:28,160 --> 00:35:29,926
answer all these questions the way you want. Okay?
701
00:35:29,928 --> 00:35:33,196
It's not one that you can just click Create and just move on
702
00:35:33,198 --> 00:35:35,365
through or you won't get what you want, I don't think.
703
00:35:35,367 --> 00:35:39,870
All right, so it created these things. Here they are,
704
00:35:39,872 --> 00:35:41,705
you can see them on the side here. There's one,
705
00:35:41,707 --> 00:35:45,675
Tweet.swift, okay? There's TwitterUser.swift.
706
00:35:45,677 --> 00:35:48,645
And it's just a class tweet as promised.
707
00:35:48,647 --> 00:35:51,281
Look at that, it inherits from NSManagedObject, and
708
00:35:51,283 --> 00:35:54,651
you could put any code you want in here to do
709
00:35:54,653 --> 00:35:57,220
tweet specific stuff. Because the fact
710
00:35:57,222 --> 00:35:58,989
that the tweet is stored in the database is great but
711
00:35:58,991 --> 00:36:01,391
it might also have some other behavior that you want to add.
712
00:36:01,393 --> 00:36:04,561
It's just a class, right, you're using it in your app so
713
00:36:04,563 --> 00:36:06,863
you might want to put some code in there, okay, so
714
00:36:06,865 --> 00:36:10,734
it's perfectly good for that. Here's TwitterUser,
715
00:36:10,736 --> 00:36:15,138
right Okay, put all your Twitter user stuff here. But
716
00:36:15,140 --> 00:36:16,873
what about those vars? Okay,
717
00:36:16,875 --> 00:36:19,876
I thought there was going to be vars for text and
718
00:36:19,878 --> 00:36:22,746
created and the screen name for, where are those? Well,
719
00:36:22,748 --> 00:36:25,549
it turns out those are put in extensions, you see them right
720
00:36:25,551 --> 00:36:27,651
here, there's two of them, that one and that one, so
721
00:36:27,653 --> 00:36:30,353
let's look at those, okay. This file right here,
722
00:36:30,355 --> 00:36:34,925
this extension. This is creating a property of R for
723
00:36:34,927 --> 00:36:37,661
each of the things, the attributes and
724
00:36:37,663 --> 00:36:40,830
relationships to in my Tweet object.
725
00:36:40,832 --> 00:36:43,867
Now, why does it put them off in an extension here? Why
726
00:36:43,869 --> 00:36:46,203
didn't it just put it in the other class? Can anyone think
727
00:36:46,205 --> 00:36:50,340
of a reason why this would be an extension? It's a good
728
00:36:50,342 --> 00:36:54,211
reason actually which is that, you might wanna change these
729
00:36:54,213 --> 00:36:56,680
attributes in relationship over time that you're working
730
00:36:56,682 --> 00:36:58,982
on your app. You decide you need a new property, okay?
731
00:36:58,984 --> 00:37:02,953
Well you gonna regenerate this file. When you do that, you
732
00:37:02,955 --> 00:37:05,689
don't wanna blow away in the other code that you wrote for
733
00:37:05,691 --> 00:37:08,458
tweet. Right, so it keeps all the stuff that's being
734
00:37:08,460 --> 00:37:13,296
generated by x code when you do that create subclass,
735
00:37:13,298 --> 00:37:15,131
it's keeping it all in one file so it can be constantly
736
00:37:15,133 --> 00:37:19,502
overwritten. Make sense? See, that's why we put an extension
737
00:37:19,504 --> 00:37:22,739
there. Now lets look at this wacky extension here,
738
00:37:22,741 --> 00:37:25,275
what's going on here? Well these are obvious,
739
00:37:25,277 --> 00:37:29,045
text, ID and created, why are they optional? Because they're
740
00:37:29,047 --> 00:37:31,915
nil when you first create a tweet it's empty those things
741
00:37:31,917 --> 00:37:35,452
are all nil so it has to be an optional, and then look at
742
00:37:35,454 --> 00:37:39,089
this one. Tweeter has promised again twitter users.
743
00:37:39,091 --> 00:37:41,825
Now I told you that tweeter since it's a relationship
744
00:37:41,827 --> 00:37:45,295
would be an NS managed object But the system is smart enough
745
00:37:45,297 --> 00:37:47,530
to say, well not only is it an NSManaged object,
746
00:37:47,532 --> 00:37:49,432
but I know it's a TwitterUser. Okay,
747
00:37:49,434 --> 00:37:52,702
which is this other class over here that it created.
748
00:37:52,704 --> 00:37:54,537
By the way, sometimes when you generate a whole
749
00:37:54,539 --> 00:37:58,742
bunch of things at once, it will miss that. Okay,
750
00:37:58,744 --> 00:38:02,145
it will notice, it will just have this NSManaged object,
751
00:38:02,147 --> 00:38:06,283
just regenerate them again if that happens. Okay?
752
00:38:06,285 --> 00:38:07,384
It's just like, cause it's like a one,
753
00:38:07,386 --> 00:38:10,220
instead of being a two phase generation, it's like a one
754
00:38:10,222 --> 00:38:12,522
phase generation; if it does one class before the other.
755
00:38:12,524 --> 00:38:14,758
It didn't know that twitter user existed by the time it
756
00:38:14,760 --> 00:38:18,161
did tweet. So, I don't know, hopefully they'll fix that
757
00:38:18,163 --> 00:38:20,330
someday. Might already be fixed in the latest version,
758
00:38:20,332 --> 00:38:23,867
but just regenerate if you get that problem. So
759
00:38:23,869 --> 00:38:25,368
anyway, Tweeter is a Twitter user,
760
00:38:25,370 --> 00:38:27,404
that makes sense, right? That's what a Tweeter is,
761
00:38:27,406 --> 00:38:29,639
it's a Twitter user. Let's go look at Tweet,
762
00:38:29,641 --> 00:38:32,742
it's the same thing here. Here's the screen name and
763
00:38:32,744 --> 00:38:35,145
the name, those are string. Now look at tweets,
764
00:38:35,147 --> 00:38:39,716
it's an NS set and inside this NS set is going to be,
765
00:38:39,718 --> 00:38:44,921
what? Tweet objects, right because that's what this is,
766
00:38:44,923 --> 00:38:47,490
the Tweets that this Twitter user has Tweeted. So
767
00:38:47,492 --> 00:38:50,627
this NS set is gonna have Tweet, Tweet objects.
768
00:38:50,629 --> 00:38:54,264
See this class right up here. Okay,
769
00:38:54,266 --> 00:38:58,868
now what about this @NSManaged thing? What the heck,
770
00:38:58,870 --> 00:39:00,737
we never seen that before. What is that?
771
00:39:00,739 --> 00:39:04,174
Okay, well, that's basically just some magic that says
772
00:39:04,176 --> 00:39:07,644
the system is gonna take of that, of this bar, okay?
773
00:39:07,646 --> 00:39:10,547
Because otherwise, where is this bar stored?
774
00:39:10,549 --> 00:39:13,149
It can't just be a normal stored property because
775
00:39:13,151 --> 00:39:15,985
when it changes, it needs to change in the database, so
776
00:39:15,987 --> 00:39:18,254
it's got to be code going on there.
777
00:39:18,256 --> 00:39:21,191
It kind of could be a computer property but you certainly
778
00:39:21,193 --> 00:39:24,027
don't want the computer property code in here. So,
779
00:39:24,029 --> 00:39:28,698
this NSManage basically says that it's a dynamic, bar and
780
00:39:28,700 --> 00:39:33,603
when it gets accessed it causes other code in core data
781
00:39:33,605 --> 00:39:33,837
to be executed.
782
00:39:33,839 --> 00:39:37,107
And that's basically going to do value and value for key and
783
00:39:37,109 --> 00:39:40,210
set value for key for you. Yeah.
784
00:39:40,212 --> 00:39:43,480
>> A name like "TwitterUser",
785
00:39:43,482 --> 00:39:45,982
would it change every time >> [INAUDIBLE] Yeah, so
786
00:39:45,984 --> 00:39:49,018
the question is what if I change the key like like
787
00:39:49,020 --> 00:39:52,122
if I rename screen name to be an ampersand name or
788
00:39:52,124 --> 00:39:54,624
something like that. No, you have to go back and
789
00:39:54,626 --> 00:39:57,227
regenerate them okay they will always have to regenerate.
790
00:39:57,229 --> 00:40:00,196
This is not captain sync with the visual map you have to
791
00:40:00,198 --> 00:40:04,067
regenerate all the time. Again that's why you want this to be
792
00:40:04,069 --> 00:40:05,902
a separate file from the other thing.
793
00:40:05,904 --> 00:40:09,539
Okay, so ns manage is just magic that core data uses.
794
00:40:09,541 --> 00:40:13,410
You don't even have to worry about it, you're never gonna
795
00:40:13,412 --> 00:40:14,811
have to actually type this in or anything.
796
00:40:14,813 --> 00:40:19,883
It's always just in this generated thing. Okay, so,
797
00:40:19,885 --> 00:40:25,155
how do I access these entities using these sub classes and
798
00:40:25,157 --> 00:40:28,458
get the properties. Okay, so, it's pretty simple.
799
00:40:28,460 --> 00:40:30,160
Let's say I get my manage of the context.
800
00:40:30,162 --> 00:40:32,362
Here, I'm getting it from my UI managed document,
801
00:40:32,364 --> 00:40:35,165
but I could have got it from my app delegate, whatever,
802
00:40:35,167 --> 00:40:35,932
I got my context.
803
00:40:35,934 --> 00:40:39,002
I'm gonna create one by saying insert new object for
804
00:40:39,004 --> 00:40:41,204
a different name, same as before. Except for
805
00:40:41,206 --> 00:40:46,142
look at the yellow text, I'm gonna cast it, downcast it
806
00:40:46,144 --> 00:40:50,246
to be a tweet. Because I know that these tweet entities have
807
00:40:50,248 --> 00:40:55,118
this associated subclass with them. Got it? So that's
808
00:40:55,120 --> 00:40:59,122
the magic right there. That's the thing that turns this
809
00:40:59,124 --> 00:41:01,491
Tweet that you're creating into a tweet so that now
810
00:41:01,493 --> 00:41:04,961
that it's a tweet you can say things like tweet.tweeter = or
811
00:41:04,963 --> 00:41:09,499
tweet.created = or tweet.text =, tweet.tweeter.name =,
812
00:41:09,501 --> 00:41:12,836
you can even do. Okay, see what's happened here?
813
00:41:12,838 --> 00:41:13,937
Tweet.tweeter.name.
814
00:41:13,939 --> 00:41:17,106
I'm actually just using properties. To go through,
815
00:41:17,108 --> 00:41:19,809
we know the type of Tweeter is a Twitter user, and
816
00:41:19,811 --> 00:41:25,915
we know the twitter user has a bar which is the name.
817
00:41:27,085 --> 00:41:30,920
So, this is a lot nicer than saying set value, this,
818
00:41:30,922 --> 00:41:33,957
comma, for key, text.
819
00:41:37,028 --> 00:41:39,996
Swift code, and also Swift can type check all of this to make
820
00:41:39,998 --> 00:41:45,535
sure you're providing the right types, all right? Okay,
821
00:41:45,537 --> 00:41:48,338
so now let's talk about deletion briefly.
822
00:41:48,340 --> 00:41:53,309
You can delete objects from the database. It's very easy,
823
00:41:53,311 --> 00:41:57,514
it's almost too easy. You just call deleteObject, okay?
824
00:41:57,516 --> 00:41:59,182
And it will delete from the database.
825
00:41:59,184 --> 00:42:02,385
There's a little bit of a question, if I delete a tweet,
826
00:42:02,387 --> 00:42:02,919
does it delete the Twitter user who tweeted it?
827
00:42:02,921 --> 00:42:05,622
Probably not. If I delete a Twitter user, does it delete
828
00:42:11,563 --> 00:42:14,831
all the tweets that the Twitter user tweeted? Maybe
829
00:42:14,833 --> 00:42:19,836
so, okay? So you can determine that rule for what happens
830
00:42:19,838 --> 00:42:23,106
when something is deleted back in the Visual Mapper. You just
831
00:42:23,108 --> 00:42:26,109
inspect the relationship and you can put the delete rule in
832
00:42:26,111 --> 00:42:29,145
there. You can read all about what the settings are,
833
00:42:29,147 --> 00:42:33,316
in the documentation. But you can basically have cascading
834
00:42:33,318 --> 00:42:35,218
deletes that delete, like that's probably what
835
00:42:35,220 --> 00:42:37,487
you would have if you deleted a Twitter user.
836
00:42:37,489 --> 00:42:37,620
It would cascade and
837
00:42:37,622 --> 00:42:40,823
delete all the tweets. And you can have just a nilling one,
838
00:42:40,825 --> 00:42:43,960
where if you deleted a certain tweet, it's just going to
839
00:42:43,962 --> 00:42:47,764
remove it from the set, and the Twitter user's set will be
840
00:42:47,766 --> 00:42:52,835
automatically updated, okay? One thing, once you delete
841
00:42:52,837 --> 00:42:55,538
this tweet, make sure you don't keep a strong pointer to
842
00:42:55,540 --> 00:42:58,274
it because it now points to something that's invalid.
843
00:42:58,276 --> 00:43:01,044
Cuz you deleted it from the database so you can't set any
844
00:43:01,046 --> 00:43:03,680
of its attributes or do anything cuz it's gone.
845
00:43:03,682 --> 00:43:06,482
So it kinda gets into a weird state here [COUGH].
846
00:43:06,484 --> 00:43:10,753
Just make sure you don't do anything with that. All right,
847
00:43:10,755 --> 00:43:14,023
one thing that's kind of fun about deletion here is that
848
00:43:14,025 --> 00:43:16,893
your NsMangedObject subclass like tweet or
849
00:43:16,895 --> 00:43:21,331
TwitterUser will be sent this method, prepareForDeletion,
850
00:43:21,333 --> 00:43:24,901
when someone tries to delete it, and in there you can
851
00:43:24,903 --> 00:43:28,137
do a lot of things. You could decrement some count for
852
00:43:28,139 --> 00:43:30,239
example if you're keeping a track of a count or
853
00:43:30,241 --> 00:43:33,509
something like that. One thing you don't have to do in here
854
00:43:33,511 --> 00:43:36,012
though is modify in a relationships.
855
00:43:36,014 --> 00:43:37,614
That happens for you automatically.
856
00:43:37,616 --> 00:43:42,418
So if I delete a tweet k? From the database, it automatically
857
00:43:42,420 --> 00:43:46,456
gets removed from any ns sets that Twitter users point to,
858
00:43:46,458 --> 00:43:50,560
even if I go into an NS set, okay, of tweets and
859
00:43:50,562 --> 00:43:54,864
I delete a tweet out of there, it will [INAUDIBLE] you know,
860
00:43:54,866 --> 00:43:58,267
any, both sides of every relationship get updated. So
861
00:43:58,269 --> 00:44:00,703
you never have to update either side of a relationship.
862
00:44:00,705 --> 00:44:03,439
You touch one, the other side stays in synch.
863
00:44:03,441 --> 00:44:05,942
That's one of the really cool things about core data. So
864
00:44:05,944 --> 00:44:07,343
you don't have to do that in a prepare for
865
00:44:07,345 --> 00:44:10,113
deletion. Prepare for deletion lets you do other things,
866
00:44:10,115 --> 00:44:12,649
other things you might be counting or
867
00:44:12,651 --> 00:44:15,785
doing other things.
868
00:44:15,787 --> 00:44:19,288
So now we know how to create our database, set all the data
869
00:44:19,290 --> 00:44:22,925
in there, it's awesome. Now we get to the real value of
870
00:44:22,927 --> 00:44:25,762
having the data in there which is querying, okay. So
871
00:44:25,764 --> 00:44:28,064
now we wanna go and look in the database and
872
00:44:28,066 --> 00:44:32,769
get the objects we want based on certain criteria, okay. And
873
00:44:32,771 --> 00:44:37,140
how do we do that? The main
874
00:44:37,142 --> 00:44:40,943
Piece of this is this class called NSFETCH request,
875
00:44:40,945 --> 00:44:44,380
okay? And in NSFETCH request we're going to specify
876
00:44:44,382 --> 00:44:46,349
which objects we want out of the database, and
877
00:44:46,351 --> 00:44:49,018
they're going to come back in an array, it's as simple as
878
00:44:49,020 --> 00:44:52,855
that, okay? So, how do we create an NSFETCH request?
879
00:44:52,857 --> 00:44:56,259
We need four things. One, the entity to fetch.
880
00:44:56,261 --> 00:44:58,995
This is very important to understand. Okay. You can only
881
00:44:58,997 --> 00:45:02,732
fetch one kind of thing with a fetch request. There's no
882
00:45:02,734 --> 00:45:05,835
fetch request in the world that's gonna give you an array
883
00:45:05,837 --> 00:45:08,738
of some tweets and some Twitter users.
884
00:45:08,740 --> 00:45:11,674
It's always gonna give you an array of all the same thing,
885
00:45:11,676 --> 00:45:14,877
all tweets, or all Twitter users. Okay?
886
00:45:14,879 --> 00:45:18,648
So, you specify the entity, the one entity that's going to
887
00:45:18,650 --> 00:45:22,418
be in this fetch request. All right? Second thing is
888
00:45:22,420 --> 00:45:26,255
how many objects you want to limit your fetch to,
889
00:45:26,257 --> 00:45:28,224
like maybe you only want to fetch 100 or
890
00:45:28,226 --> 00:45:30,793
you want to fetch them in groups of 20 or 40 or
891
00:45:30,795 --> 00:45:34,831
something like that. You can specify that in the request.
892
00:45:34,833 --> 00:45:37,700
You can specify how to sort the result because I told you
893
00:45:37,702 --> 00:45:41,738
the result comes back as an array, arrays are ordered. So
894
00:45:41,740 --> 00:45:44,874
you can specify what order the things you're fetching come
895
00:45:44,876 --> 00:45:48,578
back in. All right? And then lastly and most importantly,
896
00:45:48,580 --> 00:45:50,079
you're gonna specify the predicate.
897
00:45:50,081 --> 00:45:55,818
This is the description of which objects you want. Okay?
898
00:45:55,820 --> 00:45:59,088
So, let's look at all of this things. Here's what the code
899
00:45:59,090 --> 00:46:01,157
looks like. Basically to create a request right here.
900
00:46:01,159 --> 00:46:02,458
So, let's look at all parts of this.
901
00:46:02,460 --> 00:46:03,559
So, we're creating a request for
902
00:46:03,561 --> 00:46:06,562
the certain entity. Here I'm saying the BatchSize and
903
00:46:06,564 --> 00:46:08,664
fetchLimits and then the sortDescriptors and
904
00:46:08,666 --> 00:46:10,299
predicate, we'll talk about it in the next slide.
905
00:46:10,301 --> 00:46:15,171
So the batch size of 20 here, just means that when I fetch
906
00:46:15,173 --> 00:46:18,040
it's gonna look like it returns all the objects, but
907
00:46:18,042 --> 00:46:21,444
it's actually only fetch them at 20 at a time.
908
00:46:21,446 --> 00:46:23,679
This is really great for things like table views,
909
00:46:23,681 --> 00:46:26,883
where you know the person's scrolling through it, so
910
00:46:26,885 --> 00:46:28,451
you don't wanna fetch all million item.
911
00:46:28,453 --> 00:46:30,586
You know that you can fetch them in batches as they go
912
00:46:30,588 --> 00:46:33,289
along. Okay. We're gonna talk a little bit about another
913
00:46:33,291 --> 00:46:35,458
important thing called faulting, which it so you can
914
00:46:35,460 --> 00:46:37,560
have a million items on the table and it's still pretty
915
00:46:37,562 --> 00:46:41,430
lightweight even if you don't set a batch limit but you can.
916
00:46:41,432 --> 00:46:43,499
Also you might have something where there's thousands of
917
00:46:43,501 --> 00:46:46,035
objects but maybe your UI can only show a hundred,
918
00:46:46,037 --> 00:46:47,603
it's just the maximum you can show.
919
00:46:47,605 --> 00:46:50,807
Well then you can say only give me the first hundred.
920
00:46:50,809 --> 00:46:54,544
Okay that's also Allowed. Alright so
921
00:46:54,546 --> 00:46:56,179
let's talk about the two most important ones,
922
00:46:56,181 --> 00:46:58,447
you don't really use these very much, but
923
00:46:58,449 --> 00:46:59,115
the sortdescriptor and
924
00:46:59,117 --> 00:47:00,483
the predicate you are going to use a lot.
925
00:47:00,485 --> 00:47:04,287
Let's talk about those in detail. The sortdescriptor is,
926
00:47:04,289 --> 00:47:09,492
is an array of descriptions of things to sort by.
927
00:47:09,494 --> 00:47:12,428
Okay? And it's gonna use those sort by things
928
00:47:12,430 --> 00:47:14,831
to sort the things that come out of the array, or
929
00:47:14,833 --> 00:47:16,299
come out of the database when it puts them
930
00:47:16,301 --> 00:47:19,168
in the array. Okay? So here's what it's like to create
931
00:47:19,170 --> 00:47:22,705
a sort descriptor. It has a few different initializers,
932
00:47:22,707 --> 00:47:26,576
but here's kind of one of it's most, verbose ones.
933
00:47:26,578 --> 00:47:30,413
First is the key that you're going to be sorting by. So
934
00:47:30,415 --> 00:47:33,216
if I'm looking for Twitter users, I might want the array
935
00:47:33,218 --> 00:47:36,819
to come back sorted by the screen name, okay, at sign,
936
00:47:36,821 --> 00:47:38,020
whatever. Okay, I wanna sort it so
937
00:47:38,022 --> 00:47:41,224
I would create a descriptor where the key is screenName,
938
00:47:41,226 --> 00:47:42,725
cuz that's what I want it to sort by.
939
00:47:42,727 --> 00:47:45,494
Ascending is whether it's you know the @ sign
940
00:47:45,496 --> 00:47:48,531
A people come first or the @ sign Z people come first
941
00:47:48,533 --> 00:47:52,235
right? So [INAUDIBLE] is it an ascending list or a descending
942
00:47:52,237 --> 00:47:55,571
list? And then this selector which is usually optional,
943
00:47:55,573 --> 00:47:57,773
you don't usually need it, is interesting.
944
00:47:57,775 --> 00:48:02,044
It's basically something that just says, how do I sort?
945
00:48:02,046 --> 00:48:05,348
These strings, okay? Do I just sort them alphabetically?
946
00:48:05,350 --> 00:48:08,684
Well, what does it mean to sort alphabetically? Okay,
947
00:48:08,686 --> 00:48:10,820
this localized standard compare means
948
00:48:10,822 --> 00:48:13,689
alphabetically in the way that people are used to seeing it
949
00:48:13,691 --> 00:48:16,726
in the finder on the Mac, basically, so it's kind of
950
00:48:16,728 --> 00:48:20,963
like user-sensible alphabetical order.
951
00:48:20,965 --> 00:48:23,866
Okay, there's other alphabetical orders that
952
00:48:23,868 --> 00:48:26,068
are more like strict alphabet order.
953
00:48:26,070 --> 00:48:28,938
But maybe they don't deal with diacritics, you know?
954
00:48:28,940 --> 00:48:31,774
They don't look, they don't sort diacritics properly,
955
00:48:31,776 --> 00:48:35,211
things like, or capitalization they might not do right. So
956
00:48:35,213 --> 00:48:37,113
this is a good one. And this is the default, so
957
00:48:37,115 --> 00:48:39,282
you probably don't need to put this in here. But
958
00:48:39,284 --> 00:48:41,350
this can basically be, if this is a string, okay,
959
00:48:41,352 --> 00:48:44,754
if screenName is a string, which it is, then this can be,
960
00:48:44,756 --> 00:48:48,357
any string method. Now there are some string methods that
961
00:48:48,359 --> 00:48:51,894
are magic and are actually done on the database side.
962
00:48:51,896 --> 00:48:53,229
So they're super efficient, okay. And
963
00:48:53,231 --> 00:48:55,598
you can read the documentation to find out which ones.
964
00:48:55,600 --> 00:48:58,434
This is definitely one of them. Okay, so
965
00:48:58,436 --> 00:49:01,504
this method is not actually being called on every single
966
00:49:01,506 --> 00:49:04,407
screen name in the database. You know, in other words, it,
967
00:49:04,409 --> 00:49:06,142
it's not fetching them calling that method,
968
00:49:06,144 --> 00:49:07,977
fetching the next one, calling it, comparing them,
969
00:49:07,979 --> 00:49:11,948
trying to sort it. No, this is happening in the database.
970
00:49:11,950 --> 00:49:15,351
Okay, the database knows how to do the sort. All right, so
971
00:49:15,353 --> 00:49:19,322
that's the sort descriptor. Now, notice that, we,
972
00:49:19,324 --> 00:49:21,791
when we create our fetch request,
973
00:49:21,793 --> 00:49:25,728
we actually provide an array of sort descriptors.
974
00:49:25,730 --> 00:49:28,831
We don't just give one sort descriptor, we do an array.
975
00:49:28,833 --> 00:49:29,498
Why would we need an array?
976
00:49:29,500 --> 00:49:32,001
Well, sometimes you, maybe you're searching for names,
977
00:49:32,003 --> 00:49:35,538
people by name, and you wanna search by last name first and
978
00:49:35,540 --> 00:49:40,409
then by first name, right, the common thing to do. So
979
00:49:40,411 --> 00:49:42,278
you can give an array of sort descriptors.
980
00:49:42,280 --> 00:49:45,114
The first sort descriptor is the last name sort descriptor,
981
00:49:45,116 --> 00:49:48,617
and the second one is the first name sort descriptor,
982
00:49:48,820 --> 00:49:53,089
okay? That's why it's an array. All right, now,
983
00:49:53,091 --> 00:49:56,592
NSPredicate, the real gut of how we do all this.
984
00:49:56,594 --> 00:50:00,529
The predicate is a really flexible object, okay, you,
985
00:50:00,531 --> 00:50:03,165
this is one where you really have to go read NSPredicate
986
00:50:03,167 --> 00:50:05,968
documentation. Okay, it would take me, I could have a whole
987
00:50:05,970 --> 00:50:08,304
lecture on all of the things NSPredicate can do.
988
00:50:08,306 --> 00:50:12,408
The format of NSPredicate is NSPredicate is basically you
989
00:50:12,410 --> 00:50:15,011
have a constructor here, an initializer,
990
00:50:15,013 --> 00:50:19,281
which takes a format string, like this, with little percent
991
00:50:19,283 --> 00:50:21,784
at signs in it that you put then at the end.
992
00:50:21,786 --> 00:50:24,687
See, here's two %@ signs, you put them at the end, joe and
993
00:50:24,689 --> 00:50:29,258
aDate, right? And these values get put into this spot.
994
00:50:29,260 --> 00:50:32,628
It's like printdef, okay, you all know printdef.
995
00:50:32,630 --> 00:50:34,764
It's, who, who doesn't, does everyone know, who,
996
00:50:34,766 --> 00:50:36,232
if you don't know what printdef is,
997
00:50:36,234 --> 00:50:37,366
raise your hand. Okay, so
998
00:50:37,368 --> 00:50:39,802
everyone knows what printdef is, just like printdef,
999
00:50:39,804 --> 00:50:43,839
in that way. But of course, this is, has meaning, okay,
1000
00:50:43,841 --> 00:50:46,642
meaning in terms of what you can search in the database.
1001
00:50:46,644 --> 00:50:49,111
So, here's just some examples. Here, for example,
1002
00:50:49,113 --> 00:50:51,580
I'm searching the database to find all the tweet,
1003
00:50:51,582 --> 00:50:54,216
these are all searches for tweets by the way,
1004
00:50:54,218 --> 00:50:55,351
all these things you're seeing right here,
1005
00:50:55,353 --> 00:50:57,887
searches for tweets. So here I'm trying to find a tweet
1006
00:50:57,889 --> 00:51:00,790
whose text contains case insensitively,
1007
00:51:00,792 --> 00:51:03,993
that's what that square bracket [c] means, this text,
1008
00:51:03,995 --> 00:51:07,663
and the text is the search string, okay? So I have some
1009
00:51:07,665 --> 00:51:11,600
search string, I'm trying to find all the tweets, that,
1010
00:51:11,602 --> 00:51:16,472
contain that text, okay? Or here's another one down here,
1011
00:51:16,474 --> 00:51:20,276
I wanna find all the tweets that were tweeted by Joe and
1012
00:51:20,278 --> 00:51:24,847
were created before or after some date, aDate right there.
1013
00:51:24,849 --> 00:51:30,286
See that? And then here is, an interesting one,
1014
00:51:30,288 --> 00:51:34,256
I'm saying give me all the tweets, not Twitter users,
1015
00:51:34,258 --> 00:51:39,829
tweets whose Twitter screen name is CS193p. So
1016
00:51:39,831 --> 00:51:43,199
this is all of CS19 p's tweets,
1017
00:51:43,201 --> 00:51:47,336
okay? So even though this says tweeter.screenName,
1018
00:51:47,338 --> 00:51:51,407
this is a tweet query. You see that?
1019
00:51:51,409 --> 00:51:55,277
Okay, now here's interesting, th, this is only the tip of
1020
00:51:55,279 --> 00:51:58,147
the iceberg, you can do some pretty powerful things. Here,
1021
00:51:58,149 --> 00:51:58,948
for example, I'm doing a tweet u,
1022
00:51:58,950 --> 00:52:02,318
TwitterUser request down here at the bottom, and
1023
00:52:02,320 --> 00:52:06,088
I'm trying to find all the Twitter users that have tweets
1024
00:52:06,090 --> 00:52:10,893
that contain the search string, okay? So
1025
00:52:10,895 --> 00:52:13,696
that's pretty powerful, since tweets is a too many
1026
00:52:13,698 --> 00:52:18,834
relationship, but it can do that. All right, so you gotta
1027
00:52:18,836 --> 00:52:21,537
read up on NSPredicate to know what you can do.
1028
00:52:21,539 --> 00:52:23,339
In your homework, I'm gonna ask you to do something
1029
00:52:23,341 --> 00:52:25,641
pretty straightforward. So don't worry too much,
1030
00:52:25,643 --> 00:52:28,911
but you're gonna need to not skip the step of going and
1031
00:52:28,913 --> 00:52:32,748
reading this. You can also make compound predicates.
1032
00:52:32,750 --> 00:52:35,050
Basically, you have this NSCompoundPredicate.
1033
00:52:35,052 --> 00:52:37,786
You can make an AND predicate or an OR predicate. And
1034
00:52:37,788 --> 00:52:41,423
you just give it an alid, an array of other predicates,
1035
00:52:41,425 --> 00:52:41,957
and you'll AND them together.
1036
00:52:41,959 --> 00:52:44,860
Now you don't need to do that because you could put OR and
1037
00:52:44,862 --> 00:52:47,863
AND in the predicate string. But sometimes,
1038
00:52:47,865 --> 00:52:51,333
you wanna like calculate which ones you want, okay,
1039
00:52:51,335 --> 00:52:54,370
you will by some if thens or something, you wanna decide
1040
00:52:54,372 --> 00:52:56,839
which ones are there, and so this is a good way to AND
1041
00:52:56,841 --> 00:53:02,011
them together. All right, or and or or. Okay, I'm
1042
00:53:02,013 --> 00:53:04,480
not really gonna talk about this whole slide actually,
1043
00:53:04,482 --> 00:53:10,920
advanced querying, but there are some kind of weird little,
1044
00:53:10,922 --> 00:53:14,523
things you can put in your predicate, like @average,
1045
00:53:14,525 --> 00:53:18,794
which will assume that this is returning an array, and it'll
1046
00:53:18,796 --> 00:53:23,699
average all of these keys in that array. Okay, so here for
1047
00:53:23,701 --> 00:53:28,404
example, I have tweets.tweet, I'm basically looking at all
1048
00:53:28,406 --> 00:53:32,274
of the tweets that this Twitter user has tweeted.
1049
00:53:32,276 --> 00:53:33,876
And I'm averaging the latitude,
1050
00:53:33,878 --> 00:53:37,580
assuming they had a latitude and longitude on the tweet.
1051
00:53:37,582 --> 00:53:40,549
I'm averaging the latitude to find the average latitude at
1052
00:53:40,551 --> 00:53:42,885
which someone tweets. Okay, that's kinda weird,
1053
00:53:42,887 --> 00:53:46,355
but you can do that. So same thing with counts, okay?
1054
00:53:46,357 --> 00:53:49,391
Now you're seeing counts here, but in, in your homework,
1055
00:53:49,393 --> 00:53:52,761
you're gonna be asked to do a count, but not with this.
1056
00:53:52,763 --> 00:53:53,462
Okay, this won't work for
1057
00:53:53,464 --> 00:53:54,897
the kind of counting I'm asking you to do.
1058
00:53:54,899 --> 00:53:58,000
You're just gonna have to have code that counts for that.
1059
00:53:58,002 --> 00:53:59,268
Okay, so don't get confused by this.
1060
00:53:59,270 --> 00:54:03,272
Do not use @count in your homework, all right. And
1061
00:54:03,274 --> 00:54:06,275
down here, it's talking about this class NSExpression,
1062
00:54:06,277 --> 00:54:09,478
which lets you create these arbitrary expressions to
1063
00:54:09,480 --> 00:54:13,015
search for. Again, I'm not gonna talk about it,
1064
00:54:13,017 --> 00:54:14,416
you don't need to do it for your homework.
1065
00:54:14,418 --> 00:54:16,719
You can certainly take a look at the class NSExpression.
1066
00:54:16,721 --> 00:54:21,590
It's pretty amazing what's in there. Okay, all right, so
1067
00:54:21,592 --> 00:54:23,859
let's put this all together, all this querying business,
1068
00:54:23,861 --> 00:54:25,628
so I'm gonna create this fetch request for
1069
00:54:25,630 --> 00:54:29,498
a Twitter user. Okay, I wanna have all the tweets
1070
00:54:29,500 --> 00:54:33,569
that this Twitter user has created in the last 24 hours.
1071
00:54:33,571 --> 00:54:37,473
So how do I do that? Okay, I'm gonna fetch into, actually
1072
00:54:37,475 --> 00:54:40,509
I know I'm gonna create all the, sorry, be clear here.
1073
00:54:40,511 --> 00:54:44,079
I'm fetching all the Twitter users who have tweeted in
1074
00:54:44,081 --> 00:54:47,049
the last 24 hours. Okay, so I'm fetching Twitter users.
1075
00:54:47,051 --> 00:54:50,886
So that's my entity, I'm gonna create the time yesterday,
1076
00:54:50,888 --> 00:54:54,223
which is just the time interval since now of minus 24
1077
00:54:54,225 --> 00:54:56,191
hours. This is in seconds, okay?
1078
00:54:56,193 --> 00:54:58,560
Then I'm gonna create the predicate,
1079
00:54:58,562 --> 00:55:02,364
which is any tweets that were created after yesterday,
1080
00:55:02,366 --> 00:55:06,935
24 hours ago, okay? See that predicate? Pretty fun. And
1081
00:55:06,937 --> 00:55:10,739
then I'm gonna sort the result by the name of the Twitter
1082
00:55:10,741 --> 00:55:14,176
user, not a screen name, but the actual name.
1083
00:55:14,178 --> 00:55:15,411
And ascending is true, so
1084
00:55:15,413 --> 00:55:19,615
I want the As at the beginning and the Zs at the end, okay?
1085
00:55:19,617 --> 00:55:23,285
So this, I create this request. Now I have request,
1086
00:55:23,287 --> 00:55:28,524
how do I get the array, which is all of these things, okay?
1087
00:55:28,526 --> 00:55:31,960
We do that with a method in your managedObjectContext.
1088
00:55:31,962 --> 00:55:33,662
I told you you need a managedObjectContext for
1089
00:55:33,664 --> 00:55:35,431
everything, and you do, creating, and
1090
00:55:35,433 --> 00:55:38,701
also for fetching. You're gonna use this method in your
1091
00:55:38,703 --> 00:55:40,869
context called executeFetchRequest, and
1092
00:55:40,871 --> 00:55:43,505
you just give it the NSFetchRequest.
1093
00:55:43,507 --> 00:55:46,408
It returns an NSArray, okay, or an array,
1094
00:55:46,410 --> 00:55:50,646
a bridge to an array, okay, of all the things.
1095
00:55:50,648 --> 00:55:52,915
Now if that array is empty, not nil,
1096
00:55:52,917 --> 00:55:56,752
but empty, that means nothing matches your request.
1097
00:55:56,754 --> 00:55:58,053
There's nothing in the database that matched that.
1098
00:55:58,055 --> 00:56:00,622
In other words, in the, using the previous slide, there
1099
00:56:00,624 --> 00:56:03,459
are no Twitter users who have tweeted in the last 24 hours.
1100
00:56:03,461 --> 00:56:07,262
That's empty array. Otherwise it's gonna return a sorted
1101
00:56:07,264 --> 00:56:10,666
array of all the Twitter users, sorted by their name,
1102
00:56:10,668 --> 00:56:13,669
who have tweeted in the last 24 hours. Okay?
1103
00:56:13,671 --> 00:56:15,137
And the objects in there, in that array,
1104
00:56:15,139 --> 00:56:18,507
will be Twitter user, instances of Twitter user.
1105
00:56:18,509 --> 00:56:20,242
Okay, cuz that's what I searched for.
1106
00:56:20,244 --> 00:56:24,546
Now notice that I have to try, okay, context try, okay,
1107
00:56:24,548 --> 00:56:30,719
that's because this method throws. Now, if this throws,
1108
00:56:30,721 --> 00:56:33,889
it probably means there£s some problem with your database.
1109
00:56:33,891 --> 00:56:36,892
Okay? Like maybe your database is not opened, maybe tried to
1110
00:56:36,894 --> 00:56:40,729
send this to a UI managed object manage documents
1111
00:56:40,731 --> 00:56:42,731
context and you never opened it, so it£s not open or
1112
00:56:42,733 --> 00:56:48,570
something like that. You could ignore the throw, by doing
1113
00:56:48,572 --> 00:56:52,074
tri question mark. The thing that tri question mark does
1114
00:56:52,076 --> 00:56:55,077
that you have to realize is, it turns whatever this is,
1115
00:56:55,079 --> 00:56:58,080
this method call. Whatever it returns, it turns it into
1116
00:56:58,082 --> 00:57:03,285
an optional. So execute fetch request returns an NS array,
1117
00:57:03,287 --> 00:57:07,189
or an array. So, by putting tri question mark, it makes
1118
00:57:07,191 --> 00:57:11,560
this now return an optional array. And why is that?
1119
00:57:11,562 --> 00:57:16,765
That's because if it throws it's gonna return nill. Okay?
1120
00:57:16,767 --> 00:57:19,568
So it's a little funky to realize this but
1121
00:57:19,570 --> 00:57:23,305
this just gonna turn this into an optional array, and so
1122
00:57:23,307 --> 00:57:27,009
let users, users right there, is going to be of type,
1123
00:57:27,011 --> 00:57:31,513
optional array. Okay optional array of And
1124
00:57:31,515 --> 00:57:34,650
his managed object actually, to be exact. All right so
1125
00:57:34,652 --> 00:57:38,253
a lot of times we do do try question mark here because we
1126
00:57:38,255 --> 00:57:42,324
don't really know what to do to handle an error so if we do
1127
00:57:42,326 --> 00:57:46,428
a fetch and it comes back nil, and there was some problem,
1128
00:57:46,430 --> 00:57:49,498
it's kind of like [SOUND] we're reporting error
1129
00:57:49,500 --> 00:57:53,402
to the user or something but it's not like much we can do.
1130
00:57:53,404 --> 00:57:57,172
Okay, so sometimes, this is a case where we might do try
1131
00:57:57,174 --> 00:58:01,577
question mark there. Okay, so that's it, very simple and
1132
00:58:01,579 --> 00:58:02,978
now you have the array of objects back,
1133
00:58:02,980 --> 00:58:06,215
you can do whatever you want with them.
1134
00:58:06,217 --> 00:58:09,651
I want to talk a little about performance. What if you had
1135
00:58:09,653 --> 00:58:12,321
a million objects and you said show me all the Twitter user,
1136
00:58:12,323 --> 00:58:15,224
a million tweets or something or a million Twitter users and
1137
00:58:15,226 --> 00:58:18,894
you said, show me all the Twitter users that tweeted in
1138
00:58:18,896 --> 00:58:22,965
the last 24 hours. And there were 750, or 7,000 of them.
1139
00:58:22,967 --> 00:58:26,168
Are you really gonna get an array with 7,000 objects
1140
00:58:26,170 --> 00:58:28,971
all of, each of which has a screen name and the name and
1141
00:58:28,973 --> 00:58:32,174
whatever else it. I mean, that would be massive, right.
1142
00:58:32,176 --> 00:58:34,142
And the answer is no, you don't get that.
1143
00:58:34,144 --> 00:58:38,113
You get this kind of magical thing which is an array of
1144
00:58:38,115 --> 00:58:42,684
husks of those objects if you want to think of it that way.
1145
00:58:42,686 --> 00:58:43,886
So they're not really there and
1146
00:58:43,888 --> 00:58:46,922
in fact if you tried to print them, if you used print to
1147
00:58:46,924 --> 00:58:50,025
print them, it would just say this is an NS managed object,
1148
00:58:50,027 --> 00:58:51,994
it wouldn't show you the screen name or the name or
1149
00:58:51,996 --> 00:58:55,297
anything else about it because it doesn't actually pull that
1150
00:58:55,299 --> 00:59:00,502
data Until you ask for it. Until you ask specifically for
1151
00:59:00,504 --> 00:59:03,705
a Twitter user's name or his screen name.
1152
00:59:03,707 --> 00:59:05,641
It doesn't get that out of the database.
1153
00:59:05,643 --> 00:59:09,077
This is called faulting. The objects live in this kind of
1154
00:59:09,079 --> 00:59:11,280
husk state where they don't actually have their data. And
1155
00:59:11,282 --> 00:59:14,416
when you ask for them the data gets faulted in. Now when that
1156
00:59:14,418 --> 00:59:17,185
faulting happens, it might be happening in big bunches,
1157
00:59:17,187 --> 00:59:18,086
because it might be more efficient for
1158
00:59:18,088 --> 00:59:21,757
the database to fault in a hundred at a time. Okay? But
1159
00:59:21,759 --> 00:59:22,958
it's all happening behind the scenes.
1160
00:59:22,960 --> 00:59:25,160
The only reason I even mention it is because you might be
1161
00:59:25,162 --> 00:59:28,497
trying to print these things out, and you're like, what?
1162
00:59:28,499 --> 00:59:31,400
Where's my name and screen name? Okay? And you'll know
1163
00:59:31,402 --> 00:59:34,670
why it's because it's not been faulted yet. Okay? So
1164
00:59:34,672 --> 00:59:36,371
if you really want the name or screen name, you want,
1165
00:59:36,373 --> 00:59:42,210
you have to .name so it faults it in, and can see it. Okay?
1166
00:59:42,646 --> 00:59:45,380
All right let's talk about thread safety with core data.
1167
00:59:45,382 --> 00:59:50,619
Okay, NSManagedObjectContext is not thread safe. So
1168
00:59:50,621 --> 00:59:54,289
you cannot have a context fork off some
1169
00:59:54,291 --> 00:59:56,592
closure on another thread with dispatch and
1170
00:59:56,594 --> 01:00:00,195
access it. Okay? So that sounds really bad. Because
1171
01:00:00,197 --> 01:00:03,365
a database you can imagine I'd want to have another thread
1172
01:00:03,367 --> 01:00:05,500
doing some heavy intensive database thing.
1173
01:00:05,502 --> 01:00:06,802
Like loading the database up or
1174
01:00:06,804 --> 01:00:09,404
doing some complicated queries or something like that.
1175
01:00:09,406 --> 01:00:11,506
And you're telling me I can't do it?
1176
01:00:11,508 --> 01:00:14,543
Because it's not thread same? Well no of course you can do
1177
01:00:14,545 --> 01:00:18,480
it. And the way you have to do it is ns managed object each
1178
01:00:18,482 --> 01:00:24,119
NSManagedObejct instance has to have its own thread. Okay.
1179
01:00:24,121 --> 01:00:26,622
Now, the cool thing is you can have multiple
1180
01:00:26,624 --> 01:00:30,826
NSManagedObjectContext on the same database. Okay? So, how
1181
01:00:30,828 --> 01:00:34,062
does that work? So, you have this NSManagedObjectContext.
1182
01:00:34,064 --> 01:00:35,631
It's writing into the database.
1183
01:00:35,633 --> 01:00:39,201
When you hit save that save goes out to the database and
1184
01:00:39,203 --> 01:00:43,805
other ManagedObjectContext will start seeing it. Okay?
1185
01:00:43,807 --> 01:00:45,774
When they do fetches they'll see it.
1186
01:00:45,776 --> 01:00:47,743
So it basically works through the saves.
1187
01:00:47,745 --> 01:00:50,912
When you save through one context boom the other ones
1188
01:00:50,914 --> 01:00:53,749
start seeing it. Automatically as soon as they, next time
1189
01:00:53,751 --> 01:00:56,952
they fetch they're gonna automatically see it. Okay, so
1190
01:00:56,954 --> 01:01:00,355
it's really kind of cool that it just uses threads,
1191
01:01:00,357 --> 01:01:02,190
okay, queues, to do this.
1192
01:01:02,192 --> 01:01:04,826
Now that does mean that the cues to use them on, they have
1193
01:01:04,828 --> 01:01:07,896
to be serial queues, can't have concurrent queues because
1194
01:01:07,898 --> 01:01:10,365
then you'd have one manage object in different threads,
1195
01:01:10,367 --> 01:01:13,502
because a concurrent queue can fire off multiple threads to
1196
01:01:13,504 --> 01:01:15,937
go execute its little blocks. All right?
1197
01:01:15,939 --> 01:01:19,775
So it has to be serial queue. Now the kind of,
1198
01:01:19,777 --> 01:01:23,011
NSMangedObjectContext that you get by doing that app delegate
1199
01:01:23,013 --> 01:01:26,615
thing and the one you get from doing UI manage documents,
1200
01:01:26,617 --> 01:01:31,086
those are both main cue in this managed object context.
1201
01:01:31,088 --> 01:01:35,023
It could only be used in the main cue so don't ever like,
1202
01:01:35,025 --> 01:01:37,426
fork those off and start using those on another queue.
1203
01:01:37,428 --> 01:01:40,195
Now you can create your own NSManagedObjectContext on
1204
01:01:40,197 --> 01:01:43,365
other queues if you want. There's an initializer for it.
1205
01:01:43,367 --> 01:01:44,266
I'm not really gonna talk about that.
1206
01:01:44,268 --> 01:01:46,735
You're not gonna need to do that for this class.
1207
01:01:46,737 --> 01:01:51,306
But that's how you would do concurrency, okay?
1208
01:01:51,308 --> 01:01:53,642
Now when you have, because of this, because
1209
01:01:53,644 --> 01:01:56,511
you can have this multiple context, you gotta know about
1210
01:01:56,513 --> 01:01:58,880
this method right here, performBlock.
1211
01:01:58,882 --> 01:02:02,584
Very important method in manage ManageObjectContext.
1212
01:02:02,586 --> 01:02:05,987
And it basically takes a closure here. Closure that
1213
01:02:05,989 --> 01:02:09,925
take no arguments, returns nothing, and inside this block
1214
01:02:09,927 --> 01:02:13,361
you always know with 100% certainty whatever code you're
1215
01:02:13,363 --> 01:02:17,966
executing in here is going to be performed on this context,
1216
01:02:17,968 --> 01:02:22,871
safe queue. Okay, the queue that it was created on,
1217
01:02:22,873 --> 01:02:24,406
the queue that it's running on,
1218
01:02:24,408 --> 01:02:27,309
the queue that won't cause any problems, always.
1219
01:02:27,311 --> 01:02:31,646
Okay, so really anytime you do anything with core data
1220
01:02:31,648 --> 01:02:34,549
any time you access an NSManagedObjectContext,
1221
01:02:34,551 --> 01:02:38,153
or any of the NSManagedObjects you get out of it, you want to
1222
01:02:38,155 --> 01:02:42,224
do it inside a performBlock. Did you hear what I just said?
1223
01:02:42,226 --> 01:02:45,427
I'm going to say it again. performBlock wants to be
1224
01:02:45,429 --> 01:02:50,298
wrapped around every access to any NSManagedObjectContext or
1225
01:02:50,300 --> 01:02:54,369
NSManagedObject of any kind. Okay. Now
1226
01:02:54,371 --> 01:02:57,405
in your homework you're only ever gonna have one context or
1227
01:02:57,407 --> 01:02:59,574
you might say what a waste. Why do you put these
1228
01:02:59,576 --> 01:03:01,109
stupid perform blocks around everything?
1229
01:03:01,111 --> 01:03:02,778
I know everything's in the main context.
1230
01:03:02,780 --> 01:03:05,080
Well the answer you'll want to get used to doing this.
1231
01:03:05,082 --> 01:03:08,683
Okay? Because maybe down the road you do add another thread
1232
01:03:08,685 --> 01:03:10,752
that loads up the database with more tweets or
1233
01:03:10,754 --> 01:03:13,922
something like that and also, it starts breaking if
1234
01:03:13,924 --> 01:03:17,659
you're not performing things on the right context, okay.
1235
01:03:17,661 --> 01:03:18,660
So just get in the habit of doing this.
1236
01:03:18,662 --> 01:03:22,130
So you can also perform block and wait, which will cause
1237
01:03:22,132 --> 01:03:24,566
this code to be executed on the safe queue and
1238
01:03:24,568 --> 01:03:29,104
wait till it's done and then continue on this queue, okay.
1239
01:03:29,106 --> 01:03:33,575
So you can do that as well. So, always wrap perform block
1240
01:03:33,577 --> 01:03:39,447
around anything you do in core data.
1241
01:03:39,817 --> 01:03:44,753
All right? Quick thing on the parent context. Most context,
1242
01:03:44,755 --> 01:03:48,456
most management contexts have a parentContext, all right?
1243
01:03:48,458 --> 01:03:51,159
That parentContext is either the context that actually
1244
01:03:51,161 --> 01:03:54,296
writing it to the database, or in some cases, it's another,
1245
01:03:54,298 --> 01:03:57,365
and it's may context. So that when you save, it's actually
1246
01:03:57,367 --> 01:04:00,302
saving to another context, and then that context saves again.
1247
01:04:00,304 --> 01:04:02,938
UI manage document works this way.
1248
01:04:02,940 --> 01:04:06,241
Okay, and what's kinda cool is that that parent context is
1249
01:04:06,243 --> 01:04:08,643
almost always on a different queue.
1250
01:04:08,645 --> 01:04:12,247
So if you're using UI managed documents you can actually
1251
01:04:12,249 --> 01:04:15,317
do a little trick which is if you wanna run something for
1252
01:04:15,319 --> 01:04:19,621
that database on another queue just performBlock
1253
01:04:19,623 --> 01:04:20,622
on the parentContext.
1254
01:04:20,624 --> 01:04:24,359
ParentContext is just a method in ManagedObject context,
1255
01:04:24,361 --> 01:04:27,829
right or for a property. Just do performBlock on that.
1256
01:04:27,831 --> 01:04:30,765
Now you'll be performing it on a different queue off the main
1257
01:04:30,767 --> 01:04:33,168
queue, okay. And it'll be in that database.
1258
01:04:33,170 --> 01:04:35,570
Everything will be fine so it's kind of a trick.
1259
01:04:35,572 --> 01:04:37,806
Okay, again, you won't need that for your assignment,
1260
01:04:37,808 --> 01:04:40,909
but just understand that you can do that.
1261
01:04:41,511 --> 01:04:44,946
All right, so there's so much more to Core Data, no wait,
1262
01:04:44,948 --> 01:04:48,583
I mean we've already been here an hour so and barely covered
1263
01:04:48,585 --> 01:04:51,586
the basics. So there's optimistic locking in there,
1264
01:04:51,588 --> 01:04:55,290
it has full Undo/Redo which is incredible. Specify thing like
1265
01:04:55,292 --> 01:04:57,859
staleness, you know, when I have to refetch this thing,
1266
01:04:57,861 --> 01:04:59,728
how much time goes by before I have to refetch.
1267
01:04:59,730 --> 01:05:02,731
All that stuff. You have to go look at the documentation for
1268
01:05:02,733 --> 01:05:05,700
NS Managed Object Context. Okay, that's really the core
1269
01:05:05,702 --> 01:05:08,069
of where this stuff is all happening.
1270
01:05:08,071 --> 01:05:10,605
Eh, to find out more about it. Again,
1271
01:05:10,607 --> 01:05:11,873
I'm not gonna ask you to do any of this for
1272
01:05:11,875 --> 01:05:15,243
your homework but you gotta know that it's out there or
1273
01:05:15,245 --> 01:05:16,111
when you get in the real world and
1274
01:05:16,113 --> 01:05:18,680
start doing this you'll be missing opportunities to do
1275
01:05:18,682 --> 01:05:22,317
cool features. All right, the last thing I wanna talk about
1276
01:05:22,319 --> 01:05:25,654
is the interrelationship between Core Data and
1277
01:05:25,656 --> 01:05:27,889
UITableView. As you can imagine,
1278
01:05:27,891 --> 01:05:31,092
if you got this huge database of stuff,
1279
01:05:31,094 --> 01:05:35,530
a great place to show it is in a TableView. Okay, in fact,
1280
01:05:35,532 --> 01:05:38,400
99% of the time, either a TableView or a CollectionView
1281
01:05:38,402 --> 01:05:42,404
is how you're gonna show the stuff in a big database, okay.
1282
01:05:42,406 --> 01:05:47,108
And that's so common that iOS provides this awesome class
1283
01:05:47,110 --> 01:05:51,680
NSFetchedResultsController okay, which is a call which
1284
01:05:51,682 --> 01:05:56,885
will hook up an NSFetchRequest to a UITableView.
1285
01:05:56,887 --> 01:06:00,822
And not just hook it up once but hook it up so that if
1286
01:06:00,824 --> 01:06:03,458
the database changes in any way that the FetchRequest
1287
01:06:03,460 --> 01:06:08,330
would return different results it updates the table. Okay, so
1288
01:06:08,332 --> 01:06:10,332
the database can be changing behind the scenes.
1289
01:06:10,334 --> 01:06:12,701
The table is just always staying in sync with it.
1290
01:06:12,703 --> 01:06:13,068
So that fetch request and
1291
01:06:13,070 --> 01:06:16,104
that table are always in sync, okay, which is really,
1292
01:06:16,106 --> 01:06:18,273
really cool. Okay, it makes it really easy for
1293
01:06:18,275 --> 01:06:21,276
you to implement your table views with stuff
1294
01:06:21,278 --> 01:06:22,844
out of the database. Okay, so
1295
01:06:22,846 --> 01:06:27,048
how do you use this NSFetch, FetchedResultsController?
1296
01:06:27,050 --> 01:06:28,483
Well, first of all, let's talk about how,
1297
01:06:28,485 --> 01:06:30,051
how FetchResultsController actually works.
1298
01:06:30,053 --> 01:06:33,788
It's very simple. It provides methods to implement all those
1299
01:06:33,790 --> 01:06:36,458
UITableView delegate methods like number of sections and
1300
01:06:36,460 --> 01:06:39,327
tables, number of rows and sections. The only one it
1301
01:06:39,329 --> 01:06:41,730
doesn't implement is self row and index path.
1302
01:06:41,732 --> 01:06:43,565
You still have to implement that. Because of course,
1303
01:06:43,567 --> 01:06:47,202
self row and index path knows about, you know, the custom UI
1304
01:06:47,204 --> 01:06:50,271
cells or whatever, so, you have to be the one who's
1305
01:06:50,273 --> 01:06:53,241
putting that data on screen in the right way. But in terms of
1306
01:06:53,243 --> 01:06:56,611
all the other, things in the UITableView data source
1307
01:06:56,613 --> 01:06:59,447
even things like section hea- headers and all that stuff,
1308
01:06:59,449 --> 01:07:01,850
NSFetchedResultsController will do all that for you.
1309
01:07:01,852 --> 01:07:03,418
It has methods to implement all those things.
1310
01:07:03,420 --> 01:07:06,254
So here's a couple examples for what it looks like here.
1311
01:07:06,256 --> 01:07:09,324
Okay? The other thing you can do, which is kinda cool,
1312
01:07:09,326 --> 01:07:11,960
well, I'll talk about that in a second. Let's talk about
1313
01:07:11,962 --> 01:07:13,928
cellForRowAtIndexPath, since you're responsible for
1314
01:07:13,930 --> 01:07:17,198
im- implementing that. The key method you need to know from
1315
01:07:17,200 --> 01:07:19,801
FetchedResultsController is object and
1316
01:07:19,803 --> 01:07:22,637
index path, okay. This is a method that you can send
1317
01:07:22,639 --> 01:07:25,673
to it. You give it an index path into the table and
1318
01:07:25,675 --> 01:07:30,378
it will return you the NSManagedObject at that row.
1319
01:07:30,380 --> 01:07:33,415
Okay, now this might be a subclass of NSManagedObject
1320
01:07:33,417 --> 01:07:34,682
like Tweet or Twitter User, but
1321
01:07:34,684 --> 01:07:37,886
it's gonna return it to you so in your cellForRowAtIndexPath,
1322
01:07:37,888 --> 01:07:42,824
you're gonna call this, okay, of this index path
1323
01:07:42,826 --> 01:07:45,493
to get the object that's at that row and then you're gonna
1324
01:07:45,495 --> 01:07:49,097
use that object to fill out the information in that row.
1325
01:07:49,099 --> 01:07:51,433
Okay, that's what cellForRowAtIndexPath does.
1326
01:07:51,435 --> 01:07:54,669
Everyone understand this? Super important you understand
1327
01:07:54,671 --> 01:07:56,638
this cuz otherwise, you'll be like, how do I, okay,
1328
01:07:56,640 --> 01:08:00,775
cellForRowAtIndexPath? This is how you do it. Okay. So
1329
01:08:00,777 --> 01:08:03,845
this FetchedResultsController is probably gonna be a var in
1330
01:08:03,847 --> 01:08:06,714
your UITableViewController. Okay, that you're going to
1331
01:08:06,716 --> 01:08:09,951
have created with a fetched results, with the NS fetched
1332
01:08:09,953 --> 01:08:13,154
request. And then with your cellForRowAtIndexPath you're
1333
01:08:13,156 --> 01:08:15,690
gonna find out each of the things that are in
1334
01:08:15,692 --> 01:08:19,861
the database that match your fetch. One per row, and
1335
01:08:19,863 --> 01:08:25,533
this is gonna return it. Okay? All right, so how do we create
1336
01:08:25,535 --> 01:08:28,770
an NSFetchedResultsController by the way? All you do,
1337
01:08:28,772 --> 01:08:31,706
really all you have to do is create that NS fetch request
1338
01:08:31,708 --> 01:08:34,142
that's it, okay? So for example
1339
01:08:34,144 --> 01:08:36,177
here I'm going to create a FetchResultsController.
1340
01:08:36,179 --> 01:08:38,980
You can see that its initializer takes the fetch
1341
01:08:38,982 --> 01:08:42,383
request. Obviously the context, and
1342
01:08:42,385 --> 01:08:47,155
then this is kind of a cool thing, the sectionNameKeyPath,
1343
01:08:47,157 --> 01:08:49,691
if all of the objects in your database, okay,
1344
01:08:49,693 --> 01:08:53,995
know the section they should be in, they have some key in
1345
01:08:53,997 --> 01:08:55,630
there that is the section they're supposed to be in.
1346
01:08:55,632 --> 01:08:59,334
Then you can specify that sec that key right here and it'll
1347
01:08:59,336 --> 01:09:02,604
automatically do the sections for you. Okay. The only
1348
01:09:02,606 --> 01:09:05,673
thing is you gotta make sure you sort the result of your
1349
01:09:05,675 --> 01:09:10,245
fetched sort in the same order as those sections sort. But
1350
01:09:10,247 --> 01:09:12,914
it'll do that for you. And then there's cache right here.
1351
01:09:12,916 --> 01:09:16,451
It'll actually cache the results on disk, okay.
1352
01:09:16,453 --> 01:09:19,053
So if you have a really complicated fetch that might
1353
01:09:19,055 --> 01:09:21,389
take a lot of resources, it will cache the result.
1354
01:09:21,391 --> 01:09:26,394
The only thing about this is you cannot change the request.
1355
01:09:26,396 --> 01:09:28,229
The data can be changing all the time but
1356
01:09:28,231 --> 01:09:29,964
the request itself, the predicate and
1357
01:09:29,966 --> 01:09:32,200
the source scrubbers has to be the same.
1358
01:09:32,202 --> 01:09:32,467
If you ever change it,
1359
01:09:32,469 --> 01:09:35,236
then this cache obviously becomes kinda useless. All
1360
01:09:35,238 --> 01:09:38,573
right, so how would we create a FetchResultsController doing
1361
01:09:38,575 --> 01:09:41,075
this? I'm gonna create the request. So like I'm gonna
1362
01:09:41,077 --> 01:09:43,378
fetch Tweets here. I'm gonna put Tweets in my TableView.
1363
01:09:43,380 --> 01:09:46,314
So each row of my TableView is gonna be a different Tweet.
1364
01:09:46,316 --> 01:09:48,383
I'm gonna sort by the created so
1365
01:09:48,385 --> 01:09:50,952
the newer tweets maybe are gonna be at the beginning.
1366
01:09:50,954 --> 01:09:54,522
Ad then my predicate is going to be all Tweeters who
1367
01:09:54,524 --> 01:09:56,891
whose Tweeter's name equals a certain name.
1368
01:09:56,893 --> 01:09:59,928
So I'm looking for all the tweets by a certain tweeter
1369
01:09:59,930 --> 01:10:01,162
who has a certain name, okay.
1370
01:10:01,164 --> 01:10:04,265
And when it gets this result, each row in my TableView
1371
01:10:04,267 --> 01:10:07,802
is going to be that tweet that matches. Okay? And
1372
01:10:07,804 --> 01:10:10,672
FetchResultsController's gonna make sure that's always true,
1373
01:10:10,674 --> 01:10:13,308
even if I added in another tweet to the database
1374
01:10:13,310 --> 01:10:14,576
that matched it, the TableView would
1375
01:10:14,578 --> 01:10:17,111
add in another row at the bottom automatically.
1376
01:10:17,113 --> 01:10:22,684
Okay, yeah I talked about the cacheName. And yeah, this key
1377
01:10:22,686 --> 01:10:25,386
that says which attribute is the section name has to sort
1378
01:10:25,388 --> 01:10:30,792
in the same order as the sort descripter. Sort descripters.
1379
01:10:30,794 --> 01:10:35,163
Okay, all right. The FetchResultsController also
1380
01:10:35,165 --> 01:10:38,866
while it's watching the database, okay, the, well,
1381
01:10:38,868 --> 01:10:40,568
I told you it watches the database and
1382
01:10:40,570 --> 01:10:40,735
keeps them in sync.
1383
01:10:40,737 --> 01:10:42,570
The way it does that is it has a delegate,
1384
01:10:42,572 --> 01:10:45,406
the FetchResultsController has a delegate, okay?
1385
01:10:45,408 --> 01:10:47,609
So here's one of its delegate methods.
1386
01:10:47,611 --> 01:10:49,110
Controller did change object and
1387
01:10:49,112 --> 01:10:52,680
index paths forChangeType: newIndexPath. Okay, here,
1388
01:10:52,682 --> 01:10:55,049
see how it's basically noticing the database change,
1389
01:10:55,051 --> 01:10:58,186
telling you what changed, tell you what index paths changed
1390
01:10:58,188 --> 01:11:00,655
in the TableView. It's basically telling you exactly
1391
01:11:00,657 --> 01:11:05,126
what to do cuz the database changed. Now in here you're
1392
01:11:05,128 --> 01:11:08,329
supposed to call all the UI TableView methods that will be
1393
01:11:08,331 --> 01:11:11,299
appropriate to cause this to update the table, okay.
1394
01:11:11,301 --> 01:11:15,570
Insert a row, put something there, whatever, delete a row,
1395
01:11:15,572 --> 01:11:15,837
all that. Well,
1396
01:11:15,839 --> 01:11:17,939
now you're probably starting to think hey I thought
1397
01:11:17,941 --> 01:11:21,342
this NSFetchResultsController was supposed to be easy. But
1398
01:11:21,344 --> 01:11:23,344
I gotta implement all this? In here? For
1399
01:11:23,346 --> 01:11:26,281
all these delegate methods, eh that's just too hard. Okay,
1400
01:11:26,283 --> 01:11:29,584
well lucky for you, we're gonna provide a class for
1401
01:11:29,586 --> 01:11:31,819
you called CoreDataTableViewController,
1402
01:11:31,821 --> 01:11:36,991
something we wrote that does all that for you. Okay, this,
1403
01:11:36,993 --> 01:11:39,827
all that stuff it does for you, you can actually see all
1404
01:11:39,829 --> 01:11:43,097
that in the documentation for NSFetchResultsController right
1405
01:11:43,099 --> 01:11:45,366
at the top. It actually shows you the code
1406
01:11:45,368 --> 01:11:45,733
that you want to use.
1407
01:11:45,735 --> 01:11:48,069
Unfortunately it's in Objective-C, so
1408
01:11:48,071 --> 01:11:49,404
we've written it all in Swift for
1409
01:11:49,406 --> 01:11:52,740
you, and provided this class CoreDataTableViewController so
1410
01:11:52,742 --> 01:11:55,810
you're just going to make this TableViewController be your
1411
01:11:55,812 --> 01:11:59,747
subclass or your super class of your TableViewController so
1412
01:11:59,749 --> 01:12:04,319
that you'll inherit all this NSFetchResultsController
1413
01:12:04,321 --> 01:12:04,819
functionality. And
1414
01:12:04,821 --> 01:12:08,923
all you need to do to make it work is, it has a var, this
1415
01:12:08,925 --> 01:12:12,226
CoreDataTableViewController only has one public thing
1416
01:12:12,228 --> 01:12:15,229
which is a var which is a FetchResultsController,
1417
01:12:15,231 --> 01:12:18,099
you just need to create this FetchResultsController
1418
01:12:18,101 --> 01:12:22,036
which means creating the NSFetchResult fetch request,
1419
01:12:22,038 --> 01:12:23,304
and then set this var.
1420
01:12:23,306 --> 01:12:25,206
And once you set this var, it's automatically going to
1421
01:12:25,208 --> 01:12:28,309
start updating your table. Okay now of course you're
1422
01:12:28,311 --> 01:12:30,278
gonna have to implement cellForRowAtIndexPath,
1423
01:12:30,280 --> 01:12:32,914
because you gotta load up your thing however you're gonna
1424
01:12:32,916 --> 01:12:35,450
load it up. But that's all you have to do.
1425
01:12:35,452 --> 01:12:40,421
cellForRowAtIndexPath, set this var, you're winning.
1426
01:12:40,423 --> 01:12:46,894
Okay. Now that's it for Core Data. Remember that
1427
01:12:46,896 --> 01:12:50,365
Assignment Four has nothing to do with Core Data. Okay.
1428
01:12:50,367 --> 01:12:54,102
So do not do anything with Core Data in Assignment Four.
1429
01:12:54,104 --> 01:12:56,871
Assignment Five is gonna be a lot of core data.
1430
01:12:56,873 --> 01:12:59,173
Okay. So I mean I have to teach you this
1431
01:12:59,175 --> 01:13:01,209
stuff before I give it to you, like I did for Assignment
1432
01:13:01,211 --> 01:13:02,643
Four, but then I have to teach you the stuff for
1433
01:13:02,645 --> 01:13:04,912
the next thing and sometimes people get confused and they
1434
01:13:04,914 --> 01:13:07,014
think the thing I'm teaching now goes into the assignment
1435
01:13:07,016 --> 01:13:09,517
that went out a couple days ago, but no. Okay so, A4,
1436
01:13:09,519 --> 01:13:14,589
Assignment Four, no Core Data. A5, all Core Data basically.
1437
01:13:14,591 --> 01:13:18,359
Okay, so next day, week I'll do a demo on this, okay?
1438
01:13:18,361 --> 01:13:19,994
On Monday I'm gonna do a big old demo where
1439
01:13:19,996 --> 01:13:22,330
we're gonna do all of the stuff I talked about today, so
1440
01:13:22,332 --> 01:13:24,932
you can see how it all works in action as always. And
1441
01:13:24,934 --> 01:13:26,834
then you'll have A5 go out that's on Monday, and
1442
01:13:26,836 --> 01:13:30,772
that's gonna be due the next Monday. On Wednesday, I'm
1443
01:13:30,774 --> 01:13:33,207
gonna do Auto Layout because I've kind of been hinting at
1444
01:13:33,209 --> 01:13:35,343
Auto Layout, showing you a little bit here and
1445
01:13:35,345 --> 01:13:36,778
there but now I'm gonna talk about really how
1446
01:13:36,780 --> 01:13:40,148
Auto Layout works to lay out you're, user interface.
1447
01:13:40,150 --> 01:13:42,583
You can use stack view for a lot of things but sometimes
1448
01:13:42,585 --> 01:13:44,952
you need a little bit of extra layout capabilities and
1449
01:13:44,954 --> 01:13:47,555
next week I'm also gonna talk about the requirements for
1450
01:13:47,557 --> 01:13:51,726
your final project, okay? What you are required to do
1451
01:13:51,728 --> 01:13:53,494
to have a successful final project.
1452
01:13:53,496 --> 01:13:56,197
And, so it's time, now, for you to start thinking a little
1453
01:13:56,199 --> 01:13:59,233
bit about what you might want to do for a final project. And
1454
01:13:59,235 --> 01:14:02,203
certainly, as soon as I give this lecture next week
1455
01:14:02,205 --> 01:14:05,273
on final project requirements, right away you're gonna wanna
1456
01:14:05,275 --> 01:14:07,942
nail down what you want to do because you only get three
1457
01:14:07,944 --> 01:14:10,845
weeks to do it so that only gives you a week or so to
1458
01:14:10,847 --> 01:14:14,882
think of what you want to do. Okay? So be ready to kinda go
1459
01:14:14,884 --> 01:14:20,054
outta the gate with that next week. Okay, that's it!
1460
01:14:20,056 --> 01:14:22,857
I will see you next week. >> For
1461
01:14:22,859 --> 01:14:22,890
more, please visit us at Stanford.edu.
================================================
FILE: subtitles/11. Core Data Demo.srt
================================================
1
00:00:00,001 --> 00:00:03,535
[MUSIC]
2
00:00:03,537 --> 00:00:07,373
Stanford University. >> All right.
3
00:00:07,375 --> 00:00:13,412
Welcome to Lecture Number 11 of CS193P, the Spring of 2016.
4
00:00:13,414 --> 00:00:17,783
Today it's all demo, okay. All demo all the time. We're gonna
5
00:00:17,785 --> 00:00:22,788
do a core data demo. And just to let you know what's coming
6
00:00:22,790 --> 00:00:26,558
up since I, cuz I won't come back to the slides obviously.
7
00:00:26,560 --> 00:00:29,728
On Wednesday, we'll be talking about Auto Layout, and also,
8
00:00:29,730 --> 00:00:31,930
we'll be talking about the final project requirements on
9
00:00:31,932 --> 00:00:35,267
Wednesday, okay? No Friday section this week.
10
00:00:35,269 --> 00:00:38,637
And then, next week, we'll start talking about animation.
11
00:00:38,639 --> 00:00:42,975
You have an assignment that's out now which is Number Five.
12
00:00:42,977 --> 00:00:44,343
Which is a core data assignment. And
13
00:00:44,345 --> 00:00:46,512
then there will be Assignment Six which will be an animation
14
00:00:46,514 --> 00:00:50,482
assignment which will probably go out next Monday and
15
00:00:50,484 --> 00:00:54,086
due the Monday after maybe next Wednesday,
16
00:00:54,088 --> 00:00:55,054
due the Wednesday after. I'm not sure, but
17
00:00:55,056 --> 00:00:57,222
probably next Monday and then that'll be your last
18
00:00:57,224 --> 00:01:02,428
assignment before the final project. Okay. All right,
19
00:01:02,430 --> 00:01:06,532
so let's talk about this core data demo that we're gonna do.
20
00:01:06,534 --> 00:01:08,534
We're gonna continue with Smashtag,
21
00:01:08,536 --> 00:01:10,803
leave off where we were before. Okay so here's
22
00:01:10,805 --> 00:01:16,141
the user interface that we had at the end of Lecture Nine.
23
00:01:16,143 --> 00:01:19,611
And you're probably very familiar with it because your
24
00:01:19,613 --> 00:01:21,213
homework builds on this. And
25
00:01:21,215 --> 00:01:24,983
we're gonna build on this as well with this core data demo.
26
00:01:24,985 --> 00:01:28,087
What I'm gonna do is add another, table view
27
00:01:28,089 --> 00:01:30,522
controller, here. In fact let's just drag it out.
28
00:01:30,524 --> 00:01:35,861
So here it is. And I'm going to segue to this table
29
00:01:35,863 --> 00:01:40,499
view controller from a bar button item in this existing
30
00:01:40,501 --> 00:01:42,334
table view controller. Which is a little different,
31
00:01:42,336 --> 00:01:44,670
like in your homework you're segueing from the row.
32
00:01:44,672 --> 00:01:49,308
Here I'm actually gonna segue from a little bar button item.
33
00:01:49,310 --> 00:01:50,275
Now most of you have figured
34
00:01:50,277 --> 00:01:52,878
out that if you wanna add a button to the navigation bar
35
00:01:52,880 --> 00:01:56,615
up here you can't use this regular button that's up here.
36
00:01:56,617 --> 00:01:59,284
You have to use these bar buttons that are down towards
37
00:01:59,286 --> 00:02:02,488
the bottom. Here's one right here, a bar button item so
38
00:02:02,490 --> 00:02:05,657
you're just gonna drag it out and see if you drag it up at,
39
00:02:05,659 --> 00:02:10,229
up at top of a navigation bar, then it'll jump in there. And,
40
00:02:10,231 --> 00:02:13,899
what we're gonna have this UI do is, I'm gonna call
41
00:02:13,901 --> 00:02:17,703
this button Tweeters, and we're gonna have this MVC,
42
00:02:17,705 --> 00:02:19,638
this new one we're gonna write today, it's
43
00:02:19,640 --> 00:02:25,511
gonna show a list of all the tweeters who tweeted a tweet
44
00:02:25,513 --> 00:02:29,148
that we've ever downloaded with the search term that's in
45
00:02:29,150 --> 00:02:34,319
this MBC. Right? Our main MBC. So we type #Stanford here and
46
00:02:34,321 --> 00:02:36,588
search for some tweets and then we hit Tweeters.
47
00:02:36,590 --> 00:02:40,692
Over here, we would see a list of all the Twitter users who
48
00:02:40,694 --> 00:02:43,529
tweeted those tweets with #Stanford in there.
49
00:02:43,531 --> 00:02:47,432
Okay, not just in the most, latest search of #Stanford,
50
00:02:47,434 --> 00:02:49,801
but ever. And we're gonna do this by having us
51
00:02:49,803 --> 00:02:52,371
coordinate a database where every time we get a tweet,
52
00:02:52,373 --> 00:02:54,339
we're gonna put it in the database, okay?
53
00:02:54,341 --> 00:02:57,142
And so, we then, we can query those tweets and
54
00:02:57,144 --> 00:02:58,010
get all the information we want,
55
00:02:58,012 --> 00:03:02,814
including getting a list of the users who have tweeted. So
56
00:03:02,816 --> 00:03:06,618
of course we will need a custom subclass of UITableView
57
00:03:06,620 --> 00:03:10,355
for this guy, so let's go ahead and create that. File,
58
00:03:10,357 --> 00:03:11,623
New File as usual, iOS source,
59
00:03:11,625 --> 00:03:15,394
it's a Cocoa Touch class. It's a UITableView controller also.
60
00:03:15,396 --> 00:03:19,064
We'll call this our Tweeter's TableView controller,
61
00:03:19,066 --> 00:03:19,665
cuz that's what's gonna be in here,
62
00:03:19,667 --> 00:03:23,001
tweeters. We'll put it in the same group we put all the rest
63
00:03:23,003 --> 00:03:25,871
of our stuff there. Okay, here's our tweeters.
64
00:03:25,873 --> 00:03:28,340
We're not actually going to keep any of this except,
65
00:03:28,342 --> 00:03:31,510
actually I'll keep myself a row and index path here. But
66
00:03:31,512 --> 00:03:36,381
we'll get rid of the rest of this boiler plate that we get.
67
00:03:36,383 --> 00:03:41,520
Okay, there's our tweeters table view
68
00:03:41,522 --> 00:03:44,356
controller. And we always want to make sure we go back into
69
00:03:44,358 --> 00:03:48,026
our storyboard and set with the identity inspector this to
70
00:03:48,028 --> 00:03:50,028
be our tweeter's table view controller.
71
00:03:50,030 --> 00:03:50,829
This guy right here. And
72
00:03:50,831 --> 00:03:53,465
let's go ahead and wire up this segue right here too.
73
00:03:53,467 --> 00:03:58,203
I'm just gonna select this guy and control drag over to here.
74
00:03:58,205 --> 00:04:00,005
We're in a navigation controller so
75
00:04:00,007 --> 00:04:01,974
we won't show if we were in a split view,
76
00:04:01,976 --> 00:04:03,542
we'd be show detail maybe but
77
00:04:03,544 --> 00:04:05,844
here we're in navigation se we're gonna show.
78
00:04:05,846 --> 00:04:10,315
Here's our segue, let's give it an identifier, we'll call
79
00:04:10,317 --> 00:04:16,388
this the tweeter's, we'll make this very verbose,
80
00:04:16,390 --> 00:04:21,159
Tweeters Mentioning Search Term segue. Okay, so
81
00:04:21,161 --> 00:04:23,629
that's what this segue does. When you click tweeters,
82
00:04:23,631 --> 00:04:27,399
it shows you all the tweeters that mention this particular
83
00:04:27,401 --> 00:04:30,202
search term whatever that'll be that be, #Stanford or
84
00:04:30,204 --> 00:04:35,274
whatever. Okay. So that's pretty much setup our UI right
85
00:04:35,276 --> 00:04:41,713
there. One thing that we need here is core data, we wanna be
86
00:04:41,715 --> 00:04:43,282
able to access a core data database, so
87
00:04:43,284 --> 00:04:45,083
we need that manage object context,
88
00:04:45,085 --> 00:04:47,586
remember that's the hook that let's us in there.
89
00:04:47,588 --> 00:04:49,588
And I've told you there is two ways to do that,
90
00:04:49,590 --> 00:04:52,557
one is UI manage document, I'm not gonna demo that,
91
00:04:52,559 --> 00:04:55,327
that's extra credit by the way in Assignment Number Five,
92
00:04:55,329 --> 00:04:56,461
if you wanna try and tackle that.
93
00:04:56,463 --> 00:04:59,231
Only thing difficult about that is the asynchronous stuff
94
00:04:59,233 --> 00:05:01,600
but if you're got it comfortable now with
95
00:05:01,602 --> 00:05:04,670
asynchrony, it shouldn't be very difficult at all.
96
00:05:04,672 --> 00:05:07,973
The other way was to use code that would get generated for
97
00:05:07,975 --> 00:05:09,107
you when you create a new project and
98
00:05:09,109 --> 00:05:12,244
turn on the core data button and that's what I'm gonna use.
99
00:05:12,246 --> 00:05:16,748
We started this project and if we look in our app delegate
100
00:05:16,750 --> 00:05:20,052
Okay? This is Smashtags, AppDelegate here.
101
00:05:20,054 --> 00:05:22,988
You can see that it's got some empty methods in here,
102
00:05:22,990 --> 00:05:25,223
basically with some comments about them.
103
00:05:25,225 --> 00:05:26,391
We'll be covering this stuff, by the way.
104
00:05:26,393 --> 00:05:28,927
This is part of what we call the Application Life Cycle,
105
00:05:28,929 --> 00:05:31,530
these methods. But there's nothing in here about core
106
00:05:31,532 --> 00:05:33,498
data because, when we created Smashtag, we didn't
107
00:05:33,500 --> 00:05:35,534
think we were building a core data app at that time,
108
00:05:35,536 --> 00:05:38,637
so we didn't switch that button. So no problem though,
109
00:05:38,639 --> 00:05:41,239
I'm just gonna create a new project. Go to File, New,
110
00:05:41,241 --> 00:05:46,078
Project. Okay, and again, iOS application, single view like
111
00:05:46,080 --> 00:05:48,647
we usually do, and you can call it anything you want.
112
00:05:48,649 --> 00:05:52,384
I'm gonna call mine Foo, okay. And just make sure that this
113
00:05:52,386 --> 00:05:55,220
button right here is switched, Use Core Data, okay, and
114
00:05:55,222 --> 00:05:58,156
probably also wanna make sure it's Swift unless you're
115
00:05:58,158 --> 00:05:59,458
building something Objective C.
116
00:05:59,460 --> 00:06:02,961
And then when you click Next put it anywhere you want.
117
00:06:02,963 --> 00:06:06,365
I'll put mine in my same place as always developer here.
118
00:06:06,367 --> 00:06:09,034
And it makes this Foo, now this Foo is just a blank
119
00:06:09,036 --> 00:06:12,537
app right? It's got a blank view controller here.
120
00:06:12,539 --> 00:06:13,372
But we don't care about any of that.
121
00:06:13,374 --> 00:06:16,241
All we care about is in its app delegate, we look
122
00:06:16,243 --> 00:06:20,612
down it's got all those same methods but starting here,
123
00:06:20,614 --> 00:06:23,148
Core Data stack it's got a whole bunch of other code,
124
00:06:23,150 --> 00:06:26,485
okay. And we just basically want to pick this code up.
125
00:06:26,487 --> 00:06:29,287
So I'm just going to select it all, okay.
126
00:06:29,289 --> 00:06:32,190
Starting from this Mark Core Data stack going down to
127
00:06:32,192 --> 00:06:34,793
the end. And I'm just gonna cut it out of here.
128
00:06:34,795 --> 00:06:38,063
Copy or cut it. And I'm gonna go to my AppDelegate back in
129
00:06:38,065 --> 00:06:42,501
Smashtag here. And I'm just going to paste it in.
130
00:06:42,503 --> 00:06:45,070
Okay. Now it's not quite that easy there's two more things
131
00:06:45,072 --> 00:06:47,472
we gotta think about. You can see there are errors here.
132
00:06:47,474 --> 00:06:49,674
Okay there's an error there, error here.
133
00:06:49,676 --> 00:06:53,178
That's because Core Data is in a separate framework just like
134
00:06:53,180 --> 00:06:56,381
the Twitter stuff you had is in a separate framework, so
135
00:06:56,383 --> 00:06:56,915
is Core Data. So you have to,
136
00:06:56,917 --> 00:06:59,951
at the top, anywhere you're going to be using Core Data,
137
00:06:59,953 --> 00:07:03,922
say import CoreData. Okay? So that'll get rid
138
00:07:03,924 --> 00:07:04,890
of all of our errors okay,
139
00:07:04,892 --> 00:07:08,160
because of the types like in this manage object model, and
140
00:07:08,162 --> 00:07:11,229
in this persistent core coordinator, things like that.
141
00:07:11,231 --> 00:07:13,265
But there's one more thing we have to be careful of here.
142
00:07:13,267 --> 00:07:15,534
And, you're really going to want to be careful of this
143
00:07:15,536 --> 00:07:17,702
in your homework assignment, which is that,
144
00:07:17,704 --> 00:07:21,339
this code that gets generated, it actually uses the name of
145
00:07:21,341 --> 00:07:24,709
the app as the name of the data model. You know, you know
146
00:07:24,711 --> 00:07:27,946
that visual map we're gonna make with all the entities and
147
00:07:27,948 --> 00:07:30,081
attributes? Things I showed you in the slides.
148
00:07:30,083 --> 00:07:32,684
It calls that thing the name of the app so, in this case,
149
00:07:32,686 --> 00:07:34,653
Foo, because that's what I called that other app.
150
00:07:34,655 --> 00:07:37,122
So you're gonna wanna change this to whatever you're
151
00:07:37,124 --> 00:07:38,223
gonna call your mapping model and
152
00:07:38,225 --> 00:07:40,759
you get to choose, so you can pick anything you want.
153
00:07:40,761 --> 00:07:44,396
The default happens to be Model, okay, but you could
154
00:07:44,398 --> 00:07:47,199
also put the name of your app or whatever you want in here.
155
00:07:47,201 --> 00:07:50,435
Just make sure that whatever you put in here Is the same
156
00:07:50,437 --> 00:07:53,271
thing as the name you pick when you create the mapping
157
00:07:53,273 --> 00:07:55,907
okay, which we're gonna do next. Okay, so that's it.
158
00:07:55,909 --> 00:07:59,711
So once you do that you're gonna get a few vars in here
159
00:07:59,713 --> 00:08:03,315
but the main one that's Import here is this one.
160
00:08:03,317 --> 00:08:05,450
Manage object context, so that's the thing,
161
00:08:05,452 --> 00:08:08,587
that's the hook that you're gonna get into your database,
162
00:08:08,589 --> 00:08:13,225
right. Okay, so let's go ahead and create the schema for
163
00:08:13,227 --> 00:08:18,330
that database. And we do that with File > New > File. But
164
00:08:18,332 --> 00:08:20,398
here instead of doing iOS source,
165
00:08:20,400 --> 00:08:23,768
we're gonna go down to iOS Core Data. Okay? And
166
00:08:23,770 --> 00:08:27,205
in here there's three things. And this is the one you want,
167
00:08:27,207 --> 00:08:28,607
Data Model, cuz that's what we're gonna be doing,
168
00:08:28,609 --> 00:08:31,810
that's our description of raw enti, entities and attributes,
169
00:08:31,812 --> 00:08:33,578
our Data Model. Don't accidentally pick
170
00:08:33,580 --> 00:08:37,215
Mapping Model. It sounds similar, but it's different,
171
00:08:37,217 --> 00:08:38,617
okay? So Data Model's what you want.
172
00:08:38,619 --> 00:08:41,453
So we'll double-click on that. Okay, you're gonna get this,
173
00:08:41,455 --> 00:08:44,122
so you can see the default is Model. Just make sure this is
174
00:08:44,124 --> 00:08:46,491
the same as whatever you put in your app delegate,
175
00:08:46,493 --> 00:08:49,160
that code that you copied in and, make sure you put it
176
00:08:49,162 --> 00:08:52,097
where you want. Like, I really don't want this in supporting
177
00:08:52,099 --> 00:08:54,533
files or I don't want it down here in supporting files,
178
00:08:54,535 --> 00:08:57,202
I want it up at a higher level here at the smash tag level,
179
00:08:57,204 --> 00:09:00,305
okay? So let me put that there, hit Create and
180
00:09:00,307 --> 00:09:03,775
it creates this empty map, okay. It has no entities yet,
181
00:09:03,777 --> 00:09:08,780
no attributes. Now our app is going to have some Twitter
182
00:09:08,782 --> 00:09:10,782
users. It's gonna show Twitter users for,
183
00:09:10,784 --> 00:09:13,785
that have tweeted a certain mention in a bunch of tweets.
184
00:09:13,787 --> 00:09:17,589
So clearly we're gonna need an entity which is a Twitter user
185
00:09:17,591 --> 00:09:19,758
and we're probably gonna need an entity which is a tweet.
186
00:09:19,760 --> 00:09:22,861
So let's create our entities and see where it takes us.
187
00:09:22,863 --> 00:09:23,995
So I clicked this button down here,
188
00:09:23,997 --> 00:09:28,199
Add Entity, and I added this entity called, Entity. And
189
00:09:28,201 --> 00:09:29,434
I'm gonna double-click on it to rename it.
190
00:09:29,436 --> 00:09:33,238
I'm gonna call it TwitterUser. Okay, so this is gonna be
191
00:09:33,240 --> 00:09:36,708
the entity that represents a Twitter user in my database.
192
00:09:36,710 --> 00:09:39,110
Okay, gonna add another entity, clicking down here,
193
00:09:39,112 --> 00:09:42,647
okay? This one I'm gonna call Tweet, so it's gonna represent
194
00:09:42,649 --> 00:09:46,184
a tweet. Now, once we have these entities,
195
00:09:46,186 --> 00:09:49,721
we obviously need attributes on them. So Tweet, we're going
196
00:09:49,723 --> 00:09:53,491
to add text, which is, every tweet has a text, 140
197
00:09:53,493 --> 00:09:57,095
characters worth. You can see I have an error right up here.
198
00:09:57,097 --> 00:09:59,598
It says, Tweet text must have a defined type.
199
00:09:59,600 --> 00:10:02,667
And it's right now undefined. So I'm gonna pick it to be
200
00:10:02,669 --> 00:10:05,103
a string and I could add other things here.
201
00:10:05,105 --> 00:10:08,974
Maybe one thing that'd be really important with tweet,
202
00:10:08,976 --> 00:10:11,109
with a tweet, when I'm fetching these tweets,
203
00:10:11,111 --> 00:10:14,045
if I typed #Stanford and fetched 100 tweets, and
204
00:10:14,047 --> 00:10:16,548
then I typed #Stanford and fetched another 100 tweets,
205
00:10:16,550 --> 00:10:19,317
it would probably be mostly the same tweets, maybe one or
206
00:10:19,319 --> 00:10:22,320
two new ones. So I need some kind of unique identifier in
207
00:10:22,322 --> 00:10:24,255
the database to identify a tweet so
208
00:10:24,257 --> 00:10:26,257
I don't get all kinds of duplicates. I don't want,
209
00:10:26,259 --> 00:10:28,426
you know, the same Tweet in there tons of times.
210
00:10:28,428 --> 00:10:32,163
So I'm gonna call it Unique. Luckily, we're gonna find that
211
00:10:32,165 --> 00:10:35,433
the Twitter, framework provides a nice unique ID that
212
00:10:35,435 --> 00:10:38,336
we can use to be, to uni, to unique our tweets so
213
00:10:38,338 --> 00:10:40,872
that's good. And I can put other things in here, like,
214
00:10:40,874 --> 00:10:44,309
maybe, you know, the created date of the tweet,
215
00:10:44,311 --> 00:10:46,845
maybe I would call that posted and you see I'm making
216
00:10:46,847 --> 00:10:50,515
the type here be date. These names don't have to match
217
00:10:50,517 --> 00:10:51,783
the names in the Twitter framework at all.
218
00:10:51,785 --> 00:10:54,319
In fact, the Twitter framework is completely separate.
219
00:10:54,321 --> 00:10:55,987
It has nothing to do with core data.
220
00:10:55,989 --> 00:10:57,756
It just gets the data from Twitter. Okay,
221
00:10:57,758 --> 00:11:00,392
it's our responsibility to take that data from Twitter
222
00:11:00,394 --> 00:11:02,927
and put it into this database with whatever attributes and
223
00:11:02,929 --> 00:11:06,431
entities we want. Okay, now we happen to call this tweet and
224
00:11:06,433 --> 00:11:09,634
we also have a class in our Twitter framework called tweet
225
00:11:09,636 --> 00:11:12,704
also and we're gonna have to be careful about that. But
226
00:11:12,706 --> 00:11:13,772
they're totally different. Okay,
227
00:11:13,774 --> 00:11:15,607
they represent the same thing conceptually, a tweet,
228
00:11:15,609 --> 00:11:19,377
but they're different classes. All right, our Twitter user,
229
00:11:19,379 --> 00:11:21,846
what does a Twitter user have? Well, a screen name,
230
00:11:21,848 --> 00:11:23,948
obviously, that's the most important thing,
231
00:11:23,950 --> 00:11:26,484
that's your Twitter handle, you know, @whatever,
232
00:11:26,486 --> 00:11:30,055
that's a string. You know, the, we don't really need much
233
00:11:30,057 --> 00:11:33,224
else for our app here, but maybe there's the actual name,
234
00:11:33,226 --> 00:11:36,027
we could have other attributes of a user, put whatever we
235
00:11:36,029 --> 00:11:40,365
want in here. Okay? Now, as time goes by, as you enhance
236
00:11:40,367 --> 00:11:42,600
your app, you're gonna be adding more attribute,
237
00:11:42,602 --> 00:11:44,769
more entities, and that's perfectly fine.
238
00:11:44,771 --> 00:11:47,372
The only thing to notice is every time you change your
239
00:11:47,374 --> 00:11:50,842
model here, okay? The database that's on your simulator or
240
00:11:50,844 --> 00:11:55,146
on your device is now invalid, okay? Now, there is a way to
241
00:11:55,148 --> 00:11:57,982
make compatible changes, I'm not really gonna talk about
242
00:11:57,984 --> 00:12:01,286
that. But there is a way to do that. Once you, if you started
243
00:12:01,288 --> 00:12:03,021
shipping an app on the app store, then that would start
244
00:12:03,023 --> 00:12:05,623
to matter to you because you wanna ship a new version.
245
00:12:05,625 --> 00:12:06,858
Maybe it has some new attributes, you
246
00:12:06,860 --> 00:12:10,595
want all the data the user's created to still be good. But
247
00:12:10,597 --> 00:12:12,430
when you're in development and you're changing things,
248
00:12:12,432 --> 00:12:15,500
what you can do is just go on to your device, or
249
00:12:15,502 --> 00:12:16,234
your, simulator, and
250
00:12:16,236 --> 00:12:20,004
delete your app. Okay, every time you change your schema,
251
00:12:20,006 --> 00:12:22,073
you're gonna have to delete your app and then run again.
252
00:12:22,075 --> 00:12:24,576
Of course, you'll lose all your data in the database too,
253
00:12:24,578 --> 00:12:27,912
but, that's just, you're doing development cycles anyway, so
254
00:12:27,914 --> 00:12:31,182
it shouldn't, probably shouldn't matter. Okay.
255
00:12:31,184 --> 00:12:33,184
All right, so we have this great thing.
256
00:12:33,186 --> 00:12:35,253
Let's take a look at it in the graphical view,
257
00:12:35,255 --> 00:12:36,888
clicking this little button down here, and
258
00:12:36,890 --> 00:12:39,457
you can see here's our two things. Notice if I move one,
259
00:12:39,459 --> 00:12:42,961
the other one kind of moves around to keep it all,
260
00:12:42,963 --> 00:12:43,795
in a nice space here.
261
00:12:43,797 --> 00:12:46,364
And you can see here's my attributes, the name,
262
00:12:46,366 --> 00:12:49,434
the screen name, you can see that the inspector over here
263
00:12:49,436 --> 00:12:52,570
is showing us things like the type of it. All right?
264
00:12:52,572 --> 00:12:55,774
Click on this one, type. You can even set some things like
265
00:12:55,776 --> 00:12:58,243
minimum and maximums for dates and things like that.
266
00:12:58,245 --> 00:13:00,712
I'm not really gonna talk about this inspector. You can
267
00:13:00,714 --> 00:13:03,047
play around with it, certainly read the documentation and
268
00:13:03,049 --> 00:13:06,384
find out all the things you can do over here. But
269
00:13:06,386 --> 00:13:09,420
what I'm interested now is the relationship between Twitter
270
00:13:09,422 --> 00:13:10,789
users and Tweets, okay. And
271
00:13:10,791 --> 00:13:14,459
we know that a Twitter user Tweets and creates a bunch of
272
00:13:14,461 --> 00:13:16,161
these things, okay, creates a bunch of Tweets.
273
00:13:16,163 --> 00:13:18,897
So there's a relationship between these two, and anytime
274
00:13:18,899 --> 00:13:21,599
there's a relationship between entities in your data model,
275
00:13:21,601 --> 00:13:25,270
you just go to this graphical view, hold down Ctrl, and
276
00:13:25,272 --> 00:13:28,740
drag between them, okay? When you let go, you're gonna get
277
00:13:28,742 --> 00:13:31,609
these new things. These are also gonna be vars ju-, in
278
00:13:31,611 --> 00:13:35,213
your code, just like these are vars, which is a relationship
279
00:13:35,215 --> 00:13:38,950
between the two. And each side has a relationship, okay? And
280
00:13:38,952 --> 00:13:40,552
you'll, first thing you'll want to do
281
00:13:40,554 --> 00:13:42,887
is rename these to be more meaningful names than
282
00:13:42,889 --> 00:13:44,889
newRelationship. So a Twitter user,
283
00:13:44,891 --> 00:13:48,827
Twitter user's relationship to their tweets is probably gonna
284
00:13:48,829 --> 00:13:51,529
be called tweets. It's the tweets that they've tweeted,
285
00:13:51,531 --> 00:13:54,599
right? And similarly a tweet, the relationship it has
286
00:13:54,601 --> 00:13:57,402
to the Twitter user who tweeted it is probably,
287
00:13:57,404 --> 00:13:59,237
you could call it user, or poster, or
288
00:13:59,239 --> 00:14:02,607
I'm gonna call it tweeter, but it doesn't really matter,
289
00:14:02,609 --> 00:14:06,044
it's just the name of a var, that we're gonna have in our
290
00:14:06,046 --> 00:14:09,447
code, that represents the relationship between the two.
291
00:14:09,449 --> 00:14:12,784
Now, as we talked about in the slides about core data,
292
00:14:12,786 --> 00:14:15,987
there's something special about this relationship,
293
00:14:15,989 --> 00:14:19,624
this tweet's relationship to tweet, which is that a Twitter
294
00:14:19,626 --> 00:14:23,394
user can have multiple tweets, could have hundreds of tweets
295
00:14:23,396 --> 00:14:27,899
possibly, okay? A given tweet only has one tweeter. But
296
00:14:27,901 --> 00:14:31,236
a good, a twitter user could have hundreds of tweets. So
297
00:14:31,238 --> 00:14:32,403
this relationship's a little different. So
298
00:14:32,405 --> 00:14:36,207
when we click on it here and look at it in the inspector,
299
00:14:36,209 --> 00:14:37,876
and see the inspector is pretty smart.
300
00:14:37,878 --> 00:14:40,478
It knows there's an inverse relationship tweeter
301
00:14:40,480 --> 00:14:42,914
over here. That's the inverse of tweets. And
302
00:14:42,916 --> 00:14:45,817
it also knows the type of connection here is To one and
303
00:14:45,819 --> 00:14:49,754
we want to tell it no. This is a To many, To many connection,
304
00:14:49,756 --> 00:14:54,125
okay, meaning there are many tweets per Twitter user. And
305
00:14:54,127 --> 00:14:57,262
when we do that to To many, you can see that the arrow
306
00:14:57,264 --> 00:14:59,931
here gets double arrowed in this direction. Only this
307
00:14:59,933 --> 00:15:02,901
direction, it's still single To one in that direction, but
308
00:15:02,903 --> 00:15:05,536
it's To many in this direction. Okay, and
309
00:15:05,538 --> 00:15:07,505
that's perfectly fine for one to be a To one and
310
00:15:07,507 --> 00:15:10,074
one to be a To many, as you can see here. You can also
311
00:15:10,076 --> 00:15:13,645
have both of them be To many. That's legal as well.
312
00:15:13,647 --> 00:15:17,048
All right, and we also know from the slides that what this
313
00:15:17,050 --> 00:15:21,352
means is that tweets var, okay, on our Twitter user,
314
00:15:21,354 --> 00:15:23,187
is gonna be an ns set. And
315
00:15:23,189 --> 00:15:25,823
in that ns set are gonna be ns managed objects.
316
00:15:25,825 --> 00:15:29,594
This is an ns managed object. This is an ns managed object.
317
00:15:29,596 --> 00:15:33,698
This var, that points this way in Tweet, is gonna be an ns
318
00:15:33,700 --> 00:15:37,835
managed object, okay, because it's only a single one.
319
00:15:37,837 --> 00:15:40,705
Every single entity in the database is represented by
320
00:15:40,707 --> 00:15:43,875
an ns managed object. Now, that's a little bit kind
321
00:15:43,877 --> 00:15:46,377
of hard to use, to have every single thing,
322
00:15:46,379 --> 00:15:49,147
Twitter users and tweets be NS managed object.
323
00:15:49,149 --> 00:15:50,782
Because the only way we can set the attributes
324
00:15:50,784 --> 00:15:53,685
is using that set value for key. And get the attributes
325
00:15:53,687 --> 00:15:57,055
with value for key. It's a little unwieldy. So of course,
326
00:15:57,057 --> 00:15:59,791
we want to have subclasses of NS managed object for
327
00:15:59,793 --> 00:16:02,427
each of our entities. So a class called tweet, which
328
00:16:02,429 --> 00:16:05,063
is a subclass of NS managed object that represents these
329
00:16:05,065 --> 00:16:07,932
things, and has vars for all this. And the same thing for
330
00:16:07,934 --> 00:16:12,070
Twitter user. Okay. And again, remember from the slides we
331
00:16:12,072 --> 00:16:14,539
can go up here and have, in the editor menu,
332
00:16:14,541 --> 00:16:17,709
Xcode generate those sub classes for us. One thing I'll
333
00:16:17,711 --> 00:16:20,578
remind you if you're gonna do this, see where it says model
334
00:16:20,580 --> 00:16:23,948
dot x c model. Do you see how that's selected? This menu,
335
00:16:23,950 --> 00:16:26,184
the editor menu is contact sensitive,
336
00:16:26,186 --> 00:16:28,486
it depends on what thing you have selected.
337
00:16:28,488 --> 00:16:29,087
So for example if I go over here and
338
00:16:29,089 --> 00:16:32,390
pick Story Board, look this menu£s completely different,
339
00:16:32,392 --> 00:16:34,292
it£s doing Story Board things over here,
340
00:16:34,294 --> 00:16:36,094
okay like embedding and stuff like that. So
341
00:16:36,096 --> 00:16:38,730
you wanna make sure you have this data model selected so
342
00:16:38,732 --> 00:16:41,466
that you get the data model editor menu.
343
00:16:41,468 --> 00:16:45,737
So we're gonna pick Create NS managed sub class here.
344
00:16:45,739 --> 00:16:46,170
So NS Managed sub class,
345
00:16:46,172 --> 00:16:49,007
it's saying which of these do you wanna create it for?
346
00:16:49,009 --> 00:16:51,876
We only have one okay, so it's obviously selected.
347
00:16:51,878 --> 00:16:54,345
But you can actually have multiple of these mappings and
348
00:16:54,347 --> 00:16:57,281
have multiple databases. Here it's saying which of
349
00:16:57,283 --> 00:16:59,817
the entities do you wanna generate sub classes for and
350
00:16:59,819 --> 00:17:03,388
I'm gonna click them both. Okay? I want both of these.
351
00:17:03,390 --> 00:17:06,557
Click next. And be very careful of this one, okay?
352
00:17:06,559 --> 00:17:09,260
This is asking you where you want to put these things.
353
00:17:09,262 --> 00:17:10,661
Well, look where it wanted to put it.
354
00:17:10,663 --> 00:17:13,564
Right at the very top of my work space.
355
00:17:13,566 --> 00:17:14,632
I definitely don't want it there.
356
00:17:14,634 --> 00:17:17,335
I definitely want it somewhere in my Smash Tag project.
357
00:17:17,337 --> 00:17:20,204
Probably right at this level. Not in supporting files, but
358
00:17:20,206 --> 00:17:23,608
right here. Make sure you set this you'll be unhappy if you
359
00:17:23,610 --> 00:17:25,977
put it up in your top work space level,
360
00:17:25,979 --> 00:17:29,547
files won't be in the right place it'll be bad.
361
00:17:29,549 --> 00:17:31,682
Also make sure you're language is right of course.
362
00:17:31,684 --> 00:17:35,286
This scaler remember that's, all the things that,
363
00:17:35,288 --> 00:17:38,056
integers and doubles are represented as NS numbers,
364
00:17:38,058 --> 00:17:40,892
okay? And if you do the use scalers the Vars
365
00:17:40,894 --> 00:17:41,692
that it will create will be
366
00:17:41,694 --> 00:17:44,228
not be NS numbers will be instant doubles. And
367
00:17:44,230 --> 00:17:49,300
fortunately, in this time intervals. I don't want that.
368
00:17:49,302 --> 00:17:51,969
I have it in this date right here posted. So
369
00:17:51,971 --> 00:17:54,539
I'm not going to switch this, but if you change your mind
370
00:17:54,541 --> 00:17:57,308
on this you can always go back and regenerate these. Okay,
371
00:17:57,310 --> 00:17:59,243
it's not like you generate these once and you're done,
372
00:17:59,245 --> 00:18:01,779
you can always go back. All right. So I'm going to
373
00:18:01,781 --> 00:18:04,515
hit create to create this thing. We'll see the files
374
00:18:04,517 --> 00:18:09,120
are created, okay. You can see right here this first one is
375
00:18:09,122 --> 00:18:10,421
tweet okay? Tweet.swift.
376
00:18:10,423 --> 00:18:13,624
It's a sub class of NS managed object as we would expect. And
377
00:18:13,626 --> 00:18:15,927
this is just a place we can put any code we want,
378
00:18:15,929 --> 00:18:18,796
okay? Any code that in an object oriented design sense
379
00:18:18,798 --> 00:18:22,100
would make sense to be in the tweet, class.
380
00:18:22,102 --> 00:18:24,936
Okay this is a bizarre place to do thing. If we go back and
381
00:18:24,938 --> 00:18:30,174
regenerate these this file will not get changed. Okay? So
382
00:18:30,176 --> 00:18:33,578
it won't stomp our, whatever code we put in here. Okay,
383
00:18:33,580 --> 00:18:39,016
similar we have TwitterUser, okay, right here. Same thing,
384
00:18:39,018 --> 00:18:40,284
okay? Just a place to put a TwitterUser.
385
00:18:40,286 --> 00:18:41,953
It's a sub-class of NSManagedObject. And
386
00:18:41,955 --> 00:18:45,323
then there are these, these two really cool extensions
387
00:18:45,325 --> 00:18:47,959
that were created for us, okay, one for TwitterUser and
388
00:18:47,961 --> 00:18:51,896
one for Tweet that have vars that represent each of our
389
00:18:51,898 --> 00:18:53,564
attributes, so we don't have to use set value for
390
00:18:53,566 --> 00:18:56,167
a key, okay? You can see its screenName is a string,
391
00:18:56,169 --> 00:18:59,403
name is a string, tweet, as promised, is an NSSet.
392
00:18:59,405 --> 00:19:02,373
And in that NSSet, we NSManage objects, in fact,
393
00:19:02,375 --> 00:19:06,644
they'll be tweets, okay? A subclass of NSManagedObject.
394
00:19:06,646 --> 00:19:07,912
Notice that they are all optional,
395
00:19:07,914 --> 00:19:10,948
that's because when we create a twitter user in this
396
00:19:10,950 --> 00:19:14,285
case in the database all the. Okay,
397
00:19:14,287 --> 00:19:19,490
we can set them to whatever we want and similar for tweet.
398
00:19:19,492 --> 00:19:23,127
Okay, it's got an extension as well as you can see, and
399
00:19:23,129 --> 00:19:25,463
notice it's tweeter relationship
400
00:19:25,465 --> 00:19:30,134
is a pointer to a TwitterUser If you do this, and
401
00:19:30,136 --> 00:19:34,605
you find this to be managed object, okay? It could be
402
00:19:34,607 --> 00:19:37,341
because your Tweet and Twitter user files got generated
403
00:19:37,343 --> 00:19:39,810
in the wrong order, so that twitter user hadn't been
404
00:19:39,812 --> 00:19:43,147
generated at the time tweet was, and so it gets object.
405
00:19:43,149 --> 00:19:46,484
The fix is just go back and regenerate them again, okay?
406
00:19:46,486 --> 00:19:50,755
Cuz it takes into account what is generated in the past.
407
00:19:50,757 --> 00:19:53,591
Okay? Otherwise this is all pretty straightforward.
408
00:19:53,593 --> 00:19:54,792
Now these files you never edit.
409
00:19:54,794 --> 00:19:57,929
These files get regenerated every time you go into that
410
00:19:57,931 --> 00:20:01,632
create ns manage object thing. So as you change your schema
411
00:20:01,634 --> 00:20:06,003
these might change. All right now I like to take all of
412
00:20:06,005 --> 00:20:10,041
these core data things, okay, including my model file and
413
00:20:10,043 --> 00:20:11,275
I like to put them in their own groups.
414
00:20:11,277 --> 00:20:14,445
I'll select them all and say new group from selection and
415
00:20:14,447 --> 00:20:18,482
call it core data. So this is my core data group.
416
00:20:18,484 --> 00:20:18,549
Inside this group,
417
00:20:18,551 --> 00:20:21,452
I've got all my stuff that has to do with core data, and
418
00:20:21,454 --> 00:20:25,156
we're going to add a little bit more to that in a minute.
419
00:20:26,192 --> 00:20:29,594
Alright, so back to our app and
420
00:20:29,596 --> 00:20:31,195
what we're trying to accomplish here.
421
00:20:31,197 --> 00:20:32,630
All right, so here's what our app looks like,
422
00:20:32,632 --> 00:20:34,599
right? Just got this thing that's gonna segue,
423
00:20:34,601 --> 00:20:39,003
there's really two parts to making this new NVC work.
424
00:20:39,005 --> 00:20:41,339
One is we have to put data in the database,
425
00:20:41,341 --> 00:20:44,242
that's actually something this NVC does, cuz it's the thing
426
00:20:44,244 --> 00:20:46,510
that does the fetching. When we type #Stanford,
427
00:20:46,512 --> 00:20:48,145
it's the thing that goes out and gets them.
428
00:20:48,147 --> 00:20:51,816
So this is going to load up the database with Tweet and
429
00:20:51,818 --> 00:20:52,283
tweeter users. And
430
00:20:52,285 --> 00:20:55,286
then this one is going to look in the database, okay,
431
00:20:55,288 --> 00:20:57,488
to find things out. So let's do this part first,
432
00:20:57,490 --> 00:21:00,124
let's go to our existing tweet table view controller and
433
00:21:00,126 --> 00:21:03,828
have it update the database every time we do a fetch.
434
00:21:03,830 --> 00:21:05,696
All right, so here's our code for
435
00:21:05,698 --> 00:21:08,733
our tweet table view hopefully your you i remember this where
436
00:21:08,735 --> 00:21:11,369
you intimately famil, familiar with it as our search text.
437
00:21:11,371 --> 00:21:14,238
Here's where we're doing our search for tweets, right.
438
00:21:14,240 --> 00:21:16,040
Here's where we find new tweets and
439
00:21:16,042 --> 00:21:18,509
insert them in the table. So I'm just gonna,
440
00:21:18,511 --> 00:21:20,144
in addition here to putting them in the table,
441
00:21:20,146 --> 00:21:24,682
I'm gonna call some method, let's say updateDatabase and
442
00:21:24,684 --> 00:21:29,954
pass along these new tweets to go put them in the database,
443
00:21:29,956 --> 00:21:34,191
all right. Now, we're gonna be doing CoreData here, so
444
00:21:34,193 --> 00:21:37,828
I wanna make sure I import CoreData up at the top here,
445
00:21:37,830 --> 00:21:43,467
okay? And then I have to write this method here, private func
446
00:21:43,469 --> 00:21:48,372
updateDatabase, okay? It takes the new tweets that it got,
447
00:21:48,374 --> 00:21:50,741
which is an array of tweet, but
448
00:21:50,743 --> 00:21:52,810
now I have to be careful here, okay?
449
00:21:52,812 --> 00:21:56,614
Cuz which tweet is this? Is that the tweet in the Twitter
450
00:21:56,616 --> 00:22:00,785
framework, or is that this tweet right here that's an ns
451
00:22:00,787 --> 00:22:05,089
managed object? Hm, okay we have to be careful here. And
452
00:22:05,091 --> 00:22:07,591
the way we distinguish the two is by putting the framework
453
00:22:07,593 --> 00:22:11,929
name in front. So this update database takes Twitter tweets
454
00:22:11,931 --> 00:22:15,399
and we're gonna put them, turn them into, in [INAUDIBLE]
455
00:22:15,401 --> 00:22:17,902
tweets and put ' in the database so be careful if you
456
00:22:17,904 --> 00:22:20,905
do that. Now when you do your homework, you're welcome to
457
00:22:20,907 --> 00:22:24,575
not call this thing in your database tweet.
458
00:22:24,577 --> 00:22:27,111
If you don't wanna have to deal with twitter.tweet.
459
00:22:27,113 --> 00:22:30,881
Okay you can call it something else. But it, if you do call
460
00:22:30,883 --> 00:22:33,751
it tweet, then you wanna make sure to distinguish which ones
461
00:22:33,753 --> 00:22:37,555
you're talking about at any given time. All right? Now,
462
00:22:37,557 --> 00:22:40,091
to update our database, here's where we need that
463
00:22:40,093 --> 00:22:43,227
object context, right? That little hook that lets us put
464
00:22:43,229 --> 00:22:45,262
things in the database, search through the database, etc ect.
465
00:22:45,264 --> 00:22:50,067
I'm actually gonna make that part of the model of this NVC,
466
00:22:50,069 --> 00:22:52,169
because if you think about what this NVC does, yeah,
467
00:22:52,171 --> 00:22:55,306
it lets you search, but it also updates that database.
468
00:22:55,308 --> 00:22:58,442
Which database it updates is really part of
469
00:22:58,444 --> 00:23:00,111
describing its model. Okay, so
470
00:23:00,113 --> 00:23:04,014
I'm gonna have a var here called managedObjectContext.
471
00:23:04,016 --> 00:23:07,451
It's gonna be an NSManagedObjectContext, and
472
00:23:07,453 --> 00:23:09,587
it'll be optional. And if this is nil,
473
00:23:09,589 --> 00:23:12,556
this is not set to anything, then I just won't update any
474
00:23:12,558 --> 00:23:15,192
database. Okay, so I'm gonna write my code, so if this is
475
00:23:15,194 --> 00:23:17,862
nil I'm not gonna update it. I'll still do my searching,
476
00:23:17,864 --> 00:23:19,797
but I'm not gonna update any database. So
477
00:23:19,799 --> 00:23:24,101
if anyone wants me to update a database they better set this,
478
00:23:24,103 --> 00:23:26,237
okay, to the database they want me to update.
479
00:23:26,239 --> 00:23:28,939
Now especially for demo purposes, I'm actually gonna
480
00:23:28,941 --> 00:23:32,176
have this have a default which is that App delegate thing.
481
00:23:32,178 --> 00:23:35,146
Remember that code that we just copied over into app
482
00:23:35,148 --> 00:23:37,915
delegate over here? This manage object context?
483
00:23:37,917 --> 00:23:42,620
I wanna call this property, access this property on my
484
00:23:42,622 --> 00:23:45,489
app delegate, to get the managed object context. Again,
485
00:23:45,491 --> 00:23:48,292
if you're using a UI managed document, you'd be doing this
486
00:23:48,294 --> 00:23:50,828
differently. Okay? So I got a call then.
487
00:23:50,830 --> 00:23:53,764
If you remember how that code goes, we're gonna get
488
00:23:53,766 --> 00:23:56,167
UI applications, shared applications.
489
00:23:56,169 --> 00:23:58,836
So this is just an instance of UI application that's shared.
490
00:23:58,838 --> 00:24:01,739
So outta your whole app, it's the only one you're ever
491
00:24:01,741 --> 00:24:04,308
gonna use. Now I'm gonna get its delegate.
492
00:24:04,310 --> 00:24:08,712
Okay, now its delegate is of type UI application delegate.
493
00:24:08,714 --> 00:24:11,182
Right, it's a protocol. You know how delegates work,
494
00:24:11,184 --> 00:24:13,517
it's always a protocol. And we don't want that, okay?
495
00:24:13,519 --> 00:24:17,188
We want it to be this particular app delegate class
496
00:24:17,190 --> 00:24:21,358
right here. Okay. This app class app delegate, kind of
497
00:24:21,360 --> 00:24:24,628
a generic name app delegate. It happens to implement the UI
498
00:24:24,630 --> 00:24:27,364
application delegate protocol so that's good. Okay. So
499
00:24:27,366 --> 00:24:32,770
that means I can go over here and say as AppDelegate, okay?
500
00:24:32,772 --> 00:24:35,606
So now I have a hold of that AppDelegate
501
00:24:35,608 --> 00:24:39,043
instance, all right? And all I need to do now is send it
502
00:24:39,045 --> 00:24:41,145
ManagedObjectContext. So let's go ahead and do that.
503
00:24:41,147 --> 00:24:42,746
I had to put a parentheses around here.
504
00:24:42,748 --> 00:24:46,317
Now this might be nil, okay? What if the delegate is for
505
00:24:46,319 --> 00:24:49,253
some reason not set, or what if it isn't this class
506
00:24:49,255 --> 00:24:52,823
AppDelegate for some reason? You change it mid-flight,
507
00:24:52,825 --> 00:24:57,027
okay? Then I'm gonna optional chain here and get the managed
508
00:24:57,029 --> 00:25:01,699
object content. Okay. So this all might come out nil,
509
00:25:01,701 --> 00:25:05,436
that's okay because this is an optional. We just won't do,
510
00:25:05,438 --> 00:25:07,571
put in anything in the database in that case.
511
00:25:07,573 --> 00:25:11,175
All right, now it's a little weird to have this default.
512
00:25:11,177 --> 00:25:13,644
More likely you would just have this be nil and
513
00:25:13,646 --> 00:25:15,846
someone would have to explicitly set it, but
514
00:25:15,848 --> 00:25:19,116
that would require probably to create a little subclass of
515
00:25:19,118 --> 00:25:22,119
tweet table view called root tweet table view, and
516
00:25:22,121 --> 00:25:23,621
it would do this line of code, and
517
00:25:23,623 --> 00:25:26,757
then call super.manage.object context equals this.
518
00:25:26,759 --> 00:25:28,826
Okay, and then we would go in our storyboard and
519
00:25:28,828 --> 00:25:31,562
we would set the identity inspector for this table view
520
00:25:31,564 --> 00:25:33,631
to be root table, Tweet table view controller.
521
00:25:33,633 --> 00:25:35,866
So that's a little more than I wanna go through in the demo
522
00:25:35,868 --> 00:25:38,669
here, so we're just going to kinda go cheap mode here and
523
00:25:38,671 --> 00:25:43,007
just make it default to be this managedObjectContext. All
524
00:25:43,009 --> 00:25:45,743
right, so this is where we're gonna store our database when
525
00:25:45,745 --> 00:25:48,846
we, or our information when we do this update database.
526
00:25:48,848 --> 00:25:51,849
Okay. Now remember, any time we access the database,
527
00:25:51,851 --> 00:25:54,685
we have to do it with that perform block because these
528
00:25:54,687 --> 00:25:57,254
database, these managedObjectContext, are only
529
00:25:57,256 --> 00:26:00,024
thread safe within the thread they were created on.
530
00:26:00,026 --> 00:26:02,359
And I'm gonna write all my code in this demo as
531
00:26:02,361 --> 00:26:05,462
if I don't know what thread these managedObjectContexts
532
00:26:05,464 --> 00:26:09,233
are created on. Even though we know that that AppDelegate one
533
00:26:09,235 --> 00:26:12,136
is always a main queue one. But I'm not gonna write my
534
00:26:12,138 --> 00:26:15,105
code assuming that, just so you can see what it looks like
535
00:26:15,107 --> 00:26:18,809
to write multi-threaded managedObject, code, okay? So,
536
00:26:18,811 --> 00:26:22,846
that means we always have to have our managedObjectContext
537
00:26:22,848 --> 00:26:27,017
and do performBlock, okay? Now if this is nil,
538
00:26:27,019 --> 00:26:31,021
what happens here? Nothing, good, that's what I want.
539
00:26:31,023 --> 00:26:33,924
Okay, I, I've, if I don't have a managedObjectContext,
540
00:26:33,926 --> 00:26:34,992
I don't wanna update the database.
541
00:26:34,994 --> 00:26:36,894
So this is perfect so far, all right?
542
00:26:36,896 --> 00:26:39,697
So we're gonna perform a block here. Inside this block,
543
00:26:39,699 --> 00:26:42,499
all we gotta do is put these newTweets, okay,
544
00:26:42,501 --> 00:26:45,869
into the database. So, that's pretty straightforward.
545
00:26:45,871 --> 00:26:46,570
I'm just gonna have a little for
546
00:26:46,572 --> 00:26:49,807
loop that goes through those tweets. Let's say for
547
00:26:49,809 --> 00:26:55,446
twitterInfo in the newTweets. And then here,
548
00:26:55,448 --> 00:27:00,818
I have to, you know, create a new but unique Tweet,
549
00:27:00,820 --> 00:27:06,023
in the N-S managed objects sense. With that, you know,
550
00:27:06,025 --> 00:27:10,060
Twitter info. Okay, so that's what I need to do in here. So
551
00:27:10,062 --> 00:27:13,297
I need a method to do that. Now that method doesn't really
552
00:27:13,299 --> 00:27:16,367
want to go in this Tweet table view controller,
553
00:27:16,369 --> 00:27:19,903
okay. That's really something that wants to go in the Tweet
554
00:27:19,905 --> 00:27:25,242
class. Okay. A tweet class is one stop shop for
555
00:27:25,244 --> 00:27:27,611
everything tweet when it comes to the database. So
556
00:27:27,613 --> 00:27:30,614
if you had a method that takes Twitter info from the network
557
00:27:30,616 --> 00:27:32,082
and turns it into a tweet in the database,
558
00:27:32,084 --> 00:27:35,753
that needs to believe, live in the one stop shop. Okay.
559
00:27:35,755 --> 00:27:40,090
So we're gonna actually put that code some method that
560
00:27:40,092 --> 00:27:41,258
does this in tweet.
561
00:27:41,260 --> 00:27:43,927
Okay, remember Tweet is this subclass of NSObject, so
562
00:27:43,929 --> 00:27:46,664
we're gonna do exactly what that comment just said and
563
00:27:46,666 --> 00:27:50,234
insert the code we want in here. Now this code,
564
00:27:50,236 --> 00:27:53,470
what is this method gonna do? Well, this method is going to
565
00:27:53,472 --> 00:27:56,774
look in the database and if there's already a tweet there
566
00:27:56,776 --> 00:28:00,978
with that unique ID, then it's just gonna just return that.
567
00:28:00,980 --> 00:28:03,514
If not, it's gonna create one and load it up with
568
00:28:03,516 --> 00:28:06,316
the Twitter information, okay that it got from from,
569
00:28:06,318 --> 00:28:10,120
from over the network and then, it's gonna return that.
570
00:28:10,122 --> 00:28:12,356
So, it either looks it up and returns it or
571
00:28:12,358 --> 00:28:15,859
create it if necessary. Okay. I'm gonna call this thing,
572
00:28:15,861 --> 00:28:18,095
first of all, it's gonna be a class function,
573
00:28:18,097 --> 00:28:20,698
meaning I'm gonna send this to the tweet class
574
00:28:20,700 --> 00:28:22,199
because I'm creating an instance here,
575
00:28:22,201 --> 00:28:23,701
I can't really send it to an instance yet,
576
00:28:23,703 --> 00:28:26,470
I haven't created one. I'm gonna call this thing
577
00:28:26,472 --> 00:28:31,942
tweetWithTwitterInfo and it's gonna take some Twitter info
578
00:28:31,944 --> 00:28:37,047
which is gonna be a Twitter.Tweet, okay? So I have
579
00:28:37,049 --> 00:28:41,051
to import Twitter here. So then making a Twitter.Tweet.
580
00:28:41,053 --> 00:28:44,455
And the other thing I gotta have if I'm gonna create
581
00:28:44,457 --> 00:28:48,125
something in the database is inManagedObjectContext.
582
00:28:48,127 --> 00:28:51,762
Okay, I have to have the NS ManagedObjectContext that you
583
00:28:51,764 --> 00:28:53,997
want to create this thing in, or, or
584
00:28:53,999 --> 00:28:56,200
it just can't create it, okay.
585
00:28:56,202 --> 00:29:00,104
And this thing is gonna return a tweet. Okay, it's either
586
00:29:00,106 --> 00:29:02,973
gonna return the tweet that it found in the database by
587
00:29:02,975 --> 00:29:05,576
looking at the unique ID of this Twitter.Tweet, or
588
00:29:05,578 --> 00:29:08,378
it's gonna create one in the database and return that.
589
00:29:08,380 --> 00:29:13,150
Okay, so let's have it return nil. Actually, let's just make
590
00:29:13,152 --> 00:29:15,419
it so it returns an optional tweet in case for
591
00:29:15,421 --> 00:29:16,987
some reason we can't create one.
592
00:29:16,989 --> 00:29:21,058
We'll have it return nil by default. Okay, we'll get back
593
00:29:21,060 --> 00:29:23,761
to how we're going to implement this in a second. I,
594
00:29:23,763 --> 00:29:26,430
I want to go back to the code that calls this,
595
00:29:26,432 --> 00:29:29,633
which is here. Okay, this update database. And use it
596
00:29:29,635 --> 00:29:32,903
here. And so you just call, you know how you call class
597
00:29:32,905 --> 00:29:36,440
functions, you just have the name of the class, tweet, and
598
00:29:36,442 --> 00:29:42,446
then this is tweet, what's it called? TweetWithTwitterInfo,
599
00:29:42,448 --> 00:29:45,649
and we're gonna pass the Twitter info along.
600
00:29:45,651 --> 00:29:49,119
Okay, this is the Twitter info that we're pulling out of this
601
00:29:49,121 --> 00:29:53,257
array of Twitter.Tweets, right? In a for loop here.
602
00:29:53,259 --> 00:29:58,028
And ininManagedObjectContext, we're gonna pass our own
603
00:29:58,030 --> 00:30:02,332
managed object context. And we can go ahead and unwrap it,
604
00:30:02,334 --> 00:30:04,868
because we know we wouldn't, have made it into this block
605
00:30:04,870 --> 00:30:08,872
if this were not nil. All right? And notice that it's
606
00:30:08,874 --> 00:30:12,409
complaining here about you gotta put self in, sorry,
607
00:30:12,411 --> 00:30:16,180
you've gotta put self in front of this because this right
608
00:30:16,182 --> 00:30:20,651
here is a block, okay? So we gotta make sure we avoid any
609
00:30:20,653 --> 00:30:25,289
loops, you know, any any memory cycles and
610
00:30:25,291 --> 00:30:29,326
what else is it complaining about here? Cannot,
611
00:30:29,328 --> 00:30:36,033
I must have typed that wrong. Let's go back here. All right,
612
00:30:36,035 --> 00:30:41,505
go back here to tweet. I must have, yes,
613
00:30:41,507 --> 00:30:46,109
I forgot to put context okay in this iManagedObjectContext
614
00:30:46,111 --> 00:30:49,847
there. All right, back. All right, so we got this,
615
00:30:49,849 --> 00:30:52,649
so this, now one thing that's interesting about this
616
00:30:52,651 --> 00:30:55,552
tweetWithTwitterInfo, it returns a value, right,
617
00:30:55,554 --> 00:30:57,888
it returns a tweet. Now I don't care.
618
00:30:57,890 --> 00:31:00,724
I don't want the tweet here, I'm just creating it, I'm just
619
00:31:00,726 --> 00:31:03,460
the guy loading the database, so I don't want that tweet. So
620
00:31:03,462 --> 00:31:06,029
notice that swift doesn't complain, even though I'm
621
00:31:06,031 --> 00:31:10,634
ignoring it. But some would say it's pretty good code to
622
00:31:10,636 --> 00:31:15,105
say_ = here, okay. That tells someone reading this code,
623
00:31:15,107 --> 00:31:18,275
yeah, I understand that this returns to a value but
624
00:31:18,277 --> 00:31:19,276
I'm not interested in it.
625
00:31:19,278 --> 00:31:22,246
Remember the underbar in Swift is the universal,
626
00:31:22,248 --> 00:31:25,749
I don't care about this thing, okay. So, looks a little odd
627
00:31:25,751 --> 00:31:28,318
to do this but, some would say this is a little bit better
628
00:31:28,320 --> 00:31:31,121
coding style, it's just looks, it makes it a little clearer
629
00:31:31,123 --> 00:31:35,125
to people reading your code that this is what you intend.
630
00:31:35,294 --> 00:31:39,229
Okay? Now this will, once we implement this,
631
00:31:39,231 --> 00:31:42,232
this'll load up the database with these tweets, but it's
632
00:31:42,234 --> 00:31:46,203
not gonna save that database. Okay, now if we're using a UI
633
00:31:46,205 --> 00:31:49,706
managed document, okay, then it would all be autosaving,
634
00:31:49,708 --> 00:31:51,942
fine. But here we use an app delegate one and
635
00:31:51,944 --> 00:31:55,012
even if we're not sure which one we're using, it can't hurt
636
00:31:55,014 --> 00:31:58,548
to save, okay, the context in the UI managed document case.
637
00:31:58,550 --> 00:32:00,817
So how do we save? Well, it's simple.
638
00:32:00,819 --> 00:32:03,854
We just say inManagedObjectContext, save.
639
00:32:03,856 --> 00:32:07,090
Okay? But of course, we're gonna have an error here,
640
00:32:07,092 --> 00:32:09,526
because it's gonna say hold on a second, well,
641
00:32:09,528 --> 00:32:12,629
first it's saying we're inside a block so we have to do self,
642
00:32:12,631 --> 00:32:16,366
so we'll do that. And we still have another error, which it
643
00:32:16,368 --> 00:32:21,204
says this call can throw, but it's not marked with try. So
644
00:32:21,206 --> 00:32:23,941
it's to say hey, pay attention, this can throw.
645
00:32:23,943 --> 00:32:26,643
So we need to put try in front And of course if we're going
646
00:32:26,645 --> 00:32:30,080
to try something we need to put it inside of a do loop.
647
00:32:30,082 --> 00:32:33,750
And catch any errors that come out. Okay, and
648
00:32:33,752 --> 00:32:35,652
we'll put it in a local variable we'll call error.
649
00:32:35,654 --> 00:32:38,088
This is going to be an N-S error instance,
650
00:32:38,090 --> 00:32:40,724
okay. We could look at the error code etcetera.
651
00:32:40,726 --> 00:32:42,192
I'm just going to go ahead and print it out.
652
00:32:42,194 --> 00:32:46,463
I'm going to say core data error. Error,
653
00:32:46,465 --> 00:32:47,664
something like that. This is bad,
654
00:32:47,666 --> 00:32:49,333
you wouldn't want to do this in your real code, okay.
655
00:32:49,335 --> 00:32:53,003
This is just demo ware here, you would want to decide what
656
00:32:53,005 --> 00:32:55,472
you wanted to do if you tried to save and it failed for
657
00:32:55,474 --> 00:33:01,378
some reason. Okay? So, anyway, so this is gonna save it.
658
00:33:01,380 --> 00:33:03,547
So this will work again, even if you were in an autosave and
659
00:33:03,549 --> 00:33:07,184
you iManage document, it's gonna work just fine. Okay?
660
00:33:07,186 --> 00:33:11,888
All right, let's jump back to where we were over here.
661
00:33:11,890 --> 00:33:17,361
Tweet. And implement this thing, TweetWithTwitterInfo.
662
00:33:17,363 --> 00:33:20,030
So I said that what TweetWithTwitterInfo's gonna
663
00:33:20,032 --> 00:33:22,165
do is first it's gonna look in the database and
664
00:33:22,167 --> 00:33:25,402
see if it can find this Twitter.Tweet. Okay.
665
00:33:25,404 --> 00:33:28,005
In the database already and if so it's gonna return it. So,
666
00:33:28,007 --> 00:33:31,575
how do we look in the database and find something? Well,
667
00:33:31,577 --> 00:33:34,845
we use this NSFetchRequest thing. Okay?
668
00:33:34,847 --> 00:33:37,881
So, we're gonna make a Fetch Request, okay? It always wants
669
00:33:37,883 --> 00:33:42,119
to know the entity name. So, here we're looking for Tweet
670
00:33:42,121 --> 00:33:45,222
in the database. Okay. So we've got this fetch request.
671
00:33:45,224 --> 00:33:47,090
I am going to sign that to a local variable here.
672
00:33:47,092 --> 00:33:51,461
Let request equal. And this request needs a predicate
673
00:33:51,463 --> 00:33:55,032
that says which tweet do you want. So I am gonna say okay,
674
00:33:55,034 --> 00:33:59,536
my predicate is NSPredicate with a format.
675
00:33:59,538 --> 00:34:03,740
And I am gonna say that the unique ID of this tweet
676
00:34:03,742 --> 00:34:06,676
equals something And what is that something?
677
00:34:06,678 --> 00:34:11,314
Well, it's the whatever the unique ID is in twitter.tweet.
678
00:34:11,316 --> 00:34:13,450
K? Which happens to be called ID. So
679
00:34:13,452 --> 00:34:18,155
I'm going to get my twitter info.id. K? So that, this
680
00:34:18,157 --> 00:34:22,459
is just a property and you might have used it with your
681
00:34:22,461 --> 00:34:25,562
homework number four but it's just a property that uniquely
682
00:34:25,564 --> 00:34:28,131
identifies this tweet. Okay, so i'm gonna go look in
683
00:34:28,133 --> 00:34:32,469
the database and see if i can find that, and the way we look
684
00:34:32,471 --> 00:34:37,741
in the database is we say, let query results.
685
00:34:37,743 --> 00:34:38,942
Just a local variable,
686
00:34:38,944 --> 00:34:42,679
equal the context that we are supposed to look in here,
687
00:34:42,681 --> 00:34:48,185
that's this context up here dot execute fetch request.
688
00:34:48,187 --> 00:34:50,754
And then, you give it that request, okay?
689
00:34:50,756 --> 00:34:52,089
So that's how we look in the database,
690
00:34:52,091 --> 00:34:54,791
but there's some problems with this. As you can see,
691
00:34:54,793 --> 00:34:59,830
little errors over here. One is that this thing throws,
692
00:34:59,832 --> 00:35:03,300
okay? Execute fetch request throws. So I need to say, try,
693
00:35:03,302 --> 00:35:06,870
okay? So that's problem number one of this. And
694
00:35:06,872 --> 00:35:09,172
if I'm going to try, then of course, I need to.
695
00:35:09,174 --> 00:35:14,344
Do a little do loop here and catch any errors.
696
00:35:14,346 --> 00:35:17,948
Now, if I'm actually getting this fetch request to look for
697
00:35:17,950 --> 00:35:21,785
this tweet and it's not there, what am I gonna Gonna do?
698
00:35:21,787 --> 00:35:23,320
There really nothing I can do.
699
00:35:23,322 --> 00:35:25,355
I just continue and try to create it.
700
00:35:25,357 --> 00:35:27,891
I'm gonna assume it's not there. Right if I search for
701
00:35:27,893 --> 00:35:29,993
it and I don't find it, I'm gonna assume it's not there.
702
00:35:29,995 --> 00:35:33,063
So basically when I catch an error I'm just gonna ignore
703
00:35:33,065 --> 00:35:37,901
the error and move on down here to keep trying to
704
00:35:37,903 --> 00:35:42,739
create it. But if I am able to find it right here, okay,
705
00:35:42,741 --> 00:35:45,175
if I do get a result there from,
706
00:35:45,177 --> 00:35:48,879
from doing this fetch. Then I'm going to say if I can let
707
00:35:48,881 --> 00:35:52,682
the tweet that's in there equal the query results,
708
00:35:52,684 --> 00:35:55,585
now remember the query results are an array of tweets.
709
00:35:55,587 --> 00:35:58,855
All the tweets that match my predicate My predicate is
710
00:35:58,857 --> 00:36:02,359
unique, so there should only be one in there. Either one or
711
00:36:02,361 --> 00:36:04,628
zero, okay? There's one in there if it found it.
712
00:36:04,630 --> 00:36:08,465
There's zero of them in there if it didn't find it, right?
713
00:36:08,467 --> 00:36:14,704
So, I'm gonna go use this method in array called first.
714
00:36:14,706 --> 00:36:18,341
Okay, that returned to the first item in the array,
715
00:36:18,343 --> 00:36:19,676
or nil, if the array is empty.
716
00:36:19,678 --> 00:36:22,345
So it never does array index out of bounds, so it's kind of
717
00:36:22,347 --> 00:36:25,649
a cool, there's also last, but first is kind of a cool one.
718
00:36:25,651 --> 00:36:27,250
And when I pull it out of there, of course,
719
00:36:27,252 --> 00:36:29,519
it needs to be a tweet. Okay, so
720
00:36:29,521 --> 00:36:33,456
if I can pull the first thing out of this result array and
721
00:36:33,458 --> 00:36:37,394
it's a tweet, I'm good to go. I can just return that tweet.
722
00:36:37,396 --> 00:36:40,030
Okay? Otherwise, I couldn't find the thing in there.
723
00:36:40,032 --> 00:36:42,232
I got to go create it in the data base.
724
00:36:42,234 --> 00:36:44,935
Everybody cool with this code? Now this is unfortunate
725
00:36:44,937 --> 00:36:47,671
a lot of code, okay? Seven lines of code here.
726
00:36:47,673 --> 00:36:50,707
This can all be done in one line. LIne of code, okay?
727
00:36:50,709 --> 00:36:53,343
So let's look at that. How can we do that? And in this is,
728
00:36:53,345 --> 00:36:55,645
requires you to start getting more and more familiar
729
00:36:55,647 --> 00:36:58,181
with optional chaining, okay. >> Cuz we got a lot of things
730
00:36:58,183 --> 00:37:00,217
that can go wrong here, okay? Things can be nil,
731
00:37:00,219 --> 00:37:03,420
all kinds of things happening right here. So believe it or
732
00:37:03,422 --> 00:37:05,889
not we can do this just by doing this.
733
00:37:05,891 --> 00:37:10,760
If let tweet = we're gonna try,
734
00:37:10,762 --> 00:37:14,531
and if we fail, return nill. Try? means try it, and if it
735
00:37:14,533 --> 00:37:18,969
fails return nill. To ask our context to execute this fetch
736
00:37:18,971 --> 00:37:23,440
request. Okay, this same fetch request we just did, okay.
737
00:37:23,442 --> 00:37:27,811
If that is successful, then go on to get the first thing in
738
00:37:27,813 --> 00:37:34,517
the array. And see if you can cast it to a tweet, okay.
739
00:37:35,721 --> 00:37:40,090
That is identical to this, okay. And
740
00:37:40,092 --> 00:37:43,326
it reads a little more simply a little more like English,
741
00:37:43,328 --> 00:37:47,464
okay. Try to use the context to execute this Fetch request.
742
00:37:47,466 --> 00:37:49,399
Get the first thing, a-and if you can get this
743
00:37:49,401 --> 00:37:53,670
first thing as a tweet, Then return it. Okay? So
744
00:37:53,672 --> 00:37:57,007
it's actually better code, I believe more readable than
745
00:37:57,009 --> 00:37:57,574
the other. Yeah? >> So
746
00:37:57,576 --> 00:38:01,478
where would we link the catch? >> Yeah, so
747
00:38:01,480 --> 00:38:03,880
we can't do this if we're gonna catch.
748
00:38:03,882 --> 00:38:06,650
If we're gonna catch and do something we ignore so,
749
00:38:06,652 --> 00:38:08,318
we didn't do that, but if we're gonna catch and
750
00:38:08,320 --> 00:38:11,755
do something then we would have to use this more verbose
751
00:38:12,190 --> 00:38:13,957
Good question there.
752
00:38:13,959 --> 00:38:18,828
Alright, so we got that, okay. So, what if we can't? Okay?
753
00:38:18,830 --> 00:38:22,265
What if we could not find the tweet in the database?
754
00:38:22,267 --> 00:38:24,567
Now we need to create the tweet, okay?
755
00:38:24,569 --> 00:38:27,270
So here I'm gonna say, okay I couldnt it so
756
00:38:27,272 --> 00:38:30,740
I couldn't look it up. So if I can create it which is,
757
00:38:30,742 --> 00:38:34,144
if you'll remember from the slide, NS entity
758
00:38:34,146 --> 00:38:40,350
description.insert, insert new object for
759
00:38:40,352 --> 00:38:43,286
entity for name, okay? We're creating a tweet.
760
00:38:43,288 --> 00:38:47,590
The manage object context is context, okay, and
761
00:38:47,592 --> 00:38:53,697
we obviously want it to be a tweet. Okay?
762
00:38:53,932 --> 00:38:55,832
So here we've created the tweet.
763
00:38:55,834 --> 00:38:59,302
If we£re able to, okay if let, so we£re gonna see if we can
764
00:38:59,304 --> 00:39:02,372
create it by inserting something in the database.
765
00:39:02,374 --> 00:39:06,242
Everybody cool with that line of code.
766
00:39:06,244 --> 00:39:09,746
All right, now we've created a tweet, but it£s empty, okay.
767
00:39:09,748 --> 00:39:10,914
It£s all everything£s nill in there,
768
00:39:10,916 --> 00:39:13,416
so we need to start setting some of the values like really
769
00:39:13,418 --> 00:39:16,019
importantly we better set that unique equal to the Twitter
770
00:39:16,021 --> 00:39:19,956
info£s ID. Okay, make sure it's unique, we also wanna set
771
00:39:19,958 --> 00:39:26,196
the tweets text equal to the Twitter info's text. I think
772
00:39:26,198 --> 00:39:29,499
we also had the tweet posted which would be the Twitter
773
00:39:29,501 --> 00:39:33,870
info's created, okay? So we just wanna go through here and
774
00:39:33,872 --> 00:39:36,506
grab the information that we got from it network, and
775
00:39:36,508 --> 00:39:38,675
put it in the database that's basically what we're doing
776
00:39:38,677 --> 00:39:41,311
here. Okay? Now, what about this one,
777
00:39:41,313 --> 00:39:47,917
tweet.tweeter =? Ooh, what do we, how do we do that?
778
00:39:47,919 --> 00:39:50,820
Gotta set up that relationship between [LAUGH] tweeter and
779
00:39:50,822 --> 00:39:52,789
tweets, okay? Well, to do that,
780
00:39:52,791 --> 00:39:56,092
we have to create the Twitter user. That goes for this,
781
00:39:56,094 --> 00:39:59,796
okay, and by that I mean this Twitter user right here.
782
00:39:59,798 --> 00:40:04,000
This managed object Twitter user. Okay. And to create
783
00:40:04,002 --> 00:40:07,103
a twitter user, I'm going to use the exact same kind of
784
00:40:07,105 --> 00:40:10,440
code that I just was in the middle of typing and tweet.
785
00:40:10,442 --> 00:40:12,942
I'm going to type it in really fast because
786
00:40:12,944 --> 00:40:17,447
it's exactly the same, okay. So I'll go through it here but
787
00:40:17,449 --> 00:40:17,781
it's exactly the same.
788
00:40:17,783 --> 00:40:20,984
So I need to import twitter, just like I did in that one.
789
00:40:20,986 --> 00:40:24,354
So here we're in twitter user. Okay? And this one's called
790
00:40:24,356 --> 00:40:27,157
Twitter user with Twitter info. This time the Twitter
791
00:40:27,159 --> 00:40:30,760
info is not a Twitter, tweet, it's a Twitter.user. Okay,
792
00:40:30,762 --> 00:40:32,829
and we still have to pass the the manage object context.
793
00:40:32,831 --> 00:40:36,833
And it returns a Twitter user if it can find one or create.
794
00:40:36,835 --> 00:40:40,537
Okay, now for Twitter user the unique is the screen name.
795
00:40:40,539 --> 00:40:43,239
Every Twitter user has their own unique screen name, so
796
00:40:43,241 --> 00:40:43,673
we don't have a unique
797
00:40:43,675 --> 00:40:46,009
we just use the screen name as the unique thing. Here,
798
00:40:46,011 --> 00:40:48,678
we're doing that try to see if we can find it, if we can, we
799
00:40:48,680 --> 00:40:52,882
return it. If we can't, we're inserting a new one, okay, and
800
00:40:52,884 --> 00:40:56,686
setting the screen name and the name, and returning it.
801
00:40:56,688 --> 00:41:01,157
Okay? Now, notice that I'm not setting the tweet's NS set.
802
00:41:01,159 --> 00:41:05,161
I'm not adding anything to the tweet NS set here. Okay,
803
00:41:05,163 --> 00:41:05,261
at this point I don't know any tweets that are in a set.
804
00:41:05,263 --> 00:41:08,598
that's because I,
805
00:41:08,600 --> 00:41:10,233
So I'm just creating a twitter user that so
806
00:41:10,235 --> 00:41:16,005
far, has no tweets in the database, okay, when I create.
807
00:41:16,608 --> 00:41:19,275
Alright? Now let's go back here to where we're creating
808
00:41:19,277 --> 00:41:22,946
the tweet, and now to set this tweet.tweeter all we need to
809
00:41:22,948 --> 00:41:29,152
do is say twitter user Twitter user with Twitter info.
810
00:41:29,154 --> 00:41:35,925
And now we pass the user, which is the Twitterinfo.user
811
00:41:35,927 --> 00:41:40,163
in inManagedObjectContext.
812
00:41:40,165 --> 00:41:45,835
The context, okay? This creates that Twitter user.
813
00:41:45,837 --> 00:41:50,173
Okay, and sets it to this bar which is that relationship.
814
00:41:50,175 --> 00:41:53,576
The NS Set in the Twitter user, will have this tweet
815
00:41:53,578 --> 00:41:56,246
auto, tweet automatically added to it, okay.
816
00:41:56,248 --> 00:41:58,481
You don't have to do both sides, if you do one side,
817
00:41:58,483 --> 00:42:01,184
it does the other, if I'd put a tweet in the Twitter user
818
00:42:01,186 --> 00:42:05,588
NS Set, the Twitter would have been set to it. Okay? If you
819
00:42:05,590 --> 00:42:08,791
do, if you touch one side, the other side gets kept in sync,
820
00:42:08,793 --> 00:42:10,493
that's the awesome thing about Core Data. So
821
00:42:10,495 --> 00:42:12,428
you never have to worry about database integrity,
822
00:42:12,430 --> 00:42:15,465
it manages the integrity all the time for you, okay?
823
00:42:15,467 --> 00:42:19,569
Very important to understand. All right, so we got it.
824
00:42:19,571 --> 00:42:21,437
So as long as we were able to do,
825
00:42:21,439 --> 00:42:25,208
either look it up or create it, we can return it okay?
826
00:42:25,210 --> 00:42:29,212
Whoops. Return the tweet, otherwise if we couldn£t do
827
00:42:29,214 --> 00:42:32,282
either of those things then we£ll just return nill.
828
00:42:32,984 --> 00:42:36,185
Okay very unlikely cuz this almost never fails. Inserting
829
00:42:36,187 --> 00:42:42,091
a new object, it£s gonna work for G1 100% of the time. Okay?
830
00:42:42,093 --> 00:42:47,664
Got that? All right. So back to where we were calling this.
831
00:42:48,099 --> 00:42:49,299
Right here. Everybody cool with this?
832
00:42:49,301 --> 00:42:53,937
You can see how this is going to put this array of tweets,
833
00:42:53,939 --> 00:43:00,043
okay, into the database. All right.
834
00:43:00,045 --> 00:43:07,016
Now, let's go ahead and before we do our new mvc
835
00:43:07,018 --> 00:43:08,851
let's just make sure this is working okay?
836
00:43:08,853 --> 00:43:12,088
I'm gonna write a little function here that prints out
837
00:43:12,090 --> 00:43:12,689
what's in the database.
838
00:43:12,691 --> 00:43:14,624
How many Twitter users, how many tweets.
839
00:43:14,626 --> 00:43:17,493
Let's just see if this working okay? So let's write,
840
00:43:17,495 --> 00:43:19,429
let's write that function, see what that would look like.
841
00:43:19,431 --> 00:43:22,098
So I'm gonna have a private funk. I'm gonna call
842
00:43:22,100 --> 00:43:26,402
it PrintDatabaseStatistics cuz it's basically going just
843
00:43:26,404 --> 00:43:31,608
print how many, Twitter users are and how many, of the,
844
00:43:31,610 --> 00:43:36,179
how many tweets and how many Twitter users, so of course we
845
00:43:36,181 --> 00:43:42,619
have to perform this in a block Okay, cuz we always
846
00:43:42,621 --> 00:43:47,857
have to do that when we're doing manage object context
847
00:43:47,859 --> 00:43:50,927
stuff and I'm going to watch this,
848
00:43:50,929 --> 00:43:55,865
if let results equal try to execute on our context and
849
00:43:55,867 --> 00:44:00,403
actually we can exclamation point that.
850
00:44:00,405 --> 00:44:01,671
Execute fetch request. Now watch.
851
00:44:01,673 --> 00:44:04,207
I'm going to create a fetch request on the fly here.
852
00:44:04,209 --> 00:44:07,710
Fetch request with an entity name and what am I looking for
853
00:44:07,712 --> 00:44:10,380
here? Okay, let's count the Twitter users first. So
854
00:44:10,382 --> 00:44:14,250
Twitter user. Now, notice I have a fetch request here with
855
00:44:14,252 --> 00:44:17,186
now predicate. I just created an quest
856
00:44:17,188 --> 00:44:20,890
with no predicate set on it. That gets all twitter users.
857
00:44:20,892 --> 00:44:23,693
If you have a without a predicate, that means give me
858
00:44:23,695 --> 00:44:26,529
all of them, all of whatever the entity is
859
00:44:26,531 --> 00:44:31,200
that you're looking for. Okay, so if I'm able to do that and
860
00:44:31,202 --> 00:44:34,337
get the results, then I'm just going to print that we have
861
00:44:34,339 --> 00:44:40,376
Result.count how ever many we go here.
862
00:44:40,378 --> 00:44:44,847
Twitter users. Okay so, that£s this little code right
863
00:44:44,849 --> 00:44:47,450
here, we£ll print out how many Twitter users are in
864
00:44:47,452 --> 00:44:50,720
the database. Alright, now there£s actually a better
865
00:44:50,722 --> 00:44:53,589
way to do this because this is a bit of a problem, when I
866
00:44:53,591 --> 00:44:56,259
execute that vet request it gives me every single
867
00:44:56,261 --> 00:44:58,094
Twitter user out of the database and
868
00:44:58,096 --> 00:45:01,764
into my memory. Now, there just husks because remember I
869
00:45:01,766 --> 00:45:05,535
talked about the faulting thing. So, the attributes like
870
00:45:05,537 --> 00:45:08,504
the Twitter user screen name and name those aren't pulled
871
00:45:08,506 --> 00:45:11,941
from the database. But, like a little husk is there and
872
00:45:11,943 --> 00:45:13,509
if I had thousands thousands of Twitter users,
873
00:45:13,511 --> 00:45:16,112
that would be unnecessarily thousands of bytes
874
00:45:16,114 --> 00:45:18,715
being pulled into my memory. Okay. So there's a better way
875
00:45:18,717 --> 00:45:22,318
to do this. There's actually an execute equivalent, which
876
00:45:22,320 --> 00:45:24,921
only counts how many things there are, and it counts it on
877
00:45:24,923 --> 00:45:27,356
the database side without pulling everything over and
878
00:45:27,358 --> 00:45:31,794
counting it in an array. So for the tweets I'll use that
879
00:45:31,796 --> 00:45:36,999
way of doing it and that looks like this. I'm gonna let tweet
880
00:45:37,001 --> 00:45:41,971
count equal our context-managed object,
881
00:45:41,973 --> 00:45:46,075
countForFetchRequest, it's called. Okay? Instead of
882
00:45:46,077 --> 00:45:48,511
execute fetch request it calls count for fetch request.
883
00:45:48,513 --> 00:45:51,814
I'm still gonna create a fetch request on the fly here, and
884
00:45:51,816 --> 00:45:57,353
this time, I am, In this fetch request entity name.
885
00:45:57,355 --> 00:46:01,124
This time I'm searching for tweets, not Twitter users.
886
00:46:01,126 --> 00:46:02,792
Notice there's this little error pointer right here.
887
00:46:02,794 --> 00:46:06,496
You see this error pointer? This is an interesting method.
888
00:46:06,498 --> 00:46:06,896
I'm not sure why it does
889
00:46:06,898 --> 00:46:08,564
it this way. But instead of throwing the error,
890
00:46:08,566 --> 00:46:10,633
it actually returns it through a pointer.
891
00:46:10,635 --> 00:46:13,669
And if you're not interested, you can just say, nil. Say,
892
00:46:13,671 --> 00:46:17,373
I don't want any error information. Okay?
893
00:46:17,375 --> 00:46:20,710
So it's kind of an odd little method in that way.
894
00:46:20,712 --> 00:46:22,278
That's going to return the tweet count.
895
00:46:22,280 --> 00:46:26,983
And now, I can just print. The tweetCount,
896
00:46:26,985 --> 00:46:32,221
Tweets. So this is better code right here than this, okay?
897
00:46:32,223 --> 00:46:33,456
It's gonna be much more efficient.
898
00:46:33,458 --> 00:46:36,492
I'll leave them both in here so that when I post the demo,
899
00:46:36,494 --> 00:46:37,360
you can see the difference.
900
00:46:37,362 --> 00:46:41,664
Here, I'll even put a comment in here. A more efficient way
901
00:46:41,666 --> 00:46:47,436
to count objects. Okay, but they're both just counting.
902
00:46:47,438 --> 00:46:48,638
This is counting the number of Twitter users,
903
00:46:48,640 --> 00:46:51,808
this is counting the number of tweets. Okay, so
904
00:46:51,810 --> 00:46:55,578
let's call this print every time we update the database,
905
00:46:55,580 --> 00:46:55,745
let's go ahead and
906
00:46:55,747 --> 00:46:58,414
call this print database statistics thing, and I'm
907
00:46:58,416 --> 00:47:02,385
actually gonna put a little print in here saying, done
908
00:47:02,387 --> 00:47:06,255
printing database statistics. Okay, I'm gonna put
909
00:47:06,257 --> 00:47:09,258
this in here because something interesting is going to happen
910
00:47:09,260 --> 00:47:14,130
with that print. Okay? All right,
911
00:47:14,132 --> 00:47:16,199
let's go ahead and run. Now we should be
912
00:47:16,201 --> 00:47:19,168
loading up the database with Twitter users and tweets every
913
00:47:19,170 --> 00:47:21,204
time we search for something, hashtag Stanford.
914
00:47:21,206 --> 00:47:24,407
We're not showing any of it but hopefully we'll be loading
915
00:47:24,409 --> 00:47:30,346
up the database. Okay, we have to,
916
00:47:30,348 --> 00:47:32,682
we'll set our prototypes here when we get to that.
917
00:47:32,684 --> 00:47:36,085
So, here's our segue. You can see our table here is empty.
918
00:47:36,087 --> 00:47:38,154
We haven't done anything to implement that MVC, so
919
00:47:38,156 --> 00:47:43,192
it's just a blank MVC. But if we do #stanford,
920
00:47:43,194 --> 00:47:48,064
okay, it searches and we'll see down here in our console
921
00:47:48,466 --> 00:47:51,100
that it had a hundred tweets, which is right,
922
00:47:51,102 --> 00:47:53,502
because remember here in our code,
923
00:47:53,504 --> 00:47:55,771
we fetch a hundred tweets, okay?
924
00:47:55,773 --> 00:47:59,375
So, that's doing that properly and it's only 75 users,
925
00:47:59,377 --> 00:48:02,712
why is that? Well, probably there are people in here who
926
00:48:02,714 --> 00:48:04,080
are tweeting more than ones, okay?
927
00:48:04,082 --> 00:48:07,016
Numerous fill like here, this guy, I am not the clone. Okay?
928
00:48:07,018 --> 00:48:10,152
He tweeted twice right there, so you only count it once.
929
00:48:10,154 --> 00:48:12,688
So that sounds right that there's something,
930
00:48:12,690 --> 00:48:15,725
75 and 100. Now, here's a very important test
931
00:48:15,727 --> 00:48:16,559
to make sure things are working.
932
00:48:16,561 --> 00:48:19,629
I'm gonna look for hashtag Stanford again and
933
00:48:19,631 --> 00:48:25,201
I better not get 200 tweets. There it is, only 100.
934
00:48:25,203 --> 00:48:27,803
So nobody even tweeted to Stanford,
935
00:48:27,805 --> 00:48:32,875
one of you maybe tweeted, make it work for us here. Okay,
936
00:48:32,877 --> 00:48:34,443
let me try something else.
937
00:48:34,445 --> 00:48:38,080
How about #SFGiants, believe they're playing right now.
938
00:48:38,082 --> 00:48:40,883
Okay, now we have 200 tweets because we searched for
939
00:48:40,885 --> 00:48:44,654
something different, okay. So we've got the 100 tweets from
940
00:48:44,656 --> 00:48:46,322
Stanford, and hundreds of tweets from Giants, so
941
00:48:46,324 --> 00:48:49,926
I'm gonna do Giants again. Still nobody tweeting.
942
00:48:49,928 --> 00:48:51,260
Nobody's interested in the Giants or
943
00:48:51,262 --> 00:48:56,432
in Stanford currently, I guess. There we go.
944
00:48:56,434 --> 00:48:59,402
Somebody finally tweeted something new for the Giants.
945
00:48:59,404 --> 00:49:00,503
Okay. So now, we have 201 tweets,
946
00:49:00,505 --> 00:49:04,573
okay. Doesn't sound like things are going well for
947
00:49:04,575 --> 00:49:09,011
the Giants. Hope I didn't spoil it for anybody. Okay.
948
00:49:09,013 --> 00:49:11,247
So, we got our thing working right here.
949
00:49:11,249 --> 00:49:14,884
Look at this though, dome printing database statistics,
950
00:49:14,886 --> 00:49:20,256
that's printing out before it prints, isn't that weird?
951
00:49:20,258 --> 00:49:23,659
This print is definitely after print database statistics, so
952
00:49:23,661 --> 00:49:28,130
why is this printing out before? Anyone have an idea?
953
00:49:30,101 --> 00:49:34,103
It begins with an A. Asynchronous,
954
00:49:34,105 --> 00:49:37,940
yeah. Okay, this stuff, these performed blocks both this
955
00:49:37,942 --> 00:49:40,476
performed block right here, and also this performed block
956
00:49:40,478 --> 00:49:44,013
up here happen asynchronously. These blocks just get put onto
957
00:49:44,015 --> 00:49:46,015
their queue, and in this case it's the main queue, but
958
00:49:46,017 --> 00:49:48,217
that doesn't matter, whatever the queue is and
959
00:49:48,219 --> 00:49:48,751
they get put on that.
960
00:49:48,753 --> 00:49:50,886
So, when I do printdatabase for statistics,
961
00:49:50,888 --> 00:49:54,123
it does this line of code which returns immediately.
962
00:49:54,125 --> 00:49:56,993
It instantly returns, it puts that block on some queue and
963
00:49:56,995 --> 00:49:59,562
returns immediately. It doesn't wait to execute
964
00:49:59,564 --> 00:50:02,064
this code because this is not perform block and wait, it's
965
00:50:02,066 --> 00:50:05,634
just perform block. So this goes, returns immediately, and
966
00:50:05,636 --> 00:50:08,304
then this prints out immediately. Then later, this
967
00:50:08,306 --> 00:50:11,440
block gets pulled off onto the queue, the main queue, and
968
00:50:11,442 --> 00:50:16,746
run does its thing which causes this output. Okay,
969
00:50:16,748 --> 00:50:17,813
see why I put this in here so
970
00:50:17,815 --> 00:50:20,850
you understand that? This is happening asynchronously
971
00:50:20,852 --> 00:50:23,452
unless you say perform block and wait. It's asynchronous.
972
00:50:23,454 --> 00:50:30,359
All right, now we got the data in our database. Excellent,
973
00:50:30,361 --> 00:50:33,562
now we can go on back to our storyboard right here.
974
00:50:33,564 --> 00:50:39,135
And we can implement this NBC, this new NBC of ours.
975
00:50:39,137 --> 00:50:41,704
We need to do some things to do that.
976
00:50:41,873 --> 00:50:45,007
We need to make sure that we have for example,
977
00:50:45,009 --> 00:50:48,844
reuse identifier for this cell. So what is that cell?
978
00:50:48,846 --> 00:50:51,947
It's basically a Twitter user, so I'll call it Twitter user
979
00:50:51,949 --> 00:50:56,285
cell. We want these probably to be subtitle,
980
00:50:56,287 --> 00:50:57,053
okay, instead of custom cell.
981
00:50:57,055 --> 00:50:59,822
We don't need custom cells for any reason.
982
00:50:59,824 --> 00:51:03,492
Whoops. We can make sure we have our identities for
983
00:51:03,494 --> 00:51:05,761
the whole table. We do. That's all looking good.
984
00:51:05,763 --> 00:51:09,098
We can make sure we have this set, okay? Which we do. Okay?
985
00:51:09,100 --> 00:51:12,201
So it's all good, all right? So we're all ready to.
986
00:51:12,203 --> 00:51:16,405
Basically implement this guy's class his custom sub class.
987
00:51:16,407 --> 00:51:19,141
So let's go take a look at that, that's right here.
988
00:51:19,143 --> 00:51:22,445
Whenever we implement a new mvc, a great thing to do
989
00:51:22,447 --> 00:51:25,014
I think, it's the fourth time I've said it in this class.
990
00:51:25,016 --> 00:51:27,983
Come up with this model first, try to understand what this
991
00:51:27,985 --> 00:51:31,554
thing is actually doing by understanding its model. Now,
992
00:51:31,556 --> 00:51:35,324
this is a very simple NVC, it takes some mention,
993
00:51:35,326 --> 00:51:39,395
like hashtag stamper and it searches for all the users,
994
00:51:39,397 --> 00:51:44,166
okay, who have tweeted that, okay, in, in our database.
995
00:51:44,168 --> 00:51:48,838
So a clear, very important part is that mention. Okay,
996
00:51:48,840 --> 00:51:50,639
whatever that mention is, hashtag Stanford,
997
00:51:50,641 --> 00:51:53,209
that clearly needs to be part of our model. Okay,
998
00:51:53,211 --> 00:51:54,944
without that we can't do anything. But
999
00:51:54,946 --> 00:51:59,115
another really important part of the model is that database.
1000
00:51:59,117 --> 00:52:01,050
Okay? Without that database I can't look
1001
00:52:01,052 --> 00:52:03,953
this mention up. Okay? So another one is
1002
00:52:03,955 --> 00:52:07,857
a managed object model just like our other one, actually,
1003
00:52:07,859 --> 00:52:09,525
had part of its model as well. Okay,
1004
00:52:09,527 --> 00:52:14,263
that's an NSManaged not models and NSManagedObjectContext,
1005
00:52:14,265 --> 00:52:19,235
NSManagedObjectContext, okay? I'll have that be optional,
1006
00:52:19,237 --> 00:52:22,471
but to be frank, if that's nil I'm not gonna, I'm gonna have
1007
00:52:22,473 --> 00:52:27,810
an empty table, okay? Because this time really demands that.
1008
00:52:27,812 --> 00:52:31,080
If either of these change I'm gonna do a did set here and
1009
00:52:31,082 --> 00:52:33,782
update my ui, which basically means I'm gonna have to reload
1010
00:52:33,784 --> 00:52:40,089
my table, okay? So, I'll do for either of these, and
1011
00:52:40,091 --> 00:52:41,290
then of course I need a private,
1012
00:52:41,292 --> 00:52:46,529
private funk update ui. Okay,
1013
00:52:46,531 --> 00:52:49,832
and in here we're gonna have to use this information. Okay,
1014
00:52:49,834 --> 00:52:54,336
it's complaining here because we have to import core data.
1015
00:52:54,338 --> 00:52:58,807
Okay. Notice that this particular NBC does not import
1016
00:52:58,809 --> 00:53:04,346
Twitter. Okay. Because it's purely using the database,
1017
00:53:04,815 --> 00:53:07,383
the other MVC uses Twitter and the database, this one
1018
00:53:07,385 --> 00:53:09,685
only uses the database, so it doesn't import Twitter,
1019
00:53:09,687 --> 00:53:12,555
just core data. All right, so we'll get to
1020
00:53:12,557 --> 00:53:15,357
update U-I. A second one thing I wanted to do before that is
1021
00:53:15,359 --> 00:53:18,561
do our prepare for Segway and set these two things,
1022
00:53:18,563 --> 00:53:21,030
okay, so we have to be prepared for Segway
1023
00:53:21,032 --> 00:53:25,901
back in this MVC over here. So let's go back to that NBC,
1024
00:53:25,903 --> 00:53:28,671
we'll put it, right here, prepare for segue this prepare
1025
00:53:28,673 --> 00:53:33,108
for segue, by now prepare for segue should be old
1026
00:53:33,110 --> 00:53:35,811
hat for you guys. You've done so many segues, but we're just
1027
00:53:35,813 --> 00:53:39,415
going to check to see if the segue's identifier is right.
1028
00:53:39,417 --> 00:53:42,351
That's going to be, what did I call that thing?
1029
00:53:42,353 --> 00:53:42,685
I can't even remember now,
1030
00:53:42,687 --> 00:53:47,289
let's look in our story board. This was called, copy and
1031
00:53:47,291 --> 00:53:51,360
paste. Tweeters mentioning search term, okay?
1032
00:53:51,362 --> 00:53:53,562
So, if it's the tweeters mentioning search term segue,
1033
00:53:53,564 --> 00:53:58,000
then I need to get the destination view controller as
1034
00:53:58,002 --> 00:54:03,105
a tweeter table u controller. So I'm gonna say if I can let,
1035
00:54:03,107 --> 00:54:07,176
the tweeters TVC we'll call it,
1036
00:54:07,178 --> 00:54:10,679
equal the segue.destinationViewContro-
1037
00:54:10,681 --> 00:54:14,450
ller as a TweetersTableViewController.
1038
00:54:14,452 --> 00:54:15,384
Okay, so I've got a hold of it.
1039
00:54:15,386 --> 00:54:19,521
Now I can just set the public API the model of this, so
1040
00:54:19,523 --> 00:54:25,094
it's got the mention which is just our search text, and
1041
00:54:25,096 --> 00:54:26,595
actually don't even need to do that.
1042
00:54:26,597 --> 00:54:31,800
This is our search text and tweeters.tvc.managedobjectsco-
1043
00:54:31,802 --> 00:54:36,138
ntext is our managed objects context, okay, so
1044
00:54:36,140 --> 00:54:36,272
we just prepare.
1045
00:54:36,274 --> 00:54:41,543
That's all we're doing here is preparing. Now that's okay,
1046
00:54:41,545 --> 00:54:44,613
now back to here. Update U-I. So, how are we going to update
1047
00:54:44,615 --> 00:54:46,015
this U-I? We need to have this table,
1048
00:54:46,017 --> 00:54:49,885
and we need to have it full of Twitter users who have Tweeted
1049
00:54:49,887 --> 00:54:53,289
a Tweet that has this mention hashtag Stanford in there or
1050
00:54:53,291 --> 00:54:56,358
whatever, okay. How are we going to do that? Well,
1051
00:54:56,360 --> 00:54:59,061
we're gonna use an NS fetched results controller,
1052
00:54:59,063 --> 00:55:01,730
that really cool object I told you about the end of the last
1053
00:55:01,732 --> 00:55:05,267
lecture. And that is an object that basically just glues
1054
00:55:05,269 --> 00:55:07,636
an NS fetch request to a table view,
1055
00:55:07,638 --> 00:55:09,138
and that's exactly what we want here.
1056
00:55:09,140 --> 00:55:10,306
We're gonna create an NS fetch request,
1057
00:55:10,308 --> 00:55:12,875
which fetches the twitterings, we want, then we're
1058
00:55:12,877 --> 00:55:16,412
gonna glue it on this table, okay? And in that way, anytime
1059
00:55:16,414 --> 00:55:19,515
the database changes in a way that those users would change,
1060
00:55:19,517 --> 00:55:21,450
it's gonna automatically update the table, and
1061
00:55:21,452 --> 00:55:23,819
we don't have to do anything else, okay?
1062
00:55:23,821 --> 00:55:27,656
Now, let's remind ourselves how NS Fetch Request,
1063
00:55:27,658 --> 00:55:32,695
NS Fetch Request, NS Fetch Request Controller How
1064
00:55:32,697 --> 00:55:35,864
it works, okay? It's got two pieces to it, and
1065
00:55:35,866 --> 00:55:38,334
I'm gonna have both of those pieces implemented for
1066
00:55:38,336 --> 00:55:41,470
you using this class that you're gonna be able to use
1067
00:55:41,472 --> 00:55:44,073
for your homework, it's right over here,
1068
00:55:44,075 --> 00:55:46,842
called core data table view controller.
1069
00:55:46,844 --> 00:55:48,644
So let's drag this core data view controller.
1070
00:55:48,646 --> 00:55:51,480
I'm gonna put it in my core data group right here and
1071
00:55:51,482 --> 00:55:56,118
copy it in, okay. All right so let's take a look at this
1072
00:55:56,120 --> 00:56:00,122
thing and see how ns fetched results controller works.
1073
00:56:00,124 --> 00:56:02,558
So, here's core data table view controller,
1074
00:56:02,560 --> 00:56:04,727
it's a sub class of ui table view controller,
1075
00:56:04,729 --> 00:56:07,863
you're gonna make it be a sub class, you're gonna make it
1076
00:56:07,865 --> 00:56:10,833
be the super class of your ui table view controller.
1077
00:56:10,835 --> 00:56:12,735
So back here in our TweetersTableView,
1078
00:56:12,737 --> 00:56:15,471
instead of inheriting from UITableViewController,
1079
00:56:15,473 --> 00:56:19,007
I'm going to inherit from CoreDateTableViewController.
1080
00:56:19,009 --> 00:56:21,944
That way, I'll get everything you get from
1081
00:56:21,946 --> 00:56:22,277
UITableViewController.
1082
00:56:22,279 --> 00:56:25,314
But I'm also going to get this NS Fetch results controller
1083
00:56:25,316 --> 00:56:28,317
stuff, okay? So let's look at that some more. There's two
1084
00:56:28,319 --> 00:56:32,654
parts to the NS Fetch results controller. One is that
1085
00:56:32,656 --> 00:56:37,226
I can use the [INAUDIBLE] to implement my entire UI table
1086
00:56:37,228 --> 00:56:39,862
view data source except for its self erodement index
1087
00:56:39,864 --> 00:56:42,698
path, okay? You still have to implement that. But all
1088
00:56:42,700 --> 00:56:45,567
the other ones look numbers of sections in table view,
1089
00:56:45,569 --> 00:56:46,769
numbers of rows in section,
1090
00:56:46,771 --> 00:56:48,837
even the section header titers, titles,
1091
00:56:48,839 --> 00:56:52,741
right the header titles, and footer titles section indexes.
1092
00:56:52,743 --> 00:56:54,176
All this stuff, it implements all that stuff.
1093
00:56:54,178 --> 00:56:57,079
See how the implementation of these is all using the fetch
1094
00:56:57,081 --> 00:57:00,649
results controller? So that's part A of how the NS
1095
00:57:00,651 --> 00:57:03,786
fetch results controller works. Part B is that
1096
00:57:03,788 --> 00:57:07,823
the fetch results controller has a delegate, okay? And
1097
00:57:07,825 --> 00:57:11,193
that delegate can implement these methods.
1098
00:57:11,195 --> 00:57:14,730
That will be called anytime the database changes. So
1099
00:57:14,732 --> 00:57:17,933
anytime anything happens in the database that might affect
1100
00:57:17,935 --> 00:57:20,602
this NS fetch request, okay? The delegate,
1101
00:57:20,604 --> 00:57:24,039
the NS Fetch delegate is going to be called and
1102
00:57:24,041 --> 00:57:27,543
sent these messages. And look what these things do. They
1103
00:57:27,545 --> 00:57:30,746
just update the table view, see? Table view, insert rows,
1104
00:57:30,748 --> 00:57:34,450
delete rows. You know, reload rows, that's because you know,
1105
00:57:34,452 --> 00:57:36,485
this is because something got deleted from the database,
1106
00:57:36,487 --> 00:57:37,853
this is something got inserted in the database,
1107
00:57:37,855 --> 00:57:42,791
this something changed in the database. Okay? So that's part
1108
00:57:42,793 --> 00:57:46,662
two is this, delegate of the NSFetchedResultsController.
1109
00:57:46,664 --> 00:57:50,232
So I've given you the codes to do both those things. Okay,
1110
00:57:50,234 --> 00:57:51,300
to implement this delegate, and
1111
00:57:51,302 --> 00:57:55,537
also to implement all of this UI table view data source.
1112
00:57:55,539 --> 00:57:57,906
All you have to do is set this var
1113
00:57:57,908 --> 00:57:59,274
in the CoreDataTableViewController,
1114
00:57:59,276 --> 00:58:01,577
which is an NSFetchedResultsController.
1115
00:58:01,579 --> 00:58:06,181
And all that happens when you set this var in didSet is,
1116
00:58:06,183 --> 00:58:07,850
it sets itself as the delegate, so
1117
00:58:07,852 --> 00:58:10,319
that all these methods down here at the bottom will work.
1118
00:58:10,321 --> 00:58:14,490
Okay, then it tries to perform the fetch. Okay, if it
1119
00:58:14,492 --> 00:58:17,092
catches it, it will report an error if there is any.
1120
00:58:17,094 --> 00:58:20,028
And then it reloads the table and that's it, okay.
1121
00:58:20,030 --> 00:58:22,264
The fetch results are always going to automatically
1122
00:58:22,266 --> 00:58:26,068
be linked now. So all you have to do to use Core Data table
1123
00:58:26,070 --> 00:58:28,203
controller is set this fetch results controller.
1124
00:58:28,205 --> 00:58:31,006
You have to create and set one of these and then it will just
1125
00:58:31,008 --> 00:58:35,978
work by magic. Okay. So let's go back here. Do exactly that.
1126
00:58:35,980 --> 00:58:40,382
In our update UI, we need to say self dot fetched results
1127
00:58:40,384 --> 00:58:43,385
controller equals. We do not actually need to say self
1128
00:58:43,387 --> 00:58:46,088
dot, but I just wanted to ehm emphasize that it is something
1129
00:58:46,090 --> 00:58:49,491
we are inheriting from core table core data tableview.
1130
00:58:49,493 --> 00:58:51,159
Okay, we can just set our fetch results control or
1131
00:58:51,161 --> 00:58:54,863
something. So we are going to create a new fetch result
1132
00:58:54,865 --> 00:58:58,267
controller. Okay.
1133
00:58:58,269 --> 00:59:02,037
And here are all of what the things in the initializer
1134
00:59:02,039 --> 00:59:05,207
look like. Okay. First, there's the request.
1135
00:59:05,209 --> 00:59:08,243
This is the NSFetchRequest that is going to
1136
00:59:08,245 --> 00:59:09,344
glue on to the table view. Okay.
1137
00:59:09,346 --> 00:59:13,115
We'll have to create that in a second. Second is the context.
1138
00:59:13,117 --> 00:59:15,918
So we have to go what context? What database are we looking
1139
00:59:15,920 --> 00:59:19,888
in? Next is this section named key path. Okay. N.S.
1140
00:59:19,890 --> 00:59:23,058
Spec Controller results will do the section titles,
1141
00:59:23,060 --> 00:59:26,962
if you have an attribute in the entities that here in
1142
00:59:26,964 --> 00:59:30,666
the table that you're looking up here which is the section.
1143
00:59:30,668 --> 00:59:33,135
Okay? The string the represents the section.
1144
00:59:33,137 --> 00:59:35,304
Can I call that actually with anything you want. And
1145
00:59:35,306 --> 00:59:38,941
you put the name of it here, whatever name the section
1146
00:59:38,943 --> 00:59:41,376
attributes here. The only thing about this, and
1147
00:59:41,378 --> 00:59:43,278
you can put Neil if you don't section, if we don't.
1148
00:59:43,280 --> 00:59:46,348
The only thing about this is that your table must sort
1149
00:59:46,350 --> 00:59:50,085
in the same order as these sections would sort.
1150
00:59:50,087 --> 00:59:50,485
When you think about it,
1151
00:59:50,487 --> 00:59:53,121
if you're going to have them grouped by section
1152
00:59:53,123 --> 00:59:54,623
the things need to be in the same order.
1153
00:59:54,625 --> 00:59:57,225
Otherwise you'd have some sec-, things in section here,
1154
00:59:57,227 --> 00:59:58,961
some in a different section, another different section.
1155
00:59:58,963 --> 01:00:01,597
You can't have that, they have to sort in the same order.
1156
01:00:01,599 --> 01:00:03,532
Okay, so sometimes you'll have you're sort descriptors of
1157
01:00:03,534 --> 01:00:06,668
your effect request. Maybe sort by the section first and
1158
01:00:06,670 --> 01:00:08,804
then sort by whatever you want to do.
1159
01:00:08,806 --> 01:00:11,139
That's an easy way to make it work. And
1160
01:00:11,141 --> 01:00:14,743
then there's the cash which we're also not using. Okay?
1161
01:00:14,745 --> 01:00:18,013
Not just a name. It's a string that it represents it.
1162
01:00:18,015 --> 01:00:19,748
So, I'm even gonna put this so
1163
01:00:19,750 --> 01:00:24,786
you can really see this four arguments right here. Okay.
1164
01:00:24,788 --> 01:00:29,257
So, we need to do a request right here and
1165
01:00:29,259 --> 01:00:29,858
we need to do the context.
1166
01:00:29,860 --> 01:00:32,160
Well, the context is easy, that's part of our motto.
1167
01:00:32,162 --> 01:00:35,530
So I'm just going to say if I can let the context equal
1168
01:00:35,532 --> 01:00:39,835
my own managed object context, K? Then this
1169
01:00:39,837 --> 01:00:43,271
will get to go here. Otherwise I'm gonna set my fetch results
1170
01:00:43,273 --> 01:00:46,308
controller to nil. If you set your fetch results controller
1171
01:00:46,310 --> 01:00:49,011
to nil, core data table view control will clear your
1172
01:00:49,013 --> 01:00:52,381
table okay. Which is what I want if I don't have a manage
1173
01:00:52,383 --> 01:00:55,484
object context because I don't know what database to look in.
1174
01:00:55,486 --> 01:00:58,220
Okay. So now what we need to do is request, okay?
1175
01:00:58,222 --> 01:01:03,025
We just need to have a request which is an NSFetchRequest.
1176
01:01:03,293 --> 01:01:04,126
Okay. It's going to be what?
1177
01:01:04,128 --> 01:01:08,930
What entity is going to be in this table? What is this NBC
1178
01:01:08,932 --> 01:01:14,136
show What kind of things? Twitter users, right? Showing
1179
01:01:14,138 --> 01:01:16,505
all the Twitter users that are mentioned in this tweet.
1180
01:01:16,507 --> 01:01:22,277
So this is twitter user, okay. Now, how about the predicates,
1181
01:01:22,279 --> 01:01:26,381
okay. We need a predicate of this for this thing. And so
1182
01:01:26,383 --> 01:01:30,152
that's in this predicate. Predicate with format here.
1183
01:01:30,154 --> 01:01:32,821
Okay so what is gonna be our predicate for this?
1184
01:01:32,823 --> 01:01:35,757
Well this is a little kind of complicated actually if
1185
01:01:35,759 --> 01:01:38,860
you think about it. We really wanna get all the Twitter
1186
01:01:38,862 --> 01:01:42,497
users who have a tweet that contains that string.
1187
01:01:42,499 --> 01:01:46,134
And it turns out you can do that very straightforwardly
1188
01:01:46,136 --> 01:01:48,570
within this predicate, you're just gonna say any
1189
01:01:48,572 --> 01:01:53,942
Tweets whose text contains, case insensitively, please,
1190
01:01:53,944 --> 01:01:58,346
that mention, okay. Now I have to do %@ whenever
1191
01:01:58,348 --> 01:02:01,616
anything that I'm doing here is not a reserved keyword. So
1192
01:02:01,618 --> 01:02:05,454
I have to do %@ and then put it here in this VarArgs.
1193
01:02:05,456 --> 01:02:10,792
So this is our mention right there Now that's an optional.
1194
01:02:10,794 --> 01:02:13,028
So I need to exclamation point here.
1195
01:02:13,030 --> 01:02:16,698
But I need to be careful here. What if this mention is nil?
1196
01:02:16,700 --> 01:02:19,868
This is gonna crash. And mention is part of my public
1197
01:02:19,870 --> 01:02:23,205
API. You should not have objects where people can set
1198
01:02:23,207 --> 01:02:27,442
your public API to something that will crash internally.
1199
01:02:27,444 --> 01:02:30,979
Okay, that's a bad API design, so
1200
01:02:30,981 --> 01:02:35,951
I wanna check right here, by saying where, this mention and
1201
01:02:35,953 --> 01:02:40,989
actually I also don't want to look up if the mention is
1202
01:02:42,025 --> 01:02:45,327
empty string. That doesn't make any sense either.
1203
01:02:45,329 --> 01:02:48,864
So I'm gonna say mention?.characters.count > 0.
1204
01:02:48,866 --> 01:02:52,467
So I'm using where here with this if, and this is kind of
1205
01:02:52,469 --> 01:02:55,303
interesting look mention characters count. If mention
1206
01:02:55,305 --> 01:02:58,540
is nil this whole thing is gonna be nil and the greater
1207
01:02:58,542 --> 01:03:03,879
sign operator knows that nil is never greater than zero.
1208
01:03:04,181 --> 01:03:06,414
It also knows nil is never less than zero nor
1209
01:03:06,416 --> 01:03:09,985
if nil equal to zero okay so it knows all those things.
1210
01:03:09,987 --> 01:03:11,920
So it's kind of fun you can put nils.
1211
01:03:11,922 --> 01:03:13,054
On the sides of these greater than's or
1212
01:03:13,056 --> 01:03:18,093
less than's really kinda makes your code pretty nicely here.
1213
01:03:18,095 --> 01:03:21,263
Okay? Now the thing we need to do is sort. Okay so we
1214
01:03:21,265 --> 01:03:24,633
need our sort descriptors, and remember that's an array of
1215
01:03:24,635 --> 01:03:27,402
sort descriptors because we can sort by last name first,
1216
01:03:27,404 --> 01:03:29,404
and then first name or by section name and
1217
01:03:29,406 --> 01:03:30,539
then by something else. But
1218
01:03:30,541 --> 01:03:33,842
here we only have one sort descriptor okay.
1219
01:03:33,844 --> 01:03:38,647
The sort descriptor has numerous things that you can
1220
01:03:38,649 --> 01:03:43,552
specify in its initialize. What if you used the simplest
1221
01:03:43,554 --> 01:03:46,388
one here which is I'm going to search by the key screen
1222
01:03:46,390 --> 01:03:51,059
sort by the key screen name. So we're gonna list our users
1223
01:03:51,061 --> 01:03:53,562
by their screen name, you know their at sign, whatever.
1224
01:03:53,564 --> 01:03:56,798
That's gonna be the order of this table and
1225
01:03:56,800 --> 01:03:59,568
I'm going to have it be ascending true.
1226
01:03:59,570 --> 01:04:03,271
So A capital A will be first always to capital Z
1227
01:04:03,273 --> 01:04:05,774
then all the way to a all the way down from there. Okay?
1228
01:04:05,776 --> 01:04:10,245
So that's the only thing I'm gonna search by, so this one,
1229
01:04:10,247 --> 01:04:12,714
or sort by this one sort to script Okay?
1230
01:04:12,716 --> 01:04:16,651
So now, that's it. The only thing I have to do now is
1231
01:04:16,653 --> 01:04:20,789
implement my self row at index path and I'm good to go. Okay?
1232
01:04:20,791 --> 01:04:24,025
Because this is going to always be keeping the Twitter
1233
01:04:24,027 --> 01:04:27,462
users associated with the rows. All right so self for
1234
01:04:27,464 --> 01:04:29,364
row index path of course we need to set
1235
01:04:29,366 --> 01:04:33,702
our reuse identifier which I think was Twitter user cell.
1236
01:04:33,704 --> 01:04:35,837
All right. And it says configure the cell.
1237
01:04:35,839 --> 01:04:38,740
Now, obviously, to configure the cell in any row,
1238
01:04:38,742 --> 01:04:42,878
I need to know what Twitter user is in that row, okay. So,
1239
01:04:42,880 --> 01:04:47,849
how do I do that, let twitterUser equal and
1240
01:04:47,851 --> 01:04:51,920
I'm gonna ask my fetched results controller, okay.
1241
01:04:51,922 --> 01:04:54,856
I'm gonna say, what is the object that act this
1242
01:04:54,858 --> 01:04:58,627
index path? Okay, remember this is cellForRowAtIndexPath,
1243
01:04:58,629 --> 01:05:02,397
so indexPath is what, the row we're talking about here. And
1244
01:05:02,399 --> 01:05:03,698
I'm just gonna ask the Twitter, the,
1245
01:05:03,700 --> 01:05:06,902
I'm gonna ask my fetchedResultsController
1246
01:05:06,904 --> 01:05:06,968
what it is.
1247
01:05:06,970 --> 01:05:09,137
And of course, it better be a TwitterUser. So
1248
01:05:09,139 --> 01:05:15,243
I'm gonna put as TwitterUser there and say if let, okay?
1249
01:05:15,245 --> 01:05:18,113
So if I'm able to get to twitterUser here,
1250
01:05:18,115 --> 01:05:23,485
then I can just do for example cell.TextLabel.text
1251
01:05:23,487 --> 01:05:30,091
= the twitterUser's screenName let's say. Okay?
1252
01:05:30,093 --> 01:05:32,727
Now there's a problem with this line of code right here.
1253
01:05:32,729 --> 01:05:37,933
Can anyone guess what this problem is right here? Looks
1254
01:05:37,935 --> 01:05:44,239
perfectly fine, right? This is accessing the database,
1255
01:05:44,241 --> 01:05:47,976
okay? And we know that anytime we access the database,
1256
01:05:47,978 --> 01:05:51,246
we gotta put it inside performBlock, okay? So
1257
01:05:51,248 --> 01:05:57,452
we actually have to put some context here, performBlock.
1258
01:05:57,454 --> 01:06:02,524
Okay, around this code. Now, what context are we gonna
1259
01:06:02,526 --> 01:06:05,994
use here? We could use our manage object context.
1260
01:06:05,996 --> 01:06:07,495
That's true. And that would be fine.
1261
01:06:07,497 --> 01:06:09,331
I'm actually gonna use a different context.
1262
01:06:09,333 --> 01:06:15,704
I'm gonna use the Twitter user's manage object context.
1263
01:06:16,540 --> 01:06:20,208
Every NS managed object, this is a Twitter user. Okay?
1264
01:06:20,210 --> 01:06:22,911
But we also have Tweet. Every NS managed object
1265
01:06:22,913 --> 01:06:26,081
knows the managed object context it's in. And
1266
01:06:26,083 --> 01:06:29,818
you can ask it. That will often keep you from having to
1267
01:06:29,820 --> 01:06:33,288
have var ManagedObjectContext as public var.
1268
01:06:33,290 --> 01:06:36,658
Because you'll have some other var which is a Twitter user or
1269
01:06:36,660 --> 01:06:40,028
a tweet or something like that and when that gets passed to
1270
01:06:40,030 --> 01:06:42,430
you now you know the managed object context.
1271
01:06:42,432 --> 01:06:45,533
Got it? So that makes it so that you don't have to
1272
01:06:45,535 --> 01:06:48,069
pass both of managed object context and a Twitter user or
1273
01:06:48,071 --> 01:06:50,939
something. Cuz if you pass in a Twitter user you're passing
1274
01:06:50,941 --> 01:06:54,542
the context along. Okay. So I'm just gonna use that down
1275
01:06:54,544 --> 01:06:55,944
in here. I'm doing this intentionally so
1276
01:06:55,946 --> 01:06:57,612
that you know that this method exists.
1277
01:06:57,614 --> 01:07:01,383
It's a really cool var on an ms managed object.
1278
01:07:01,385 --> 01:07:05,987
Now this is still no good, okay? What's wrong here?
1279
01:07:05,989 --> 01:07:12,260
What's going on right there? That's accessing the ui.
1280
01:07:12,262 --> 01:07:16,297
That can only happen on the main queue. Okay? This,
1281
01:07:16,299 --> 01:07:20,502
I happen to know, is the main queue, okay? I know, but
1282
01:07:20,504 --> 01:07:23,805
what if I was writing my code to truly be multi-threaded and
1283
01:07:23,807 --> 01:07:28,309
ms managed conduct? I couldn't count on that. Okay? So,
1284
01:07:28,311 --> 01:07:31,212
I can't do that, so instead, I'm gonna create a little var
1285
01:07:31,214 --> 01:07:35,583
here screen name, okay. Which is going to be a string. And
1286
01:07:35,585 --> 01:07:38,920
inside here I'm going to get rid of this and say
1287
01:07:38,922 --> 01:07:44,025
screen name equals that and then out here I'm going to set
1288
01:07:44,027 --> 01:07:47,395
this equal to screen name. Okay. But
1289
01:07:47,397 --> 01:07:50,598
there is still a problem with this. Anyone can guess what
1290
01:07:50,600 --> 01:07:56,871
this problem is? You're a quiet crowd today. This
1291
01:07:56,873 --> 01:08:00,942
is a synchronic. Okay, that screen name is going to happen
1292
01:08:00,944 --> 01:08:06,047
some other time. Certainly not in time for this. Okay, so
1293
01:08:06,049 --> 01:08:10,418
we need to perform block and wait here. Okay.
1294
01:08:10,420 --> 01:08:15,156
We have to wait for this to happen before we can do this.
1295
01:08:16,293 --> 01:08:20,829
Got it? Okay, so that's it, let's go ahead and run and
1296
01:08:20,831 --> 01:08:29,904
see what this looks like. All right,
1297
01:08:29,906 --> 01:08:35,143
let's look for Stanford again. Okay, we got 206 tweets,
1298
01:08:35,145 --> 01:08:36,344
we got this many Twitter users.
1299
01:08:36,346 --> 01:08:38,480
'm going to click this Tweeters right here, and
1300
01:08:38,482 --> 01:08:42,450
sure enough, look! Her's all the people who tweeted hashtag
1301
01:08:42,452 --> 01:08:46,287
Stanford that w've ever seen. Now, this is not
1302
01:08:46,289 --> 01:08:50,024
quite exactly what we want. I's close, okay? Notice
1303
01:08:50,026 --> 01:08:53,795
our alphabetization. You see, all the capital letters first.
1304
01:08:53,797 --> 01:08:56,898
Then [LAUGH] all the lowercase letters. Really,
1305
01:08:56,900 --> 01:09:01,102
I want interspersed, right? I want case insensitive.
1306
01:09:01,104 --> 01:09:03,271
Lowercase. Or, sorry, case insensitive so,
1307
01:09:03,273 --> 01:09:06,641
sorting as well. [SOUND] what else is going on here? Also,
1308
01:09:06,643 --> 01:09:09,911
maybe some of these I don't like. Like, look at this guy.
1309
01:09:09,913 --> 01:09:13,615
Darksidedub16. Okay, that sounds kind of evil, so
1310
01:09:13,617 --> 01:09:17,752
I don't even want Darkside to appear in my results. Okay?
1311
01:09:17,754 --> 01:09:19,120
So, let's fix both of those things,
1312
01:09:19,122 --> 01:09:19,521
let's fix the sorting and
1313
01:09:19,523 --> 01:09:23,158
let's make sure dark side does not appear in our results,
1314
01:09:23,160 --> 01:09:27,328
okay? So, how do we do those things? First, the sorting,
1315
01:09:27,330 --> 01:09:30,165
case intensively is quite easy, okay? Here is where our
1316
01:09:30,167 --> 01:09:32,934
sort descriptor is right here, here is the key and
1317
01:09:32,936 --> 01:09:35,370
here is the ascending, right there. We can just add
1318
01:09:35,372 --> 01:09:38,840
another argument here, which is the selector. To use, okay?
1319
01:09:38,842 --> 01:09:43,244
We'll use the hashtag selector here. Selector.
1320
01:09:43,246 --> 01:09:45,980
And the selector I'm going to use is NF strings,
1321
01:09:45,982 --> 01:09:49,817
I've got one called localizedcaseinsentivecompare
1322
01:09:49,819 --> 01:09:56,191
right here, okay? And so you guys remember this format for
1323
01:09:56,193 --> 01:09:58,326
specifying a selector, right? So
1324
01:09:58,328 --> 01:10:00,328
I'm just gonna use this instead of the standard one,
1325
01:10:00,330 --> 01:10:02,263
which is compare, which is not case insensitive.
1326
01:10:02,265 --> 01:10:05,733
So that's gonna effect our, case insensitive sorting.
1327
01:10:05,735 --> 01:10:08,603
Okay, it's just how we specify our sort descriptor.
1328
01:10:08,605 --> 01:10:11,105
How about getting the dark side out of there? Well,
1329
01:10:11,107 --> 01:10:14,809
what appears in that table is determined by this predicate.
1330
01:10:14,811 --> 01:10:22,717
Okay? Any tweets containing this currently, but I can say,
1331
01:10:22,719 --> 01:10:28,089
and the screen name does not begin with,
1332
01:10:28,091 --> 01:10:32,660
case insensitivity, the darkside.
1333
01:10:32,662 --> 01:10:35,730
Okay, and again I can't put anything but keywords in here,
1334
01:10:35,732 --> 01:10:40,835
so I have to put it out here Okay? Like that.
1335
01:10:40,837 --> 01:10:43,438
I just wanted to do this to show you that you can update
1336
01:10:43,440 --> 01:10:45,974
this predicate to change what shows up in your table,
1337
01:10:45,976 --> 01:10:50,979
this what controls within the table. So, now when we run.
1338
01:10:53,950 --> 01:10:57,452
Let me do #stanford. Okay, and
1339
01:10:57,454 --> 01:11:01,389
now Tweeters, we can see that the sorting is right,
1340
01:11:01,391 --> 01:11:06,694
you see? Capitals and lower case and look, no dark sides.
1341
01:11:06,696 --> 01:11:11,399
Okay, this is only a light side query here. All right.
1342
01:11:11,401 --> 01:11:14,269
Now I have five minutes left, I'll show you one
1343
01:11:14,271 --> 01:11:18,439
last other fun thing. See this subtitle right here? What if
1344
01:11:18,441 --> 01:11:22,377
instead of saying subtitle I wanted to say how many tweets
1345
01:11:22,379 --> 01:11:26,014
this guys has tweeted, right? So we're show,
1346
01:11:26,016 --> 01:11:29,050
these are all the people who have tweeted #stanford,
1347
01:11:29,052 --> 01:11:31,286
you know, had mentioned #stanford in their tweets.
1348
01:11:31,288 --> 01:11:34,389
I wanna know how many, so tell me how many each of these,
1349
01:11:34,391 --> 01:11:36,958
how many tweets this guy does, have done. All right?
1350
01:11:36,960 --> 01:11:39,727
So, to do that, I need another little function, okay?
1351
01:11:39,729 --> 01:11:43,464
It's going to be a private func. I'm going to call it
1352
01:11:43,466 --> 01:11:47,969
tweetCountWithMentionByTwitte- rUser.
1353
01:11:47,971 --> 01:11:51,539
And it's going to take a Twitter user.
1354
01:11:51,541 --> 01:11:54,709
And it's going to return an int.
1355
01:11:54,711 --> 01:11:58,146
Optional with how many times how many tweets, a tweet
1356
01:11:58,148 --> 01:12:01,015
count, where that Twitter user has mentioned that. So it's
1357
01:12:01,017 --> 01:12:03,785
gonna return the number that I want to show there. Okay.
1358
01:12:03,787 --> 01:12:06,454
So what is this little guy gonna look like right here?
1359
01:12:06,456 --> 01:12:08,890
Well I'm gonna have a local variable which is
1360
01:12:08,892 --> 01:12:11,993
the count which I am going to return at the end, K? It's
1361
01:12:11,995 --> 01:12:14,729
optional because maybe I won't be able to find the results,
1362
01:12:14,731 --> 01:12:18,266
we'll see. And I'm going to get the users managed
1363
01:12:18,268 --> 01:12:21,936
object context again and perform black. And
1364
01:12:21,938 --> 01:12:26,574
I'd better perform block and wait, k? Because this
1365
01:12:26,576 --> 01:12:29,177
a method that's being called, it needs to return, it can't,
1366
01:12:29,179 --> 01:12:32,847
it's got to do the work and have the result come back. So,
1367
01:12:32,849 --> 01:12:34,649
I'm just gonna create a fetch request here,
1368
01:12:34,651 --> 01:12:38,353
NSFetchRequest. And this time I'm searching for
1369
01:12:38,355 --> 01:12:41,956
tweets. Okay. Even though I'm inside the infiltration of
1370
01:12:41,958 --> 01:12:44,158
the twitter user table, I'm now counting tweets.
1371
01:12:44,160 --> 01:12:47,295
So, when I'm doing a different fetch inside there for tweets.
1372
01:12:47,297 --> 01:12:49,564
This is not the same fetch that's filling the table.
1373
01:12:49,566 --> 01:12:50,498
This is kind of a little side
1374
01:12:50,500 --> 01:12:54,802
fetch, that I'm doing and the predicate for that is. And
1375
01:12:54,804 --> 01:12:59,207
then there's predicate, format,
1376
01:12:59,209 --> 01:13:03,778
and text. I want the text of the Tweet to contain, case
1377
01:13:03,780 --> 01:13:08,883
insensitively, the mention that we're talking about. And
1378
01:13:08,885 --> 01:13:13,454
I want the Tweeter to equal this user. Okay,
1379
01:13:13,456 --> 01:13:17,191
just passed in here. So I need to have the mentions.
1380
01:13:17,193 --> 01:13:20,061
So that's our mention. Self.mention we'll need so
1381
01:13:20,063 --> 01:13:22,864
.mention. And I'm just gonna go ahead and
1382
01:13:22,866 --> 01:13:25,333
unwrap that. I'm pretty sure this is not new
1383
01:13:25,335 --> 01:13:27,935
because I don't think this method would be called if I
1384
01:13:27,937 --> 01:13:30,271
match it with milk because we never would've got
1385
01:13:30,273 --> 01:13:30,571
into all the self.
1386
01:13:30,573 --> 01:13:33,107
And xpath and all this stuff we never would've gotten here.
1387
01:13:33,109 --> 01:13:34,709
Some would say protect against this. But,
1388
01:13:34,711 --> 01:13:37,145
I actually think it's good to crash here because you'll find
1389
01:13:37,147 --> 01:13:40,181
this bug if you ever call this when mentioned is nil,
1390
01:13:40,183 --> 01:13:41,816
which it never should be at this point. And
1391
01:13:41,818 --> 01:13:45,720
then the Tweeter is just this argument user. So you Tweeter
1392
01:13:45,722 --> 01:13:50,558
equals user and pass a Twitter user, okay? And restrict your
1393
01:13:50,560 --> 01:13:53,327
results to that, so that's our results. So now I'm
1394
01:13:53,329 --> 01:13:56,097
going to use that count thing that we talked about before.
1395
01:13:56,099 --> 01:14:01,536
That's the user's managed object context. Count for
1396
01:14:01,538 --> 01:14:05,273
fetch request. The request. Don't care about errors,it
1397
01:14:05,275 --> 01:14:06,140
will come back zero. That's fine.
1398
01:14:06,142 --> 01:14:10,678
And I'm going to return the count. Okay? Everyone see what
1399
01:14:10,680 --> 01:14:14,215
I did there? Just found the, the request that I wanted.
1400
01:14:14,217 --> 01:14:16,951
So now that I have this nice little guy right here,
1401
01:14:16,953 --> 01:14:21,422
I can go here and say that my cell. I can actually say if
1402
01:14:21,424 --> 01:14:25,927
let the count equal the tweet count mentioned by
1403
01:14:25,929 --> 01:14:29,831
this twitter user. Okay? So if I got an account,
1404
01:14:29,833 --> 01:14:34,535
then I'm gonna have the cells detail TextLabel, text= and
1405
01:14:34,537 --> 01:14:37,371
let's even be kinda be cute and say if the count is one.
1406
01:14:37,373 --> 01:14:42,944
I'm gonna say 1 tweet otherwise I'm gonna say count
1407
01:14:42,946 --> 01:14:47,548
tweets, plural. If I wasn't able to get it then I'm just
1408
01:14:47,550 --> 01:14:52,453
gonna say that the cell detail text label text to empty,
1409
01:14:53,289 --> 01:15:03,097
okay. Let's run that. All right. Hashtag stanford again.
1410
01:15:03,099 --> 01:15:07,268
Seems to be our favorite. Here it is right here.
1411
01:15:07,270 --> 01:15:12,073
Okay tweeters and there it is. Okay, a lot of people with one
1412
01:15:12,075 --> 01:15:14,041
tweet, but here's someone with three tweets.
1413
01:15:14,043 --> 01:15:16,244
Okay there's someone with four tweets right there.
1414
01:15:16,246 --> 01:15:17,311
Now there were four tweets.
1415
01:15:17,313 --> 01:15:19,780
Now, the next obvious thing is to say,
1416
01:15:19,782 --> 01:15:23,618
awesome. I want to sort this table by the people's numbers
1417
01:15:23,620 --> 01:15:26,254
of tweets, so the people who tweet a lot are at the top.
1418
01:15:26,256 --> 01:15:29,724
Wouldn't that be a lot better than alphabetical?
1419
01:15:29,726 --> 01:15:30,791
It sure would. But
1420
01:15:30,793 --> 01:15:35,563
to do that, we would need a different schema. Okay.
1421
01:15:35,565 --> 01:15:37,632
This is important because in your homework you're going to
1422
01:15:37,634 --> 01:15:39,967
be sorting by a numb numerical value right here. And
1423
01:15:39,969 --> 01:15:42,703
you gotta make sure you have a schema that supports it.
1424
01:15:42,705 --> 01:15:44,839
Why would we need a different schema to sort
1425
01:15:44,841 --> 01:15:48,509
to sort this by the number of tweets? It's because of this.
1426
01:15:48,511 --> 01:15:50,044
Okay, if we go back up here and
1427
01:15:50,046 --> 01:15:55,383
look at our sort descriptor. Okay. For this table.
1428
01:15:55,385 --> 01:16:00,221
It's sorting by the screen name. This always has to be
1429
01:16:00,223 --> 01:16:05,226
an attribute in this entity. Okay,
1430
01:16:05,228 --> 01:16:06,994
the sort descriptor cannot be anything but
1431
01:16:06,996 --> 01:16:09,363
an attribute in this entity. Well, Twitter user
1432
01:16:09,365 --> 01:16:12,567
does not have an attribute which is that number. So
1433
01:16:12,569 --> 01:16:16,504
I cannot sort by it. You see? And not only that.
1434
01:16:16,506 --> 01:16:19,106
It would be very difficult for me to keep a number
1435
01:16:19,108 --> 01:16:22,476
on my Twitter user that would be that. Because
1436
01:16:22,478 --> 01:16:25,413
it depends on the mention. I'd have to have a different
1437
01:16:25,415 --> 01:16:26,447
Twitter user for every mention.
1438
01:16:26,449 --> 01:16:31,152
So this schema that we've designed right here,
1439
01:16:31,154 --> 01:16:35,690
which I will show you this schema, is insufficient.
1440
01:16:35,692 --> 01:16:38,626
To have that table sorted by a number, you need
1441
01:16:38,628 --> 01:16:41,929
a different schema. A slightly more powerful schema and
1442
01:16:41,931 --> 01:16:44,899
really the hardest thing about semi five for you is probably
1443
01:16:44,901 --> 01:16:47,234
design that schema. Once you design that schema,
1444
01:16:47,236 --> 01:16:49,837
look how incredibly it is easy to load these tables over
1445
01:16:49,839 --> 01:16:53,074
that coordinated table it's just like, it's trivial. Okay?
1446
01:16:53,076 --> 01:16:56,410
So, make sure you spend your brainpower on assignment five,
1447
01:16:56,412 --> 01:16:57,011
getting a good schema,
1448
01:16:57,013 --> 01:16:58,412
understanding what you're being asked to do,
1449
01:16:58,414 --> 01:17:03,150
and then getting a good schema that supports that, okay?
1450
01:17:03,152 --> 01:17:05,620
You'll, you'll find the rest of assignment five is very,
1451
01:17:05,622 --> 01:17:10,324
is straightforward, okay? All right, that's it.
1452
01:17:10,326 --> 01:17:13,461
I will see you on Wednesday we'll talk about the final
1453
01:17:13,463 --> 01:17:16,197
project requirements among other things.
1454
01:17:17,667 --> 01:17:17,932
>> For more,
1455
01:17:17,934 --> 01:17:17,965
please visit us at stanford.edu.
================================================
FILE: subtitles/12. AutoLayout.srt
================================================
1
00:00:00,001 --> 00:00:03,702
[MUSIC]
2
00:00:03,704 --> 00:00:07,973
Stanford University. >> Welcome to
3
00:00:07,975 --> 00:00:12,945
Lecture 12 of Stanford CS193P, spring of 2016. Today
4
00:00:12,947 --> 00:00:15,647
we have Autolayout, which we've actually been doing
5
00:00:15,649 --> 00:00:18,550
quite a bit of Autolayout, so far, but we're gonna talk
6
00:00:18,552 --> 00:00:21,353
a lot more in detail about the concepts of Autolayout and
7
00:00:21,355 --> 00:00:25,491
things like that. And we're gonna introduce an important
8
00:00:25,493 --> 00:00:28,460
Autolayout feature called Size Classes, okay?
9
00:00:28,462 --> 00:00:30,129
All right, so let's dive in to Autolayout.
10
00:00:30,131 --> 00:00:32,798
So you already know a lot about Autolayout, right? For
11
00:00:32,800 --> 00:00:36,235
example, you know you can use those dashed blue lines when
12
00:00:36,237 --> 00:00:38,871
you drag things in and move things around to kinda tell
13
00:00:38,873 --> 00:00:42,441
Xcode what you intend about how things are laid out, okay?
14
00:00:42,443 --> 00:00:45,444
But you know that those blue lines are really only just
15
00:00:45,446 --> 00:00:48,247
kind of a hint, right? But you can say reset to
16
00:00:48,249 --> 00:00:51,016
suggest a constraints. And it'll use the dashed blue line
17
00:00:51,018 --> 00:00:54,753
information to try and guess, what constraints you want.
18
00:00:54,755 --> 00:00:58,590
And sometimes that's what you want, sometimes not. Really
19
00:00:58,592 --> 00:01:03,228
that's just minimal help from the system to do Autolayout.
20
00:01:03,230 --> 00:01:04,663
It's kinda maybe to get you started,
21
00:01:04,665 --> 00:01:06,865
or to get some initial constraints or
22
00:01:06,867 --> 00:01:08,200
something like that, okay? And
23
00:01:08,202 --> 00:01:11,136
you know that you can look in the Size Inspector, okay?
24
00:01:11,138 --> 00:01:12,304
You can click, click on any view and
25
00:01:12,306 --> 00:01:13,505
look in the Size Inspector, and now,
26
00:01:13,507 --> 00:01:16,075
now, down there you'll see all of the constraints.
27
00:01:16,077 --> 00:01:20,612
We've looked at those before that are on a particular view.
28
00:01:20,614 --> 00:01:24,716
Okay, you also know that you can click on a constraint
29
00:01:24,718 --> 00:01:27,619
itself and look at it in the Attributes Inspector.
30
00:01:27,621 --> 00:01:29,988
So just like we click on a view and inspect that,
31
00:01:29,990 --> 00:01:31,757
you can click on any constraint,
32
00:01:31,759 --> 00:01:33,692
like a little i-beam or, you know, some bar,
33
00:01:33,694 --> 00:01:36,628
something that's connecting something to an edge or
34
00:01:36,630 --> 00:01:38,630
something. You can click on those things.
35
00:01:38,632 --> 00:01:40,099
You can even double-click on them and
36
00:01:40,101 --> 00:01:41,733
edit them kind of in a little window.
37
00:01:41,735 --> 00:01:42,768
But you can single-click on them and
38
00:01:42,770 --> 00:01:46,271
then edit them in the Attributes Inspector as well.
39
00:01:47,108 --> 00:01:50,509
Now we know that the other way, another way we can set,
40
00:01:50,511 --> 00:01:53,212
constraints is to control drag like we control drag to
41
00:01:53,214 --> 00:01:56,515
the edge and say, you know, make it be a certain distance
42
00:01:56,517 --> 00:01:58,750
from this edge. Okay, we saw that quite a bit.
43
00:01:58,752 --> 00:02:01,720
That Ctrl-dragging can also be done between views.
44
00:02:01,722 --> 00:02:04,123
So you can Ctrl-drag between two views and then say,
45
00:02:04,125 --> 00:02:08,227
make these two be the same with, okay? Things like that.
46
00:02:08,229 --> 00:02:10,529
And we'll see a little bit of that today in the demo. So
47
00:02:10,531 --> 00:02:14,299
that Ctrl-dragging is probably the number one way you do
48
00:02:14,301 --> 00:02:18,036
Autolayout, okay? But there's also a couple of menus down in
49
00:02:18,038 --> 00:02:21,106
the bottom right corner where that menu that you bring up
50
00:02:21,108 --> 00:02:25,110
that says reset to suggested constraints or update frames.
51
00:02:25,112 --> 00:02:25,244
You know that thing,
52
00:02:25,246 --> 00:02:27,846
there's two other little menus right by it. Okay the pin and
53
00:02:27,848 --> 00:02:31,717
arrange menus, and they also let you say things like
54
00:02:31,719 --> 00:02:34,720
make these. You can select five views, and then bring up
55
00:02:34,722 --> 00:02:37,990
the arrange menu and say, make all of these equal widths.
56
00:02:37,992 --> 00:02:39,992
And now we have a constraint on all five of the views to
57
00:02:39,994 --> 00:02:44,830
make them equal widths. So, those menus, frankly
58
00:02:44,832 --> 00:02:47,833
you don't use them that much because the Ctrl-drag is so
59
00:02:47,835 --> 00:02:49,968
incredibly convenient for doing that, but
60
00:02:49,970 --> 00:02:56,241
sometimes we will. You can look at your constraints by
61
00:02:56,243 --> 00:02:59,378
inspecting things, but really the way to see a big picture
62
00:02:59,380 --> 00:03:02,648
of all of the constraints is in the document outline, okay?
63
00:03:02,650 --> 00:03:04,950
Remember the document outline? That's the thing on the left
64
00:03:04,952 --> 00:03:06,251
in the storyboard that you can kinda
65
00:03:06,253 --> 00:03:07,953
bring out with the little button on the bottom and
66
00:03:07,955 --> 00:03:11,123
it shows you all your views and the hierarchy of it and
67
00:03:11,125 --> 00:03:11,156
all that stuff.
68
00:03:11,158 --> 00:03:14,059
Well it'll also show all your constraints in there. Okay,
69
00:03:14,061 --> 00:03:15,994
a big long list of your constraints.
70
00:03:15,996 --> 00:03:19,331
And, that's a good place where you can go to see them.
71
00:03:19,333 --> 00:03:22,734
It also has a constraint resolution
72
00:03:22,736 --> 00:03:25,337
UI which I might have brief, briefly shown but
73
00:03:25,339 --> 00:03:28,340
I'll certainly show today. Where it'll actually tell you
74
00:03:28,342 --> 00:03:30,642
all the constraints that are conflicting or
75
00:03:30,644 --> 00:03:33,378
not fully specifying things, that kind of stuff.
76
00:03:33,380 --> 00:03:36,148
So it's kind of, Document Outline is your constraint
77
00:03:36,150 --> 00:03:39,484
headquarters to go try and fix things with constraints and
78
00:03:39,486 --> 00:03:42,454
understand what's going on with your constraints. Now
79
00:03:42,456 --> 00:03:45,624
mastering Autolayout requires experience. I can tell you all
80
00:03:45,626 --> 00:03:49,194
these things what the, what the things you can do are, but
81
00:03:49,196 --> 00:03:52,231
until you get in there and start trying to lay things out
82
00:03:52,233 --> 00:03:55,801
and make things line up, and do things like that,
83
00:03:55,803 --> 00:03:57,536
you really won't be able to master it, okay?
84
00:03:57,538 --> 00:03:59,571
So it requires experience. So hopefully in your final
85
00:03:59,573 --> 00:04:02,241
project you'll have some UIs that require quite a bit of
86
00:04:02,243 --> 00:04:04,977
Autolayout. You know the UIs we've had so far have not
87
00:04:04,979 --> 00:04:07,079
required that much Autolayout because I haven't gotten to
88
00:04:07,081 --> 00:04:09,781
this lecture yet. So you know we've been using StatView
89
00:04:09,783 --> 00:04:12,317
a lot, okay? We're heavily relying on StatView to make it
90
00:04:12,319 --> 00:04:15,187
so we don't have to do so much Autolayout, cuz StatView kind
91
00:04:15,189 --> 00:04:18,457
of is a thing for doing Autolayout in a very simple
92
00:04:18,459 --> 00:04:22,361
way when you want to line things up in grids, okay? So
93
00:04:22,363 --> 00:04:26,498
there's no, no substitute for experience in Autolayout. It
94
00:04:26,500 --> 00:04:29,868
is possible to do Autolayout constraints from code, okay?
95
00:04:29,870 --> 00:04:33,272
There is a class called ns layout constraint. And
96
00:04:33,274 --> 00:04:36,041
it does all the things you can do in the storyboard and
97
00:04:36,043 --> 00:04:38,277
possibly even a little bit more.
98
00:04:38,279 --> 00:04:40,078
Actually definitely a little bit more.
99
00:04:40,080 --> 00:04:41,913
I'm not gonna teach that in this course.
100
00:04:41,915 --> 00:04:44,549
If you wanna learn about it, you might think about trying
101
00:04:44,551 --> 00:04:48,987
to do something in your final project, maybe. It requires
102
00:04:48,989 --> 00:04:51,923
a certain understanding of mathematics and
103
00:04:51,925 --> 00:04:54,259
really understanding relationships between things,
104
00:04:54,261 --> 00:04:56,962
to really set things up and make things work. It's a heck
105
00:04:56,964 --> 00:04:59,631
of a lot easier just to do it all in the story board. So
106
00:04:59,633 --> 00:05:02,067
unless you wanna kinda make that one of your investigative
107
00:05:02,069 --> 00:05:04,303
things for your final project, which you're gonna see
108
00:05:04,305 --> 00:05:06,104
is required that you go investigate something that I
109
00:05:06,106 --> 00:05:09,541
don't teach in lecture. You're probably gonna wanna try and
110
00:05:09,543 --> 00:05:12,711
do Autolayout from the story board, okay. And
111
00:05:12,713 --> 00:05:15,814
you can certainly do everything you need to do
112
00:05:15,816 --> 00:05:19,251
almost all the time in your story board. Okay,
113
00:05:19,253 --> 00:05:24,656
so, let's see what's next here. Let's talk about,
114
00:05:24,658 --> 00:05:30,195
rotation, okay? So sometimes, when you rotate your UI,
115
00:05:30,197 --> 00:05:33,098
the geometry of it changes so drastically that
116
00:05:33,100 --> 00:05:36,668
Autolayout is not enough to reposition everything, okay?
117
00:05:36,670 --> 00:05:39,738
A great example of this is our calculator, okay? Our
118
00:05:39,740 --> 00:05:43,775
calculator had five across, five buttons across and
119
00:05:43,777 --> 00:05:47,913
four buttons down, I think, okay? And that looked fine.
120
00:05:47,915 --> 00:05:49,948
Or it was four across and five down, I guess.
121
00:05:49,950 --> 00:05:50,982
And that looks fine in portrait.
122
00:05:50,984 --> 00:05:53,552
All the buttons were almost square. They were rectangular,
123
00:05:53,554 --> 00:05:55,420
but they were close to being square. But
124
00:05:55,422 --> 00:05:56,121
when we went into landscape,
125
00:05:56,123 --> 00:05:58,390
all of our buttons got really squished down, okay.
126
00:05:58,392 --> 00:06:01,393
They're really wide and really short. Okay, and really, it
127
00:06:01,395 --> 00:06:04,363
would have been a lot better if we had gone five across and
128
00:06:04,365 --> 00:06:06,832
four down. Because we'd have more space
129
00:06:06,834 --> 00:06:09,735
across than we do have down in landscape. Okay, well there's
130
00:06:09,737 --> 00:06:13,238
no way to do Autolayout. No way so that that would happen.
131
00:06:13,240 --> 00:06:16,675
Okay, there's no way to put any relationships between them
132
00:06:16,677 --> 00:06:18,210
so that they would just somehow flip over.
133
00:06:18,212 --> 00:06:20,979
Some of the buttons would flip over from being along the top,
134
00:06:20,981 --> 00:06:23,448
for example, to being along the left, okay? So you can't
135
00:06:23,450 --> 00:06:26,051
do it that way. So what would you do if you wanted to build
136
00:06:26,053 --> 00:06:29,588
a calculator that repositioned the buttons when you flipped
137
00:06:29,590 --> 00:06:32,424
it over? Because it's not just the calculate,
138
00:06:32,426 --> 00:06:35,327
the calculator that might want to do this. For
139
00:06:35,329 --> 00:06:39,297
you example you know when you're in the master of
140
00:06:39,299 --> 00:06:43,101
a split view okay it's not you're not in portrait.
141
00:06:43,103 --> 00:06:44,236
You're probably on an iPad or
142
00:06:44,238 --> 00:06:47,973
at least you might be on an iPhone 6+. In that master,
143
00:06:47,975 --> 00:06:51,309
you also want it to be the right arrangement of buttons.
144
00:06:51,311 --> 00:06:54,513
Okay, more like iPhone portrait, okay, than iPad.
145
00:06:54,515 --> 00:06:58,784
Okay, so the solution to doing this is called size classes.
146
00:06:58,786 --> 00:07:00,685
Okay, so your view controllers,
147
00:07:00,687 --> 00:07:02,020
any view controller that you're doing,
148
00:07:02,022 --> 00:07:02,988
like the calculator view controller,
149
00:07:02,990 --> 00:07:06,691
always lives in a size class environment, okay, for
150
00:07:06,693 --> 00:07:10,896
its width and its height. Okay, and that size class is
151
00:07:10,898 --> 00:07:14,466
either compact or regular. Regular means
152
00:07:14,468 --> 00:07:18,270
it's not compact, okay? So your view controller for each,
153
00:07:18,272 --> 00:07:21,206
for horizontally and vertically, will get something
154
00:07:21,208 --> 00:07:25,177
told to it, okay you are in a compact vertical environment,
155
00:07:25,179 --> 00:07:28,680
or you are in a compact horizontal environment. And
156
00:07:28,682 --> 00:07:31,983
you can react to that, okay? So in the calculator's case,
157
00:07:31,985 --> 00:07:34,352
if we're in a compact vertical environment,
158
00:07:34,354 --> 00:07:38,256
let's put five across and four down, because we're squi,
159
00:07:38,258 --> 00:07:40,192
you know, squished down vertically so
160
00:07:40,194 --> 00:07:43,228
we'll spread them out a little bit. And if we're not,
161
00:07:43,230 --> 00:07:46,498
then we'll go ahead and use the vertical space and make it
162
00:07:46,500 --> 00:07:50,001
be four across and five down, okay? So getting all this,
163
00:07:50,003 --> 00:07:53,405
kind of all the different kinds of aspect ratios, and
164
00:07:53,407 --> 00:07:56,942
things for all the devices to fit into this compact in
165
00:07:56,944 --> 00:07:59,277
regular. You know it's not a perfect fit, but
166
00:07:59,279 --> 00:08:02,380
here's a kind of general idea of how these fit, all right?
167
00:08:02,382 --> 00:08:06,651
So the iPhone 6 Plus in Portrait, right when it's,
168
00:08:06,653 --> 00:08:09,855
it's stand, standing upright basically.
169
00:08:09,857 --> 00:08:14,025
That is compact width, regular height. Makes perfect sense,
170
00:08:14,027 --> 00:08:15,894
right? Okay, compact width, regular height.
171
00:08:15,896 --> 00:08:18,697
When you go to Landscape, now it's compact height,
172
00:08:18,699 --> 00:08:22,801
regular width. Okay. So iPhone 6 Plus makes the most sense of
173
00:08:22,803 --> 00:08:28,240
all of the devices. Okay? Now iPhone non-plus,
174
00:08:28,242 --> 00:08:32,944
okay, is compact, in width.
175
00:08:32,946 --> 00:08:35,413
Sorry, in Portrait, it's compact in width and
176
00:08:35,415 --> 00:08:37,649
regular in height. But in Landscaped, okay,
177
00:08:37,651 --> 00:08:39,885
non 6 Plus now I'm talking about here, it's
178
00:08:39,887 --> 00:08:44,422
compact in both directions. So on a normal iPhone, non plus
179
00:08:44,424 --> 00:08:47,726
iPhone, in width, you would think it would be regular
180
00:08:47,728 --> 00:08:50,495
cuz it was regular when it was in Portrait in height, but no,
181
00:08:50,497 --> 00:08:53,431
it's compact. So you can actually tell the difference
182
00:08:53,433 --> 00:08:57,736
between whether you're on a 6 Plus or a regular iPhone 4 or
183
00:08:57,738 --> 00:09:00,705
S or 5 or 6 or whatever, non Plus because you'll
184
00:09:00,707 --> 00:09:03,241
be compact in both directions in Landscape. Okay?
185
00:09:03,243 --> 00:09:06,011
So it's just kinda weird gotta be used to that.
186
00:09:06,013 --> 00:09:10,015
iPad is always regular in both directions. Cuz it£s so big,
187
00:09:10,017 --> 00:09:13,351
it£s not compact in any way, in any direction. However,
188
00:09:13,353 --> 00:09:16,821
if you£re an MVC that is in the master of a split view,
189
00:09:16,823 --> 00:09:21,226
you£ll be compact in width. Okay, understandably right cuz
190
00:09:21,228 --> 00:09:27,265
you're kinda, jammed in there. This whole system,
191
00:09:27,267 --> 00:09:31,136
by the way, is extensible. It's not just rock solid,
192
00:09:31,138 --> 00:09:34,372
you know, hooked up to whether you're on a 6 Plus or
193
00:09:34,374 --> 00:09:36,474
an iPhone or in a split view master or
194
00:09:36,476 --> 00:09:38,476
a whatever. It has a whole mechanism for
195
00:09:38,478 --> 00:09:42,781
an MVC to find out what size class it's in. It can even say
196
00:09:42,783 --> 00:09:45,650
based on its environment I wanna be in this size class.
197
00:09:45,652 --> 00:09:48,687
And then if it has MVCs inside of it like it's a navigation
198
00:09:48,689 --> 00:09:49,354
controller or something like that.
199
00:09:49,356 --> 00:09:52,724
Then they would inherit that size class information, okay.
200
00:09:52,726 --> 00:09:56,328
Now I'm not gonna talk about that all the code side of it.
201
00:09:56,330 --> 00:09:59,097
The only code you're probably gonna be interested
202
00:09:59,099 --> 00:10:01,232
in is this method here in view controller,
203
00:10:01,234 --> 00:10:04,736
Self traitCollection. Horizontal size class and
204
00:10:04,738 --> 00:10:05,503
vertical size class and
205
00:10:05,505 --> 00:10:07,672
it's gonna give you back compact irregular.
206
00:10:07,674 --> 00:10:09,641
Okay? So that's how you'll know if you're compact
207
00:10:09,643 --> 00:10:12,844
irregular. Now, that's only if you wanted to react
208
00:10:12,846 --> 00:10:15,947
to your compactness in code. A lot of times we're gonna
209
00:10:15,949 --> 00:10:19,217
be doing this in our story board. Okay? And I'm gonna
210
00:10:19,219 --> 00:10:21,686
show you how in the story board we'll react to that. So
211
00:10:21,688 --> 00:10:24,589
in summary it looks like this, okay? You got compact width
212
00:10:24,591 --> 00:10:26,491
and regular width. Compact height and regular height.
213
00:10:26,493 --> 00:10:30,462
So, iPhones (non-Plus) in Landscape are here. Compact,
214
00:10:30,464 --> 00:10:35,567
compact. Regular width compact is iPhone 6 Plus in Landscape.
215
00:10:35,569 --> 00:10:36,234
Regular height and
216
00:10:36,236 --> 00:10:38,470
compact width is iPhones in Portrait or
217
00:10:38,472 --> 00:10:40,772
you're the Master of the Split View. And
218
00:10:40,774 --> 00:10:44,009
then iPads are regular in both. Okay. So,
219
00:10:44,011 --> 00:10:45,443
if you want to look at the calculator, for example,
220
00:10:45,445 --> 00:10:49,180
it would look something like this, all right. Now here is,
221
00:10:49,182 --> 00:10:53,718
I'm drawing my UI here as if I've adapted to this. In
222
00:10:53,720 --> 00:10:57,255
in this case right here, I've decided to go four across and
223
00:10:57,257 --> 00:11:00,191
five down because I gotta lot of down, a lot of vertical
224
00:11:00,193 --> 00:11:02,761
space. Whereas here I've gone five across and
225
00:11:02,763 --> 00:11:04,663
four down cuz I'm kinda compact in height,
226
00:11:04,665 --> 00:11:07,699
you see. Even here I've done the same thing.
227
00:11:07,701 --> 00:11:09,200
I've decided here, compact and
228
00:11:09,202 --> 00:11:11,970
regular width to go five across and four down. And
229
00:11:11,972 --> 00:11:14,539
down here, in regular both directions I decided it
230
00:11:14,541 --> 00:11:19,244
looks a little better to have the four across and five down.
231
00:11:19,246 --> 00:11:24,015
Okay? Now you would imagine if I wanted my calculator to work
232
00:11:24,017 --> 00:11:27,318
this way I might have to build fi, four different UIs, one,
233
00:11:27,320 --> 00:11:30,321
two, three, four. For all four of these. Okay, and
234
00:11:30,323 --> 00:11:32,657
that would normally be the way it is.
235
00:11:32,659 --> 00:11:35,760
Except for that, we actually do size classes with a little
236
00:11:35,762 --> 00:11:38,797
more subtlety than that. Because if you look here,
237
00:11:38,799 --> 00:11:41,866
compact height always is five across and
238
00:11:41,868 --> 00:11:43,968
four down. Whether it's compact width or
239
00:11:43,970 --> 00:11:45,303
regular width, doesn't matter. And
240
00:11:45,305 --> 00:11:48,573
here, in regular height, it's always four across,
241
00:11:48,575 --> 00:11:50,542
five down, no matter what the width. So
242
00:11:50,544 --> 00:11:52,343
really I should only have to do two things here,
243
00:11:52,345 --> 00:11:55,413
okay, based on the height. I shouldn't have to do anything
244
00:11:55,415 --> 00:11:59,851
different based on the width. And that's why the whole size
245
00:11:59,853 --> 00:12:03,388
class thing is actually treated as a grid like this.
246
00:12:03,390 --> 00:12:05,790
With nine things in it and in the middle you
247
00:12:05,792 --> 00:12:09,894
have this special width and height called any. Okay. So
248
00:12:09,896 --> 00:12:11,996
now if I want what I had on the previous screen,
249
00:12:11,998 --> 00:12:15,900
all I have to do is two UIs. One for regular height,
250
00:12:15,902 --> 00:12:19,571
any width, and one for compact height, any width. And
251
00:12:19,573 --> 00:12:23,842
even better than that, I can actually put some of my UI
252
00:12:23,844 --> 00:12:27,312
here in the middle, any height, any width. And
253
00:12:27,314 --> 00:12:31,483
only put the differences out here in these other two. So
254
00:12:31,485 --> 00:12:34,352
this the part of the UI that's the same on both right,
255
00:12:34,354 --> 00:12:36,855
I got the display and I've got this part of the keypad.
256
00:12:36,857 --> 00:12:39,424
It's the same on both. And it's only that piece that's
257
00:12:39,426 --> 00:12:42,527
either gonna be here or here okay that is different.
258
00:12:42,529 --> 00:12:45,864
And that's all I'm gonna have to do in these two things. All
259
00:12:45,866 --> 00:12:49,434
right? So, we just add this idea of this any width and
260
00:12:49,436 --> 00:12:53,738
any height to make a nine way thing. And I, in your story
261
00:12:53,740 --> 00:12:56,908
board, you can actually go to any of these nine squares and
262
00:12:56,910 --> 00:13:00,411
edit the UI. Okay? And that's what we're gonna show in our
263
00:13:00,413 --> 00:13:03,148
demo here. Okay? So, I'm gonna do this calculator thing,
264
00:13:03,150 --> 00:13:05,850
exactly what I just showed there. And
265
00:13:05,852 --> 00:13:09,521
we'll see what it looks like to the size class business
266
00:13:09,523 --> 00:13:12,857
in Xcode. Okay so here we are in calculator.
267
00:13:12,859 --> 00:13:15,326
This is how we left off in calculator.
268
00:13:15,328 --> 00:13:16,761
Actually it's a little different cuz I this is
269
00:13:16,763 --> 00:13:18,863
the version before we did that save restore. So
270
00:13:18,865 --> 00:13:21,733
I don't have save restore. So otherwise it's just a normal
271
00:13:21,735 --> 00:13:24,702
calculator thing here. I'm gonna start right off
272
00:13:24,704 --> 00:13:28,173
by noticing that I have this warning up here. You see this,
273
00:13:28,175 --> 00:13:30,708
this yellow triangle that's all over the place here.
274
00:13:30,710 --> 00:13:32,977
Telling me about warnings. What are this warnings?
275
00:13:32,979 --> 00:13:36,181
If I click them, it's the old frame will be different at
276
00:13:36,183 --> 00:13:40,018
run time. You guys, I'm sure, are well acquainted with that.
277
00:13:40,020 --> 00:13:43,188
That you get it all the time, when you're doing auto layout
278
00:13:43,190 --> 00:13:45,824
stuff. And you know the fix to this? Anyone wanna tell me
279
00:13:45,826 --> 00:13:50,461
what the fix for this is? What do I do? Go down here,
280
00:13:50,463 --> 00:13:54,899
right. Select this view controller. Go down here and
281
00:13:54,901 --> 00:13:59,637
say update frames, right. Okay let's try it.
282
00:14:00,373 --> 00:14:04,676
No. It didn't work. What's going on here? Okay, so
283
00:14:04,678 --> 00:14:06,978
have you seen this? Where you do this update frames and
284
00:14:06,980 --> 00:14:09,347
it doesn't work. I'm sure some of you have seen that as well.
285
00:14:09,349 --> 00:14:12,150
Okay, well I'm gonna show you another way to fix this, okay,
286
00:14:12,152 --> 00:14:15,253
which is also not going to work. In the document outline,
287
00:14:15,255 --> 00:14:16,921
so here's our document outline right here.
288
00:14:16,923 --> 00:14:19,524
You see this? I told you the document outline is
289
00:14:19,526 --> 00:14:22,393
kinda the headquarters for constraints. In fact,
290
00:14:22,395 --> 00:14:25,396
here's all my constraints, okay?
291
00:14:25,398 --> 00:14:27,966
Look at this little guy right here, this little button.
292
00:14:27,968 --> 00:14:31,069
This is telling me there's a problem with your constraints.
293
00:14:31,071 --> 00:14:32,871
And you can actually click on this little button,
294
00:14:32,873 --> 00:14:36,140
which I'm going to do. And it'll show you all the places,
295
00:14:36,142 --> 00:14:38,076
all the misplaced views in this case.
296
00:14:38,078 --> 00:14:38,810
It's going around showing them,
297
00:14:38,812 --> 00:14:41,813
I can even highlight them as I mouse over them.
298
00:14:41,815 --> 00:14:44,582
And you can even fix them, if you click on this little
299
00:14:44,584 --> 00:14:46,818
triangle, right here. It'll come up and
300
00:14:46,820 --> 00:14:49,787
you can do update frames to fix this. You can try and
301
00:14:49,789 --> 00:14:52,090
update the constraints to match what's here.
302
00:14:52,092 --> 00:14:55,093
I recommend against that, generally Because it's gonna
303
00:14:55,095 --> 00:14:56,728
put hardwired constraints in there to try and
304
00:14:56,730 --> 00:14:59,964
hold that thing in position. That's usually not what you
305
00:14:59,966 --> 00:15:03,201
want. You can also reset to suggested constraints here.
306
00:15:03,203 --> 00:15:06,070
That might be something you want sometimes if you know
307
00:15:06,072 --> 00:15:07,305
that you put it on the blue lines,
308
00:15:07,307 --> 00:15:09,307
but usually you want to try and update frames.
309
00:15:09,309 --> 00:15:11,309
And you can even apply it too all the views in
310
00:15:11,311 --> 00:15:14,145
the container, cuz we know we got five problems right here.
311
00:15:14,147 --> 00:15:17,849
So, I can click those and hit fix displacement here, and
312
00:15:17,851 --> 00:15:22,253
that should fix it. Nope, it didn't, okay. Now, I'm not
313
00:15:22,255 --> 00:15:25,723
exactly sure why those either of those things didn't fix it.
314
00:15:25,725 --> 00:15:29,027
It should've. But I'm gonna show you a tricky way,
315
00:15:29,029 --> 00:15:31,496
to fix this misplacement problem.
316
00:15:31,498 --> 00:15:34,032
Would you just pick up something, okay,
317
00:15:34,034 --> 00:15:37,368
that's misplaced like this, and just put it back down
318
00:15:37,370 --> 00:15:42,507
where you think it's supposed to be. Okay? Voila, okay?
319
00:15:42,509 --> 00:15:42,674
some reason that causes the entire constraint system to
320
00:15:42,676 --> 00:15:45,610
Because for
321
00:15:45,612 --> 00:15:48,746
reevaluate everything's in the right spot, no issues, see?
322
00:15:48,748 --> 00:15:52,450
No auto layout issues here, no yellow buttons up here. Okay?
323
00:15:52,452 --> 00:15:55,019
So that's a real nice little trick to know, okay?
324
00:15:55,021 --> 00:15:59,657
Just pick the thing up and put it back down in the right
325
00:15:59,659 --> 00:16:03,861
place, might fix everything All right, okay.
326
00:16:03,863 --> 00:16:07,432
So let's talk about our calculator here.
327
00:16:07,434 --> 00:16:10,868
Our calculator wants to use size classes so that instead
328
00:16:10,870 --> 00:16:14,072
of being four across and five down like this, okay, in
329
00:16:14,074 --> 00:16:18,209
compact situations, it's gonna be five across and four down.
330
00:16:18,211 --> 00:16:21,512
So I told you there's those nine little spots?
331
00:16:21,514 --> 00:16:23,214
Here they are, right here, right down at the bottom.
332
00:16:23,216 --> 00:16:27,552
See this W Any H Any? That tells you you're editing with
333
00:16:27,554 --> 00:16:30,455
any height, any. And if you click on this,
334
00:16:30,457 --> 00:16:33,524
it'll show you all nine of those areas, okay?
335
00:16:33,526 --> 00:16:37,328
Just mouse over them. So the middle one is any, any, okay.
336
00:16:37,330 --> 00:16:41,199
Up here, this is compact height, any width.
337
00:16:41,201 --> 00:16:45,336
This down here is regular height, any width, okay?
338
00:16:45,338 --> 00:16:48,072
Here is compact width, compact height.
339
00:16:48,074 --> 00:16:50,341
So if I were to click on this and
340
00:16:50,343 --> 00:16:56,014
go edit that UI, what device would this, that ui be on?
341
00:16:56,016 --> 00:16:56,781
>> iPhone 6.
342
00:16:56,783 --> 00:16:56,848
>> iPhone 6,
343
00:16:56,850 --> 00:17:00,852
in landscape, right? Or iPhone 5 in landscape also, okay?
344
00:17:00,854 --> 00:17:01,853
Cuz that's the only one that's compact. So
345
00:17:01,855 --> 00:17:06,557
this is actually specifically choosing a non-plus iPhone,
346
00:17:06,559 --> 00:17:09,527
okay? So you can pick any of these positions that you want.
347
00:17:09,529 --> 00:17:13,931
Now we know in our calculator that the compact height, one,
348
00:17:13,933 --> 00:17:18,036
okay, is the one that we wanna move these buttons down to
349
00:17:18,038 --> 00:17:21,172
the side over here. Okay, here's a compact height.
350
00:17:21,174 --> 00:17:24,409
Now, I'm gonna show you a feature here that you might
351
00:17:24,411 --> 00:17:26,577
be inclined to use with size classes,
352
00:17:26,579 --> 00:17:28,079
but it's not gonna work for us.
353
00:17:28,081 --> 00:17:31,215
Okay, but I wanna show you how it works, which is,
354
00:17:31,217 --> 00:17:34,786
I might just start by saying let's at first, at least get
355
00:17:34,788 --> 00:17:38,856
rid of these four buttons when we're in this compact version,
356
00:17:38,858 --> 00:17:41,659
okay? See here we're in width any, height compact. We're
357
00:17:41,661 --> 00:17:45,296
editing this UI. Changes that we make in here are only going
358
00:17:45,298 --> 00:17:49,700
to apply to this size class, okay, which is compact height.
359
00:17:49,702 --> 00:17:51,035
Any time we're in a compact height.
360
00:17:51,037 --> 00:17:53,538
Now, we can only make certain changes in here, like,
361
00:17:53,540 --> 00:17:56,040
we couldn't inspect objects and change their color.
362
00:17:56,042 --> 00:17:59,143
That's not gonna work. But we can remove and add views,
363
00:17:59,145 --> 00:18:03,247
and most importantly, we can change constraints, okay?
364
00:18:03,249 --> 00:18:03,881
Those are the things we can do.
365
00:18:03,883 --> 00:18:06,484
So I said we can remove and add views, how would we remove
366
00:18:06,486 --> 00:18:09,320
this stack view? Let's say we don't want this x divide plus
367
00:18:09,322 --> 00:18:11,522
minus stack here, we just don't want it in there.
368
00:18:11,524 --> 00:18:13,291
Well, we can go over to the Inspector, okay, and
369
00:18:13,293 --> 00:18:16,294
inspect this thing. And look down at the very bottom, okay?
370
00:18:16,296 --> 00:18:18,029
This is just the Inspector for the stack view.
371
00:18:18,031 --> 00:18:21,599
And the bottom, it says right here, Installed with a check.
372
00:18:21,601 --> 00:18:26,070
That means it's installed in all size classes.
373
00:18:26,072 --> 00:18:28,973
And if we didn't want it installed in this one,
374
00:18:28,975 --> 00:18:31,375
we can actually click this little plus, and
375
00:18:31,377 --> 00:18:34,812
pick this size class, which is with any height compact,
376
00:18:34,814 --> 00:18:38,549
or we could actually pick any other one we wanted, okay?
377
00:18:38,551 --> 00:18:41,185
So we can pick any combination we want.
378
00:18:41,187 --> 00:18:42,687
Since this is the one we're editing, it's,
379
00:18:42,689 --> 00:18:44,455
the kind of the first choice right here, any width,
380
00:18:44,457 --> 00:18:47,692
compact, height. So I'm gonna pick that. And when I pick
381
00:18:47,694 --> 00:18:50,795
that, it breaks that one out. It's still installed, but
382
00:18:50,797 --> 00:18:52,830
it breaks that one out with any height compact.
383
00:18:52,832 --> 00:18:56,868
But if I click this off, it will no longer install this
384
00:18:56,870 --> 00:19:00,371
view in this size class. So now anytime I'm running in
385
00:19:00,373 --> 00:19:03,407
a modern environment where it's compact, that view,
386
00:19:03,409 --> 00:19:06,444
that stack view's not even gonna be there. Okay,
387
00:19:06,446 --> 00:19:11,015
if I go back to any any here, it's still there,
388
00:19:11,017 --> 00:19:15,753
okay. It's only in this compact height that it's gone.
389
00:19:15,755 --> 00:19:20,758
So let's go run and see if this is working for us.
390
00:19:26,900 --> 00:19:29,167
All right, here we go. It's five high and
391
00:19:29,169 --> 00:19:32,603
four wide in portrait. Looks good, okay. Hopefully,
392
00:19:32,605 --> 00:19:35,506
if we go to landscape, which we know is compact height,
393
00:19:35,508 --> 00:19:39,243
it should get rid of those four buttons, right? It did!
394
00:19:39,245 --> 00:19:41,712
Excellent, see, this size class stuff works.
395
00:19:41,714 --> 00:19:45,683
And now we'll just go back over to portrait. Oops, okay,
396
00:19:45,685 --> 00:19:49,720
look what happened there. When I went back to portrait,
397
00:19:49,722 --> 00:19:53,124
it tried to put that stack view back, but the size class
398
00:19:53,126 --> 00:19:57,128
system can't put things back into other view hierarchies,
399
00:19:57,130 --> 00:19:59,430
you can only put it back at the top level.
400
00:19:59,432 --> 00:20:02,934
So it couldn't do that, so it decided to put it back, and
401
00:20:02,936 --> 00:20:05,436
just put it back as 0,0 up there, okay?
402
00:20:05,438 --> 00:20:07,338
So that's clearly not what we want,
403
00:20:07,340 --> 00:20:08,673
that's clearly not gonna work. So
404
00:20:08,675 --> 00:20:12,276
we need a different strategy, but I just wanted to show you
405
00:20:12,278 --> 00:20:15,713
there is this feature where you can use this mechanism to
406
00:20:15,715 --> 00:20:19,417
decide that certain views exist in certain places, okay?
407
00:20:19,419 --> 00:20:21,919
And you're gonna see this is gonna automatically happen
408
00:20:21,921 --> 00:20:24,855
when we start putting views in different kinds of views,
409
00:20:24,857 --> 00:20:26,591
vertical stack views and things like that.
410
00:20:26,593 --> 00:20:29,193
You'll see that this will automatically be happening for
411
00:20:29,195 --> 00:20:32,930
us. So I'm gonna undo here, get that thing back, okay?
412
00:20:32,932 --> 00:20:38,302
So let's get this back in our width any horizontal compact.
413
00:20:38,304 --> 00:20:38,769
So that's not gonna work.
414
00:20:38,771 --> 00:20:42,807
So what could we do instead? All right, well, since I
415
00:20:42,809 --> 00:20:46,310
know I can't have this thing inside this stack view, right,
416
00:20:46,312 --> 00:20:49,380
working, I'm gonna have to pull it out. I'm gonna have to
417
00:20:49,382 --> 00:20:51,916
pull all of these things out of the stack view. So let's
418
00:20:51,918 --> 00:20:55,353
just do that, okay? I'm just going to move this thing. And
419
00:20:55,355 --> 00:20:57,588
actually, you know what? I'm gonna do all this in any any.
420
00:20:57,590 --> 00:21:01,158
Because when I think about it, I can't have this here in any
421
00:21:01,160 --> 00:21:04,195
any, otherwise, it's always gonna appear there.
422
00:21:04,197 --> 00:21:07,531
Okay, in other ones, and I don't want it there,
423
00:21:07,533 --> 00:21:08,466
on the top in compact, so
424
00:21:08,468 --> 00:21:12,036
I'm gonna take this whole stack view right here, okay?
425
00:21:12,038 --> 00:21:14,905
This one, the one that I don't want here, and
426
00:21:14,907 --> 00:21:18,976
I'm going to just delete it, hit cut to delete it. Okay, so
427
00:21:18,978 --> 00:21:21,779
now in the any any, that means on all size classes,
428
00:21:21,781 --> 00:21:25,616
that thing is gone. It's not there. And what's more,
429
00:21:25,618 --> 00:21:28,252
I'm going to take this stack view right here, and
430
00:21:28,254 --> 00:21:29,687
I'm gonna pull the things out of it.
431
00:21:29,689 --> 00:21:33,491
So for example, I'm gonna pull the display out of it, okay,
432
00:21:33,493 --> 00:21:36,460
I'm gonna take this inner stack view right here and
433
00:21:36,462 --> 00:21:39,897
pull it out, leaving this empty stack view, okay? Which
434
00:21:39,899 --> 00:21:43,668
we don't even need anymore, so I'm gonna delete it, okay?
435
00:21:43,670 --> 00:21:48,539
So now, this is all part of all size classes,
436
00:21:48,541 --> 00:21:52,643
right? This is in all size classes. In the compact case,
437
00:21:52,645 --> 00:21:57,048
it's this with buttons down here. And in the regular case,
438
00:21:57,050 --> 00:22:00,851
it's this with some buttons along here. We all agree with
439
00:22:00,853 --> 00:22:04,789
that? So in any any, we always wanna build as much of the UI
440
00:22:04,791 --> 00:22:08,859
as we can that we know applies to all size classes, okay?
441
00:22:08,861 --> 00:22:11,696
So this one, we know that this is gonna go
442
00:22:11,698 --> 00:22:14,732
in the corner down here, right? And
443
00:22:14,734 --> 00:22:19,103
we also know that this goes up at the top,
444
00:22:19,105 --> 00:22:23,107
right? And we can even do the constraints here, or at
445
00:22:23,109 --> 00:22:26,877
least some of the constraints. I'm gonna go down here and
446
00:22:26,879 --> 00:22:30,381
reset all of our constraints by picking Clear Constraints
447
00:22:30,383 --> 00:22:33,084
in the same place you Update Frames and all that.
448
00:22:33,086 --> 00:22:36,120
I'm gonna Clear Constraints to get all these constraints out
449
00:22:36,122 --> 00:22:38,656
of here, and now I'm gonna put new constraints in.
450
00:22:38,658 --> 00:22:41,258
Now this part is exactly what you're used to,
451
00:22:41,260 --> 00:22:44,128
I'm just going to Ctrl + Drag to the edge here and
452
00:22:44,130 --> 00:22:47,832
hook up that trailing space. I'm gonna Ctrl + Drag to
453
00:22:47,834 --> 00:22:50,801
the bottom down here, okay, at the bottom.
454
00:22:50,803 --> 00:22:55,573
I'm gonna, for the display up here, Ctrl + Drag to the top,
455
00:22:55,575 --> 00:22:58,142
Ctrl + Drag to the leading edge, okay?
456
00:22:58,144 --> 00:23:03,013
Ctrl + Drag to the right edge, okay? So look, no errors,
457
00:23:03,015 --> 00:23:06,016
no auto-layout issues over here in my document outline.
458
00:23:06,018 --> 00:23:08,919
No little yellow thing, so everything is perfectly fine.
459
00:23:08,921 --> 00:23:10,588
I can even run right now. Let's run and
460
00:23:10,590 --> 00:23:17,495
see what happens. Okay, so here it is in portrait. Okay,
461
00:23:17,497 --> 00:23:20,398
it's doing what we want, which is this down in the corner,
462
00:23:20,400 --> 00:23:20,998
this up here. Of course,
463
00:23:21,000 --> 00:23:23,868
I haven't added the buttons that I want right here.
464
00:23:23,870 --> 00:23:26,470
And how about in landscape? It's working here too.
465
00:23:26,472 --> 00:23:29,140
Of course, I haven't added the buttons here, all right?
466
00:23:29,142 --> 00:23:31,275
So it's putting the display in the right space.
467
00:23:31,277 --> 00:23:33,010
It's keeping these buttons over in the corner.
468
00:23:33,012 --> 00:23:37,882
So this is great, okay? It's all working fine so far.
469
00:23:37,884 --> 00:23:41,385
Now let's go and fix the two size classes. So let's start
470
00:23:41,387 --> 00:23:44,422
with compact height, okay. In compact type, actually let's
471
00:23:44,424 --> 00:23:46,290
start with regular height cuz it's the similar,
472
00:23:46,292 --> 00:23:47,725
the most similar to the other one we have.
473
00:23:47,727 --> 00:23:50,227
So here's regular height. Unfortunately, it's so
474
00:23:50,229 --> 00:23:52,730
big I have to scroll up and down, but in this one,
475
00:23:52,732 --> 00:23:55,266
we basically just want to put, take these things.
476
00:23:55,268 --> 00:23:57,435
I just pasted them back, I had cut them before, and
477
00:23:57,437 --> 00:24:01,972
we just to put it right here. Okay, right along the top.
478
00:24:01,974 --> 00:24:03,274
Now the problem here though,
479
00:24:03,276 --> 00:24:06,243
is that we need to put some constraints to make it be
480
00:24:06,245 --> 00:24:10,114
the same as the rest of those buttons in the stack view.
481
00:24:10,116 --> 00:24:13,317
Right? So, how are we gonna do that? Well there's a couple
482
00:24:13,319 --> 00:24:14,752
of different ways that we can do it.
483
00:24:14,754 --> 00:24:16,954
1. Is with these menus down here I told you about so
484
00:24:16,956 --> 00:24:22,126
I'm gonna select this one so these two views are selected,
485
00:24:22,128 --> 00:24:24,228
this whole big stack view here, and
486
00:24:24,230 --> 00:24:26,163
this stack view here. And we're gonna go down here, and
487
00:24:26,165 --> 00:24:29,967
we're gonna look at these alignment and pin menus. So
488
00:24:29,969 --> 00:24:33,938
here's an alignment one. We can do thing here to, for
489
00:24:33,940 --> 00:24:37,842
example, align the edges of these things. So it might make
490
00:24:37,844 --> 00:24:41,412
a lot of sense to take this guy and have his leading and
491
00:24:41,414 --> 00:24:44,949
trailing edges aligned with this one, right? So
492
00:24:44,951 --> 00:24:47,284
let's try that I'm just going to turn on leading and
493
00:24:47,286 --> 00:24:48,986
trailing edges here to align those.
494
00:24:48,988 --> 00:24:51,155
I could also align things like the horizontal center,
495
00:24:51,157 --> 00:24:55,926
vertical centers, I could horizontally in the containers
496
00:24:55,928 --> 00:24:58,929
centered in the container etcetera. But I'm going to do
497
00:24:58,931 --> 00:25:01,799
it, but also notice it's going to align the leading edges
498
00:25:01,801 --> 00:25:05,369
with no delta. It's just like right along the leading edge.
499
00:25:05,371 --> 00:25:07,705
So I'm going to add those two constraints. Okay.
500
00:25:07,707 --> 00:25:08,439
It's added the constraints.
501
00:25:08,441 --> 00:25:11,275
Now what's interesting here is now these
502
00:25:11,277 --> 00:25:14,545
constraint complaints are red not yellow red.
503
00:25:14,547 --> 00:25:17,381
And why is that? Okay, let's go over here and look at this.
504
00:25:17,383 --> 00:25:20,217
And it says vertical position is ambiguous for
505
00:25:20,219 --> 00:25:24,488
stack view, okay? It says it over here as well in my docu-,
506
00:25:24,490 --> 00:25:24,655
no-, outline.
507
00:25:24,657 --> 00:25:28,125
It's saying this guy has no vertical position to find. And
508
00:25:28,127 --> 00:25:30,995
in fact, that's true. There, it's just floating there.
509
00:25:30,997 --> 00:25:33,063
There's nothing that says where it is vertically.
510
00:25:33,065 --> 00:25:34,598
We've defined where it is horizontally,
511
00:25:34,600 --> 00:25:36,834
but we haven't defined where it is vertically. So
512
00:25:36,836 --> 00:25:40,838
to do that, I'm gonna re-, go back to our Ctrl-drag, okay?
513
00:25:40,840 --> 00:25:43,107
So I'm just gonna Ctrl-drag from this one to this one,
514
00:25:43,109 --> 00:25:46,977
and this time, I'm gonna put Vertical spacing, okay?
515
00:25:46,979 --> 00:25:50,114
Now he's got vertical spacing, so now his problem's right
516
00:25:50,116 --> 00:25:53,684
here, are just the frames are not, are wrong, okay. And
517
00:25:53,686 --> 00:25:56,954
again, I could pick up this thing right here and try and
518
00:25:56,956 --> 00:26:00,324
put it there. I can take this, put it down here, you know,
519
00:26:00,326 --> 00:26:01,125
try and move things back or
520
00:26:01,127 --> 00:26:04,161
I could do update frames, but we're not done yet
521
00:26:04,163 --> 00:26:08,299
either with this. Because there we fix the width
522
00:26:08,301 --> 00:26:11,268
to be the same width as this cuz we align the edges but
523
00:26:11,270 --> 00:26:14,572
what about the height, okay. We need the height of this row
524
00:26:14,574 --> 00:26:17,241
to be exactly the same height as all these rows. And
525
00:26:17,243 --> 00:26:21,378
it's perfectly legal for us to do that by going control drag,
526
00:26:21,380 --> 00:26:24,515
okay, from this guy down to this guy. And
527
00:26:24,517 --> 00:26:28,118
then saying that we want them to be equal heights.
528
00:26:28,120 --> 00:26:31,589
Okay? See that Equal Heights? Okay, so
529
00:26:31,591 --> 00:26:36,060
now they're gonna be equal heights. So you see how we've
530
00:26:36,062 --> 00:26:38,996
put this thing basically and really stack view does a very
531
00:26:38,998 --> 00:26:41,131
similar thing to what we're doing right here, okay,.
532
00:26:41,133 --> 00:26:43,367
When you said all things like fill and fill equally,
533
00:26:43,369 --> 00:26:45,502
it's making equal heights and all that stuff.
534
00:26:45,504 --> 00:26:45,736
But unfortunately,
535
00:26:45,738 --> 00:26:49,206
we had to move this stack view out because it doesn't exist,
536
00:26:49,208 --> 00:26:52,843
in some of the size classes. All right, so
537
00:26:52,845 --> 00:26:53,143
we're not quite done,
538
00:26:53,145 --> 00:26:55,713
because we still want this thing to fill the whole space,
539
00:26:55,715 --> 00:26:59,116
right? We want this nice block that we've built to fill this
540
00:26:59,118 --> 00:27:02,219
whole space. So I'm gonna do exact same thing we did
541
00:27:02,221 --> 00:27:05,389
before which is control drag okay from the edge of this.
542
00:27:05,391 --> 00:27:08,025
And make it leading space, right be fixed here.
543
00:27:08,027 --> 00:27:10,728
I'm gonna double click on it, try to make it standard.
544
00:27:10,730 --> 00:27:15,332
That's not available so I'll go zero. Okay, makes it wide.
545
00:27:15,334 --> 00:27:15,666
And same thing here.
546
00:27:15,668 --> 00:27:17,134
I wanna make the distance between here and
547
00:27:17,136 --> 00:27:20,838
here be a standard distance so let's do that. Control drag.
548
00:27:20,840 --> 00:27:24,308
And down to here and this is the vertical spacing right
549
00:27:24,310 --> 00:27:27,544
here. Okay, which makes this beam. Double click on that
550
00:27:27,546 --> 00:27:30,481
and of course when we click on it it's also showing over here
551
00:27:30,483 --> 00:27:31,382
not just in this little window, but
552
00:27:31,384 --> 00:27:35,285
it's over here as well. And here standard is available.
553
00:27:35,287 --> 00:27:38,689
Okay, so we can do standard height. Okay, so
554
00:27:38,691 --> 00:27:42,393
this looks pretty good. In the regular height situation,
555
00:27:42,395 --> 00:27:45,129
we've got four wide and five high.
556
00:27:45,131 --> 00:27:52,703
So let's go into run, see if it worked. All right,
557
00:27:52,705 --> 00:27:54,371
here it is, four wide and five high. Okay,
558
00:27:54,373 --> 00:27:58,208
let's go to landscape. Perfect we don't want four wide and
559
00:27:58,210 --> 00:28:00,844
five high there so we're just getting the any any
560
00:28:00,846 --> 00:28:05,516
situation right back to here and is back. Okay so
561
00:28:05,518 --> 00:28:08,318
it's working fine. And now, what we need to do is the same
562
00:28:08,320 --> 00:28:11,488
thing we just did for regular for compact. Okay so
563
00:28:11,490 --> 00:28:15,426
let's go to that we do that by going to this little nine grid
564
00:28:15,428 --> 00:28:18,862
here we're going up to compact height any width. And
565
00:28:18,864 --> 00:28:22,099
here we want that same thing right here,
566
00:28:22,101 --> 00:28:24,435
but this time this has to be vertical. Okay.
567
00:28:24,437 --> 00:28:26,870
So I'm just gonna go to the stack views inspector.
568
00:28:26,872 --> 00:28:27,871
Here's the stack view inspecting it.
569
00:28:27,873 --> 00:28:31,275
And I'm gonna make it vertical this time. Okay?
570
00:28:31,277 --> 00:28:34,278
And I want to put it along here. Okay?
571
00:28:34,280 --> 00:28:36,847
So I'm gonna use control drag to do it this time and
572
00:28:36,849 --> 00:28:40,784
I can actually set up most of this using shift in here.
573
00:28:40,786 --> 00:28:43,420
I think I told you before when this comes up You can actually
574
00:28:43,422 --> 00:28:47,024
click multiple things, okay, with shift. So I can make it,
575
00:28:47,026 --> 00:28:51,495
for example, the horizontal spacing, the top, the bottom.
576
00:28:51,497 --> 00:28:53,697
I can line those all up. Okay? Now,
577
00:28:53,699 --> 00:28:55,899
I don't actually want this to be equal widths, because I
578
00:28:55,901 --> 00:28:58,335
don't want this to be the same width as this whole thing.
579
00:28:58,337 --> 00:29:00,170
So, I'm going to have to do equal widths,
580
00:29:00,172 --> 00:29:03,540
by making this the same width as one of these buttons. Okay.
581
00:29:03,542 --> 00:29:06,110
So we do that to set those. So I'm gonna Control drag
582
00:29:06,112 --> 00:29:07,711
from here to one of these buttons and
583
00:29:07,713 --> 00:29:11,181
do Equal Widths, okay, so that this will be the same width.
584
00:29:11,183 --> 00:29:14,718
Of course, we've got this whole frames are wrong,
585
00:29:14,720 --> 00:29:16,086
so this time, just to be different,
586
00:29:16,088 --> 00:29:17,955
I'll go up here and click one of these and
587
00:29:17,957 --> 00:29:22,292
apply to all views in container. Fix it. Okay. So
588
00:29:22,294 --> 00:29:24,194
now, we've got it there where we want it. Again,
589
00:29:24,196 --> 00:29:28,465
same thing here. We want to put a vertical spacing
590
00:29:28,467 --> 00:29:32,970
constraint here. Okay, we'll do standard if available,
591
00:29:32,972 --> 00:29:37,841
yep. Okay, and same thing over here. We will put this to
592
00:29:37,843 --> 00:29:43,113
the leading edge and again, no standard. Sorry,
593
00:29:43,115 --> 00:29:49,186
so we'll do zero. Okay, there we go. We've got five wide,
594
00:29:49,188 --> 00:29:53,590
four high in this compact situation, all right?
595
00:29:53,592 --> 00:29:59,596
So let's run that. Okay,
596
00:29:59,598 --> 00:30:02,499
here it is, portrait, looking good, landscape,
597
00:30:02,501 --> 00:30:07,037
looking good. Okay,
598
00:30:07,606 --> 00:30:11,809
any questions about that? Make sense? All right, now I wanna
599
00:30:11,811 --> 00:30:15,012
show you a couple of things about what we've wrought here.
600
00:30:15,014 --> 00:30:18,182
One thing is let's look at this stack view right here.
601
00:30:18,184 --> 00:30:21,285
Look at it's installed state down there. See that?
602
00:30:21,287 --> 00:30:25,823
It's not installed in any any. It's only installed in any
603
00:30:25,825 --> 00:30:28,559
with compact height. That's exactly what we want. So
604
00:30:28,561 --> 00:30:31,862
see how it automatically did this when we pasted this one
605
00:30:31,864 --> 00:30:35,132
in? Any time we add a view to the size class, it's going to
606
00:30:35,134 --> 00:30:39,036
automatically do this install magic for you here. Okay?
607
00:30:39,038 --> 00:30:42,472
Now another think I told you was that the document outline
608
00:30:42,474 --> 00:30:44,942
is kind of your constraint central. Okay?
609
00:30:44,944 --> 00:30:47,711
So let's understand what the document outline is telling us
610
00:30:47,713 --> 00:30:52,649
about constraints here okay? Here's all our constraints and
611
00:30:52,651 --> 00:30:54,985
notice that some of them are grayed out and
612
00:30:54,987 --> 00:30:57,654
some of them are dark blue. See that?
613
00:30:57,656 --> 00:31:00,157
Well, as you might imagine the grayed out ones
614
00:31:00,159 --> 00:31:03,327
are constraints that only apply in other size classes.
615
00:31:03,329 --> 00:31:06,363
That don't apply here. So since this is any width
616
00:31:06,365 --> 00:31:09,499
compact height, we're not seeing any of the constraints
617
00:31:09,501 --> 00:31:12,870
that have to do with any width regular heigth.
618
00:31:12,872 --> 00:31:13,136
if I switch over okay watch these carefully right here.
619
00:31:13,138 --> 00:31:14,972
Okay? But,
620
00:31:14,974 --> 00:31:17,174
You're gonna see some of these are gonna go like right and
621
00:31:17,176 --> 00:31:19,309
some of these are gonna go dark blue. Okay.
622
00:31:19,311 --> 00:31:23,780
So I'm gonna switch down here to any width, regular height.
623
00:31:23,782 --> 00:31:25,215
Which has different constraints.
624
00:31:25,217 --> 00:31:27,651
You see how this changed? So you can always tell
625
00:31:27,653 --> 00:31:31,421
which ones apply to you, okay, by which ones are dark.
626
00:31:31,423 --> 00:31:35,025
Now another thing you wanna do when you are using constraints
627
00:31:35,027 --> 00:31:39,296
is to go magic number hunting. Okay. You want to look at
628
00:31:39,298 --> 00:31:41,732
these constraints and hunt for magic numbers and
629
00:31:41,734 --> 00:31:45,802
get rid of them. Unless you really mean to have one. Okay,
630
00:31:45,804 --> 00:31:49,640
very important. So in here let's find some magic numbers.
631
00:31:49,642 --> 00:31:53,610
Okay. Here's one, 8, here's 282, 82,
632
00:31:53,612 --> 00:31:58,181
88 and 20. So those are all of our magic numbers right there.
633
00:31:58,183 --> 00:32:01,118
First thing you wanna do is, only you can only hunt for
634
00:32:01,120 --> 00:32:04,321
magic numbers in the ones that are dark blue. Okay,
635
00:32:04,323 --> 00:32:08,725
you can£t, these 282 is in some other constraint in some
636
00:32:08,727 --> 00:32:12,396
other size class, so just ignore it here. It£s like
637
00:32:12,398 --> 00:32:15,966
you£re not even seeing it. But this eight right here, okay?
638
00:32:15,968 --> 00:32:19,236
This is the one we do need to look at, okay? Cuz we just
639
00:32:19,238 --> 00:32:21,905
don't want this magic number here. So what is this one?
640
00:32:21,907 --> 00:32:25,976
This is the display's top to the top layout guide's bottom.
641
00:32:25,978 --> 00:32:28,812
It's this one right here. Okay, so that's eight.
642
00:32:28,814 --> 00:32:32,783
Okay, let's go ahead and inspect this. We're inspecting
643
00:32:32,785 --> 00:32:35,452
this constraint. And you can see that it's 8 and
644
00:32:35,454 --> 00:32:38,522
really what you wanna change that to is standard value.
645
00:32:38,524 --> 00:32:40,590
And when you change that to standard value, look.
646
00:32:40,592 --> 00:32:43,894
The eight's gone, okay? Now you might ask,
647
00:32:43,896 --> 00:32:45,362
where did that eight come in in the first place?
648
00:32:45,364 --> 00:32:48,899
Well probably we dragged that thing to, and used a blue line
649
00:32:48,901 --> 00:32:52,736
to do, to put it there. And it was eight points from the top.
650
00:32:52,738 --> 00:32:54,871
And so when we added constraint,
651
00:32:54,873 --> 00:32:58,075
it added it as eight. But what we really wanted was standard
652
00:32:58,077 --> 00:33:00,510
distance. Okay, how about this one right here?
653
00:33:00,512 --> 00:33:03,313
This is the stack view's top to the stack view bottom.
654
00:33:03,315 --> 00:33:06,984
That's this one right here. Okay, uh-oh, [LAUGH] that one
655
00:33:06,986 --> 00:33:11,655
is a magic number, but that's the wrong magic number.
656
00:33:11,657 --> 00:33:14,558
Because what is the distance in all these other things.
657
00:33:14,560 --> 00:33:16,860
In this stack view, what's the spacing?
658
00:33:16,862 --> 00:33:20,464
It's ten, remember that? Okay? So, we want the space in
659
00:33:20,466 --> 00:33:24,001
between these two stack views to also be ten. Okay? So
660
00:33:24,003 --> 00:33:26,169
this constant wants to be a magic number and
661
00:33:26,171 --> 00:33:29,406
it wants to be ten, and it truly is a magic number. Okay?
662
00:33:29,408 --> 00:33:30,140
So we want that magic number,
663
00:33:30,142 --> 00:33:33,377
because we intended it to be a magic number.
664
00:33:33,379 --> 00:33:36,179
All right? How about this one? 20.
665
00:33:36,181 --> 00:33:38,782
Okay, stack view bottom right here. That one also,
666
00:33:38,784 --> 00:33:42,386
we probably don't want that to be a non-standard value, so
667
00:33:42,388 --> 00:33:45,689
I'm just gonna pick Standard Value, okay?
668
00:33:45,691 --> 00:33:49,726
And we'll do the same thing in our other size classes,
669
00:33:49,728 --> 00:33:52,195
so we'll go here. This one right here has
670
00:33:52,197 --> 00:33:54,297
this Stack View.leading to Stack View.trailing,
671
00:33:54,299 --> 00:33:56,600
that's right here, see it highlighted right there?
672
00:33:56,602 --> 00:34:00,437
That wants to be ten. What else have we got here?
673
00:34:00,439 --> 00:34:02,773
Anything else? No? All the rest of these are good?
674
00:34:02,775 --> 00:34:05,842
And don't forget any any when you do these things.
675
00:34:05,844 --> 00:34:07,044
So go to any any. Now it looks good.
676
00:34:07,046 --> 00:34:11,248
They're all good. Okay, so I have no magic numbers now. So
677
00:34:11,250 --> 00:34:13,083
my UI is all nice and arranged.
678
00:34:13,085 --> 00:34:15,118
Now why do we wanna get rid of these magic numbers?
679
00:34:15,120 --> 00:34:18,889
Because the built-in numbers, the standard numbers,
680
00:34:18,891 --> 00:34:20,090
are gonna be the same in every single app,
681
00:34:20,092 --> 00:34:22,492
not just your app. So when people are running other apps,
682
00:34:22,494 --> 00:34:25,028
things are gonna look similar, similar spacing, etc. And
683
00:34:25,030 --> 00:34:27,564
that's what you want, cuz you want a consistent experience,
684
00:34:27,566 --> 00:34:31,001
not just inside your own app, but within you know,
685
00:34:31,003 --> 00:34:36,940
two other apps, as well. What else did I want to show here?
686
00:34:36,942 --> 00:34:41,945
We talked about insufficient constraints,
687
00:34:41,947 --> 00:34:47,084
we talked about, okay. Conflicting constraints.
688
00:34:47,086 --> 00:34:49,086
Sometimes you'll have conflicting constraints, like
689
00:34:49,088 --> 00:34:51,488
two different constraints that are trying to constrain the
690
00:34:51,490 --> 00:34:55,525
same two things and they'll be across size classes.
691
00:34:55,527 --> 00:34:58,061
Those can be sometimes hard to find. Okay,
692
00:34:58,063 --> 00:35:00,497
let's say I have my calculator app here. Okay,
693
00:35:00,499 --> 00:35:02,699
I'm looking at any any here and I go away for
694
00:35:02,701 --> 00:35:05,268
a few months and I come back and I look at this, and
695
00:35:05,270 --> 00:35:08,305
I forget the fact that I had these other side classes over
696
00:35:08,307 --> 00:35:12,042
here. And I look at this and I'm like, my gosh, I forgot to
697
00:35:12,044 --> 00:35:15,712
control drag this up to the top. No, I better do that.
698
00:35:15,714 --> 00:35:18,381
Okay vertical spacing and I better put this thing over
699
00:35:18,383 --> 00:35:22,919
here my lead spacing. Yeah, well have to fix that let's
700
00:35:22,921 --> 00:35:27,958
make this be zero and we'll fix this and we'll
701
00:35:27,960 --> 00:35:32,529
make this one be a standard. Okay whew, I fixed it. Okay,
702
00:35:32,531 --> 00:35:36,500
everything's good. Look I have no conflicting constraints.
703
00:35:36,502 --> 00:35:38,435
I must have fixed my app, it's in good shape.
704
00:35:38,437 --> 00:35:41,805
So let's go ahead and run it. I'm sure it looks really good.
705
00:35:45,844 --> 00:35:49,980
What the, okay, that one's okay but what a mess.
706
00:35:49,982 --> 00:35:51,014
What the heck happened here? And look,
707
00:35:51,016 --> 00:35:53,083
I'm getting all kinds of warnings on my console,
708
00:35:53,085 --> 00:35:56,853
conflicting constraints. You know what, so I've totally
709
00:35:56,855 --> 00:35:59,623
broken the world, even though when I go back into my app,
710
00:35:59,625 --> 00:36:02,726
it looks like everything fine. That's because when you change
711
00:36:02,728 --> 00:36:04,861
constraints, especially when you change any any,
712
00:36:04,863 --> 00:36:08,265
you wanna go take a look at your other size classes. Now,
713
00:36:08,267 --> 00:36:11,768
when we look at that. [SOUND] Two views are horizontally
714
00:36:11,770 --> 00:36:15,739
ambiguous. Three conflicting constraints. If we go down
715
00:36:15,741 --> 00:36:20,477
to this one down here, we got four conflicting constraints.
716
00:36:20,479 --> 00:36:23,580
When you have conflicting constraints, this little
717
00:36:23,582 --> 00:36:26,616
yellow thing up here will actually turn red, okay? And
718
00:36:26,618 --> 00:36:28,952
then you can click on that and see all the conflicts.
719
00:36:28,954 --> 00:36:30,687
So these things are all conflicting, and
720
00:36:30,689 --> 00:36:32,322
of course these conflict, okay?
721
00:36:32,324 --> 00:36:33,356
We know why these conflict.
722
00:36:33,358 --> 00:36:37,394
Because in any any, we've fixed the edges of this stack
723
00:36:37,396 --> 00:36:40,797
view to some things. And in the other size classes, we've
724
00:36:40,799 --> 00:36:45,468
also fixed the edges of these, this thing to other things.
725
00:36:45,470 --> 00:36:49,272
Okay, like to this thing over here, for example. So
726
00:36:49,274 --> 00:36:54,744
be careful about that when you have, when you're putting your
727
00:36:54,746 --> 00:36:57,080
constraints in there, it might look like everything's fine,
728
00:36:57,082 --> 00:36:58,248
but you could have conflicts across.
729
00:36:58,250 --> 00:37:03,186
So I'm gonna undo all this. To go back here. All right,
730
00:37:03,188 --> 00:37:06,590
another thing I want to show you is
731
00:37:06,592 --> 00:37:09,159
just to kind of prove to you what we were talking
732
00:37:09,161 --> 00:37:12,229
about with the compact width being on some platforms
733
00:37:12,231 --> 00:37:15,232
not others, I'm actually gonna go up here to a different one
734
00:37:15,234 --> 00:37:18,802
which is this guy right here. This is compact width
735
00:37:18,804 --> 00:37:21,905
any height. That's what this square is. I'm gonna click on
736
00:37:21,907 --> 00:37:23,406
that. So here's compact width any height.
737
00:37:23,408 --> 00:37:25,742
Notice that it looks like any any because we haven't done
738
00:37:25,744 --> 00:37:28,845
anything special in here. And I'm actually gonna drag
739
00:37:28,847 --> 00:37:32,115
a button out. Okay, here's a button. I'm gonna drag it.
740
00:37:32,117 --> 00:37:34,384
I'm gonna call this button, actually, we'll just do it
741
00:37:34,386 --> 00:37:36,920
with a label. We don't need a button, this label here,
742
00:37:36,922 --> 00:37:40,323
and we'll call this thing Compact Width.
743
00:37:40,325 --> 00:37:42,659
Cuz we're putting this in the compact width any,
744
00:37:42,661 --> 00:37:46,830
right? Then I'll even put it right here in the middle.
745
00:37:46,832 --> 00:37:49,032
I'll do another control drag so you can see constraints.
746
00:37:49,034 --> 00:37:52,402
So, I'm gonna control drag to my super view, okay, and
747
00:37:52,404 --> 00:37:56,806
say center this vertically in the container. So
748
00:37:56,808 --> 00:37:58,742
now it's gonna center this. So now when I run,
749
00:37:58,744 --> 00:38:03,346
okay, any time I'm appearing on a compact width situation,
750
00:38:03,348 --> 00:38:04,547
it's gonna say compact width. So
751
00:38:04,549 --> 00:38:07,250
sure enough, this one, that's compact width, right?
752
00:38:07,252 --> 00:38:09,452
We know iPhone 6 and portrait is compact width.
753
00:38:09,454 --> 00:38:13,056
And here, this is also compact width cuz this is iPhone 6 not
754
00:38:13,058 --> 00:38:17,427
6 Plus, iPhone 6 in landscape is also compact width, okay?
755
00:38:17,429 --> 00:38:19,329
Now we know this is compact height, because look,
756
00:38:19,331 --> 00:38:23,633
our times divide plus minus are not along the sides so
757
00:38:23,635 --> 00:38:23,967
it's compact height. And
758
00:38:23,969 --> 00:38:26,836
we know this is regular height because those things are on
759
00:38:26,838 --> 00:38:30,440
the top. Okay, now let's go look at this on 6 Plus.
760
00:38:38,583 --> 00:38:42,719
All right, so 6 Plus portrait is compact width,
761
00:38:42,721 --> 00:38:46,089
okay. In landscape, not compact width.
762
00:38:46,091 --> 00:38:51,261
It's regular width. All right? But this height is the same.
763
00:38:51,263 --> 00:38:54,397
This is compact height because these are not along the top.
764
00:38:54,399 --> 00:38:58,068
And this is regular height because these are the top.
765
00:38:58,270 --> 00:39:01,371
Okay, let's take a look at iPad.
766
00:39:12,417 --> 00:39:16,186
All right, so iPad, not compact width in portrait,
767
00:39:16,188 --> 00:39:20,423
also not compact width in landscape. And it's
768
00:39:20,425 --> 00:39:24,194
regular in height because, see these buttons on the top?
769
00:39:24,196 --> 00:39:27,931
In landscape and it's regular in height in portrait as well.
770
00:39:27,933 --> 00:39:31,134
So regular in both directions. Both landscape and portrait.
771
00:39:31,136 --> 00:39:32,369
Okay now I'm gonna do something here,
772
00:39:32,371 --> 00:39:34,971
I'm gonna go back to the calculator here and
773
00:39:34,973 --> 00:39:38,575
I'm gonna put it in a split view. Okay?
774
00:39:38,577 --> 00:39:40,610
So this is my calculator here, I'm gonna pop this thing into
775
00:39:40,612 --> 00:39:43,613
split view,so let's grab a split view. I'm gonna make
776
00:39:43,615 --> 00:39:45,915
this calculator be the master of the split view.
777
00:39:45,917 --> 00:39:50,186
There is the split view. I'm to, delete this and this.
778
00:39:50,188 --> 00:39:53,189
And I'm just gonna make this be the entry point, right?
779
00:39:53,191 --> 00:39:55,325
This split view is gonna be the entry point. And then I'll
780
00:39:55,327 --> 00:39:59,763
make this be the master. Okay? So now let's run on iPad and
781
00:39:59,765 --> 00:40:05,402
see what we got. Okay.
782
00:40:05,404 --> 00:40:08,738
Here we go. This is portrait. Here's landscape.
783
00:40:08,740 --> 00:40:12,142
Okay notice or my calculator is compact width
784
00:40:12,144 --> 00:40:14,711
when it's even if it's on iPad. But it's still compact
785
00:40:14,713 --> 00:40:17,180
width because it's in the master here. Here's a really
786
00:40:17,182 --> 00:40:20,016
interesting one. What happens if I'm in portrait and
787
00:40:20,018 --> 00:40:25,588
I pull the master out? Not compact width. Okay
788
00:40:25,590 --> 00:40:29,426
not compact width, even though it's the exact same width
789
00:40:29,428 --> 00:40:32,362
okay as in landscape. It's not compact width, and that's
790
00:40:32,364 --> 00:40:34,397
because there's actually API in the split view what when
791
00:40:34,399 --> 00:40:37,267
you put that thing out it can pull out different distances.
792
00:40:37,269 --> 00:40:40,437
And rather than have them try to be changing the size class
793
00:40:40,439 --> 00:40:43,706
on the fly, sometimes it's compact, sometimes regular.
794
00:40:43,708 --> 00:40:45,442
They just said, okay,
795
00:40:45,444 --> 00:40:49,412
because it lets you tell these two different states, okay?
796
00:40:49,414 --> 00:40:52,682
Whether you're in the landscape as the master or
797
00:40:52,684 --> 00:40:55,218
you're pulled out as the master, okay?
798
00:40:55,220 --> 00:41:00,056
Everyone got that? Okay, so that's it, for size classes,
799
00:41:00,058 --> 00:41:04,394
that's it for auto layout. Again, only experience will
800
00:41:04,396 --> 00:41:06,362
really teach you auto layout in the long run,
801
00:41:06,364 --> 00:41:08,031
but you know where all the pieces of it are now,
802
00:41:08,033 --> 00:41:10,166
and you know how to do control drag.
803
00:41:10,168 --> 00:41:12,735
That's probably the main thing you're gonna be doing.
804
00:41:12,737 --> 00:41:14,270
And maybe you'll be doing these menus down here,
805
00:41:14,272 --> 00:41:20,477
possibly. Okay, Friday, no section again on this Friday.
806
00:41:20,479 --> 00:41:22,912
Monday your current assignment is coordinate a smash tag
807
00:41:22,914 --> 00:41:27,116
is due. There will be another assignment, Assignment 6 going
808
00:41:27,118 --> 00:41:30,787
out next week. Not sure, probably gonna go out on
809
00:41:30,789 --> 00:41:33,756
Wednesday. It might go out on Wednesday, not 100% sure.
810
00:41:33,758 --> 00:41:36,125
I'll think about it over the week can't okay but
811
00:41:36,127 --> 00:41:38,728
the topic of that assignment and in the topic of next
812
00:41:38,730 --> 00:41:41,598
week's lectures is gonna be animation. Okay, a very
813
00:41:41,600 --> 00:41:45,101
important part obviously building an app on iOS and
814
00:41:45,103 --> 00:41:49,939
that's it. So I'll be here I'm here if you have questions as
815
00:41:49,941 --> 00:41:53,710
usual, that kind of business. >> For
816
00:41:53,712 --> 00:41:53,743
more please visit us at Stanford.edu.
================================================
FILE: subtitles/13. NSTimer and Animation.srt
================================================
1
00:00:00,001 --> 00:00:03,602
[MUSIC]
2
00:00:03,604 --> 00:00:08,507
Stanford University. >> Okay, well, welcome
3
00:00:08,509 --> 00:00:13,946
to Lecture 13 of Stanford CS193P spring of 2016. Today,
4
00:00:13,948 --> 00:00:18,117
we're gonna be talking about animation,
5
00:00:18,119 --> 00:00:21,754
primarily. I'm actually gonna show you a little class called
6
00:00:21,756 --> 00:00:25,290
NSTimer first, which we really don't use for animations. But
7
00:00:25,292 --> 00:00:28,527
when we're doing animations surprisingly, we end up using
8
00:00:28,529 --> 00:00:30,896
NSTimer sometimes. Not for the animation itself,
9
00:00:30,898 --> 00:00:34,266
but just because animation is timing-based thing, and
10
00:00:34,268 --> 00:00:36,869
NSTimer obviously is a timing-based class.
11
00:00:36,871 --> 00:00:38,771
And then we're gonna dive right into animation, and
12
00:00:38,773 --> 00:00:42,441
there's lots and lots of mechanisms for animation
13
00:00:42,443 --> 00:00:45,244
in iOS, and I'm only gonna show you, you know, two or
14
00:00:45,246 --> 00:00:48,614
three different ones. I'll talk a little bit about what,
15
00:00:48,616 --> 00:00:52,184
what things there are, but I'm only gonna dive into a few of
16
00:00:52,186 --> 00:00:55,687
them. And one of them, the simulated physics version,
17
00:00:55,689 --> 00:00:59,158
I will get started on the slides today at the end, but
18
00:00:59,160 --> 00:01:01,393
I won't make it all the way through, and
19
00:01:01,395 --> 00:01:04,963
I'll just continue in the next lecture, okay. All right,
20
00:01:04,965 --> 00:01:07,566
so let's take this little detour and talk about NSTimer.
21
00:01:07,568 --> 00:01:12,471
NSTimer is a very simple little class that allows
22
00:01:12,473 --> 00:01:16,909
you to call a method periodically, okay.
23
00:01:16,911 --> 00:01:17,976
And you could either have it
24
00:01:17,978 --> 00:01:19,645
repeating where it's calling it over and
25
00:01:19,647 --> 00:01:22,548
over every certain amount of time, or you can actually just
26
00:01:22,550 --> 00:01:25,417
have it call it once sometime in the future. Okay, so
27
00:01:25,419 --> 00:01:29,121
it's a way to set up a time or to call a method. It couldn't
28
00:01:29,123 --> 00:01:31,924
really be simpler than that. Again, we're not gonna
29
00:01:31,926 --> 00:01:34,460
use it for animation, you know, things moving around and
30
00:01:34,462 --> 00:01:35,294
stuff. There's other mechanisms for
31
00:01:35,296 --> 00:01:38,630
doing that. But you're gonna see from the demo I do today,
32
00:01:38,632 --> 00:01:41,200
wha, where NSTimer kind of can come in and be used.
33
00:01:41,202 --> 00:01:43,702
It's actually quite useful in a lot of situations.
34
00:01:43,704 --> 00:01:48,307
It's not a real time timer, you know? This is not a real
35
00:01:48,309 --> 00:01:52,144
time operating system. You got that main queue. Other blocks
36
00:01:52,146 --> 00:01:54,179
might be executing off that main queue, and so
37
00:01:54,181 --> 00:01:57,549
the timer's trying to execute, but it can't cuz it's busy.
38
00:01:57,551 --> 00:02:00,319
So it could be off a little bit but it's generally
39
00:02:00,321 --> 00:02:05,457
going to try to set this, call this method when you say.
40
00:02:05,659 --> 00:02:08,227
The timer's built on a mechanism in iOS which I'm not
41
00:02:08,229 --> 00:02:11,296
gonna talk about at all in this class, called run loops.
42
00:02:11,298 --> 00:02:14,433
For the main queue, there's a run loop set up for you, so
43
00:02:14,435 --> 00:02:18,570
NSTimer always works on the main queue. For other queues,
44
00:02:18,572 --> 00:02:21,106
you know, you may or may not have a run loop. You might
45
00:02:21,108 --> 00:02:24,209
have to set up your own run loop, etc, so for the purposes
46
00:02:24,211 --> 00:02:27,813
of this class, just assume NSTimer is a main queue thing.
47
00:02:27,815 --> 00:02:30,082
Of course, when a timer goes off and calls a method,
48
00:02:30,084 --> 00:02:33,352
you could dispatc_async to another queue in that method,
49
00:02:33,354 --> 00:02:35,821
that's fine. It is just that the actual firing of
50
00:02:35,823 --> 00:02:39,091
the timer, that has to be happening in the main queue,
51
00:02:39,093 --> 00:02:40,459
for the purposes of this class.
52
00:02:40,461 --> 00:02:42,561
Okay, so what's the main method
53
00:02:42,563 --> 00:02:45,831
look like in NSTimer that does this? This is it.
54
00:02:45,833 --> 00:02:47,866
Say I just scheduled timer with time interval.
55
00:02:47,868 --> 00:02:49,368
Notice that it's a class method, right,
56
00:02:49,370 --> 00:02:52,404
a static method on the class. So you invoke this by saying
57
00:02:52,406 --> 00:02:56,175
NSTimer.scheduledTimeWithTime- Interval. And you just specify
58
00:02:56,177 --> 00:02:58,544
how many seconds from now you want this timer to go off for
59
00:02:58,546 --> 00:03:02,147
the first time, and then you specify a target and selector.
60
00:03:02,149 --> 00:03:04,750
That's the method in the object that you want to be
61
00:03:04,752 --> 00:03:07,352
called. You got this user info, that's basically
62
00:03:07,354 --> 00:03:10,656
a cookie, right? You can put anything you want in there,
63
00:03:10,658 --> 00:03:11,590
some context, whatever,
64
00:03:11,592 --> 00:03:12,524
you can leave it nil if you want.
65
00:03:12,526 --> 00:03:16,061
And then repeat is whether it's going to go off again in
66
00:03:16,063 --> 00:03:18,997
this many seconds, at the beginning there, and
67
00:03:18,999 --> 00:03:22,167
then just keep going off, over and over and over.
68
00:03:22,169 --> 00:03:26,438
So it couldn't really be simpler than that. So
69
00:03:26,440 --> 00:03:28,240
here's an example of calling it. All right,
70
00:03:28,242 --> 00:03:31,810
I'm calling it for it to go off in two seconds there.
71
00:03:31,812 --> 00:03:35,747
You see? Two seconds and I'm gonna call a method in myself
72
00:03:35,749 --> 00:03:37,683
called fire. It has one argument.
73
00:03:37,685 --> 00:03:40,452
We'll talk about that what that argument is in a second.
74
00:03:40,454 --> 00:03:43,322
And then here I'm not passing any context or
75
00:03:43,324 --> 00:03:44,022
cookie or whatever right there,
76
00:03:44,024 --> 00:03:46,792
just nil. And this one's gonna be re, repeating. So every two
77
00:03:46,794 --> 00:03:50,195
seconds this method, fire, is going to be invoked in self.
78
00:03:50,197 --> 00:03:53,632
And so let's go ahead and look at fire with this argument
79
00:03:53,634 --> 00:03:55,100
NSTimer in there. Here it is.
80
00:03:55,102 --> 00:03:59,571
You remember from the selector syntax up here, this hashtag,
81
00:03:59,573 --> 00:04:02,975
or pound sign, whatever, hashtag we call it now,
82
00:04:02,977 --> 00:04:07,212
[LAUGH] pound sign selector. Fire, see this little
83
00:04:07,214 --> 00:04:10,115
under bar colon right there means it has an argument. And
84
00:04:10,117 --> 00:04:14,620
that argument, for NStimer is always the timer itself, okay?
85
00:04:14,622 --> 00:04:18,056
So that's the timer that is sending you this fire
86
00:04:18,058 --> 00:04:18,257
method down here. And
87
00:04:18,259 --> 00:04:21,226
inside of the implementation of this fire method, you can
88
00:04:21,228 --> 00:04:24,596
get this cookie that you gave it up here where it's,
89
00:04:24,598 --> 00:04:26,498
now it's nil but if I put a cookie up there,
90
00:04:26,500 --> 00:04:31,903
I can get it back by accessing the userInfo bar on NSTimer,
91
00:04:31,905 --> 00:04:34,906
see? So that's how you get the cookie back. Cuz remember this
92
00:04:34,908 --> 00:04:36,842
timer is going on some number of seconds or
93
00:04:36,844 --> 00:04:39,811
even minutes later, and you might want that timer to
94
00:04:39,813 --> 00:04:42,447
get that cookie back at that time. Okay,
95
00:04:42,449 --> 00:04:44,916
now what if you have a repeating timer and
96
00:04:44,918 --> 00:04:48,253
you wanna stop it? How do you do that? You call this method
97
00:04:48,255 --> 00:04:50,922
on the timer called invalidate. Now a little bit
98
00:04:50,924 --> 00:04:53,625
of a warning, if you call invalidate on a timer,
99
00:04:53,627 --> 00:04:55,093
that timer is no longer valid, and
100
00:04:55,095 --> 00:04:57,663
you should not do anything with it. You can't restart it,
101
00:04:57,665 --> 00:04:59,731
really. There's nothing you can do with it. And
102
00:04:59,733 --> 00:05:03,368
so it's usually a pretty bad idea to have a strong pointer
103
00:05:03,370 --> 00:05:06,605
to an NSTimer, okay? Because you've got the strong pointer,
104
00:05:06,607 --> 00:05:09,708
you invalidate it. Now you've got a strong pointer to
105
00:05:09,710 --> 00:05:10,876
something that's invalid.
106
00:05:10,878 --> 00:05:13,045
So it's much better to make your pointers to NSTimers
107
00:05:13,047 --> 00:05:16,415
be weak. And when you do that, if you hit invalidate,
108
00:05:16,417 --> 00:05:18,784
no one will have a strong pointer to it anymore. And
109
00:05:18,786 --> 00:05:22,988
it'll leave the heap, and your weak variable will get set
110
00:05:22,990 --> 00:05:26,625
to nil. So I recommend using weak pointers to NSTimers. Or
111
00:05:26,627 --> 00:05:31,630
not having any at all because remember every time it fires,
112
00:05:31,632 --> 00:05:37,069
you get this timer passed back to you as the argument if you
113
00:05:37,071 --> 00:05:40,105
want it. Okay, tolerance.
114
00:05:40,107 --> 00:05:44,509
So you can specify in the timer a tolerance, and that's
115
00:05:44,511 --> 00:05:49,348
basically how much you're willing to accept some slop in
116
00:05:49,350 --> 00:05:52,017
its calculation of when it goes off,
117
00:05:52,019 --> 00:05:54,853
okay? So here I'm talking about an example where you
118
00:05:54,855 --> 00:05:55,854
have a timer goes off once a minute.
119
00:05:55,856 --> 00:05:58,323
You set the tolerance to ten, which is in seconds,
120
00:05:58,325 --> 00:06:01,927
and that means if it goes off after a minute, that's fine,
121
00:06:01,929 --> 00:06:03,662
a minute and three seconds from now.
122
00:06:03,664 --> 00:06:05,864
Yeah, okay, a minute and 7 seconds, fine, but
123
00:06:05,866 --> 00:06:08,834
a minute and 20 seconds, that's too long, all right.
124
00:06:08,836 --> 00:06:10,402
And so why would you want this tolerance?
125
00:06:10,404 --> 00:06:12,437
Well, because other things are going on in the system, and
126
00:06:12,439 --> 00:06:15,273
if you really don't care about getting it right on time,
127
00:06:15,275 --> 00:06:17,342
then maybe the system can be more efficient about how it's
128
00:06:17,344 --> 00:06:21,947
using its processor and other resources during that time.
129
00:06:21,949 --> 00:06:25,951
Okay, and notice that this tolerance does not cause
130
00:06:25,953 --> 00:06:29,187
drift. So if the first time it goes off, it goes off a minute
131
00:06:29,189 --> 00:06:33,558
and seven seconds after, the next time it's gonna go off,
132
00:06:33,560 --> 00:06:36,161
try to go off two minutes from the beginning. And
133
00:06:36,163 --> 00:06:37,729
the next time, three minutes from the beginning,
134
00:06:37,731 --> 00:06:41,133
you see what I mean, it's not drifting. If it's late on one,
135
00:06:41,135 --> 00:06:43,769
it's doesn't start being late on the next one.
136
00:06:44,538 --> 00:06:46,037
All right, so let's do a demo of this, so
137
00:06:46,039 --> 00:06:50,075
you can see it in action. What I'm gonna do here is I'm gonna
138
00:06:50,077 --> 00:06:54,112
take our old friend FaceIt, okay, remember FaceIt there,
139
00:06:54,114 --> 00:06:57,883
and we're gonna make it so his eyes blink, okay, and
140
00:06:57,885 --> 00:07:01,787
we're gonna do that using a timer. So
141
00:07:01,789 --> 00:07:04,222
it's pretty straightforward actually to do this.
142
00:07:04,224 --> 00:07:07,192
I'm gonna use this opportunity to teach you something else.
143
00:07:07,194 --> 00:07:09,761
Okay, I always try to do that. Which is,
144
00:07:09,763 --> 00:07:13,331
what if I had this MVC right here. If you look at this MVC,
145
00:07:13,333 --> 00:07:16,067
if you look at the identity inspector for
146
00:07:16,069 --> 00:07:19,604
it, you can see that its class, of course,
147
00:07:19,606 --> 00:07:20,439
is FaceViewController.
148
00:07:20,441 --> 00:07:22,374
Right, you remember our FaceViewController?
149
00:07:22,376 --> 00:07:24,176
It's got all this stuff our face views.
150
00:07:24,178 --> 00:07:27,078
We got all that those gesture recognizer,
151
00:07:27,347 --> 00:07:28,079
all this stuff remember that? So
152
00:07:28,081 --> 00:07:30,749
that makes sense that that's what the controller is for
153
00:07:30,751 --> 00:07:34,219
this. What if I wanted to put all my blinking stuff in
154
00:07:34,221 --> 00:07:37,456
another class? In other words, I didn't want to put blinking
155
00:07:37,458 --> 00:07:39,624
in with all this other FaceViewController,
156
00:07:39,626 --> 00:07:42,127
okay? In other words, what if I wanted a blinking
157
00:07:42,129 --> 00:07:45,831
FaceViewController? Okay, well, you can use inheritance,
158
00:07:45,833 --> 00:07:48,633
this is object-oriented programming. It's not uncommon
159
00:07:48,635 --> 00:07:52,103
to create a subclass of another MVC's controller to
160
00:07:52,105 --> 00:07:54,806
create a controller you want for your MVC.
161
00:07:54,808 --> 00:07:56,942
And that's exactly what I'm gonna do. Okay,
162
00:07:56,944 --> 00:07:59,878
I'm gonna create a new controller here, new File.
163
00:07:59,880 --> 00:08:03,582
Okay, to iOS source, it's a Cocoa Touch Class. But
164
00:08:03,584 --> 00:08:07,252
instead of the subclass being of some UI kit class here,
165
00:08:07,254 --> 00:08:12,390
it's gonna be a subclass of my class, FaceViewController.
166
00:08:12,392 --> 00:08:17,329
Okay, and I'm gonna call it BlinkingFaceViewController,
167
00:08:17,331 --> 00:08:19,865
all right? So, here it is, it's creating it,
168
00:08:19,867 --> 00:08:22,300
putting in all the regular places where it puts it.
169
00:08:22,302 --> 00:08:24,402
Here it is, I don't need any of this stuff, so
170
00:08:24,404 --> 00:08:26,037
I'll get out that out of there. So
171
00:08:26,039 --> 00:08:27,939
I've got this BlinkingFaceViewController,
172
00:08:27,941 --> 00:08:30,542
it's a subclass of FaceViewController. And
173
00:08:30,544 --> 00:08:33,278
in my storyboard, if I went to this and
174
00:08:33,280 --> 00:08:35,046
changed in the identity inspector for
175
00:08:35,048 --> 00:08:38,316
this, changed it from being a FaceViewController to being
176
00:08:38,318 --> 00:08:41,019
a BlinkingFaceViewController. And hit Run,
177
00:08:41,021 --> 00:08:45,857
do you think this would work? Yeah it would work, because
178
00:08:45,859 --> 00:08:48,393
BlinkingFaceViewController inherits everything from
179
00:08:48,395 --> 00:08:51,062
FaceViewController, so it's gonna work just as well.
180
00:08:51,064 --> 00:08:53,365
Cuz, full inheritance of it. So, let's go back here and
181
00:08:53,367 --> 00:08:57,536
remember what face this emotions app here does. So we
182
00:08:57,538 --> 00:08:59,437
can pick our various emotions. You can see it's working
183
00:08:59,439 --> 00:09:02,541
just fine to have this be a BlinkingFaceViewController.
184
00:09:02,543 --> 00:09:03,074
Of course, it doesn't blink,
185
00:09:03,076 --> 00:09:05,644
cuz I haven't put any blinking code in there, but
186
00:09:05,646 --> 00:09:08,914
I am gonna put the blinking code in now. Okay, I'm gonna
187
00:09:08,916 --> 00:09:12,517
put all that blinking code up here in this subclass. So,
188
00:09:12,519 --> 00:09:15,053
the, this blinking thing is pretty simple. It basically
189
00:09:15,055 --> 00:09:19,891
needs a Bool here, which is whether it's blinking or not,
190
00:09:19,893 --> 00:09:22,427
we'll start it out as false, it's not blinking, and
191
00:09:22,429 --> 00:09:24,629
of course every time this changes, we're need,
192
00:09:24,631 --> 00:09:28,900
going to, basically need to start blinking, okay?
193
00:09:28,902 --> 00:09:31,636
If someone says that, you know, if someone the,
194
00:09:31,638 --> 00:09:34,806
if someone says didSet, changes this blinking thing.
195
00:09:34,808 --> 00:09:40,045
So, I need a little private, oops, private var here,
196
00:09:40,047 --> 00:09:41,880
startBlink, and
197
00:09:41,882 --> 00:09:44,282
it's going to just have to start things blinking. And
198
00:09:44,284 --> 00:09:46,818
actually, it's gonna want to look at blinking, though, and
199
00:09:46,820 --> 00:09:50,555
only do the blinking if blinking is true. Okay,
200
00:09:50,557 --> 00:09:53,191
if blinking is not true, it's not gonna want to do that.
201
00:09:53,193 --> 00:09:56,227
Private, oops, this is not func. Private func.
202
00:09:56,229 --> 00:09:59,097
[LAUGH] Okay, so inside the blinking there it's gonna want
203
00:09:59,099 --> 00:10:02,300
to start blinking, we'll talk about how to do startBlink in
204
00:10:02,302 --> 00:10:06,471
a second. Now sort of for the purposes of demo,
205
00:10:06,473 --> 00:10:09,307
but also to talk a little bit about timers here,
206
00:10:09,309 --> 00:10:12,877
I'm actually in my viewDidAppear, okay,
207
00:10:12,879 --> 00:10:17,649
super.viewDidAppear(animated). I'm gonna
208
00:10:17,651 --> 00:10:21,186
start myself blinking. So I'm gonna say blinking = true.
209
00:10:21,188 --> 00:10:23,989
It's a little bit demoware, because you probably wouldn't
210
00:10:23,991 --> 00:10:25,824
want a blinking face controller to always start
211
00:10:25,826 --> 00:10:28,693
blinking as soon as it comes up. You wanna let people, this
212
00:10:28,695 --> 00:10:31,396
is a public var and you wanna let them control it with that.
213
00:10:31,398 --> 00:10:33,965
But it's good for the demo here and
214
00:10:33,967 --> 00:10:37,602
also in viewWillDisappear, I'm gonna do the opposite and
215
00:10:37,604 --> 00:10:41,539
turn it back off. And we'll talk about why I'm gonna do
216
00:10:41,541 --> 00:10:46,911
that in a second. Blinking = false, okay? All right, so
217
00:10:46,913 --> 00:10:49,047
we got this kind of infrastructure for
218
00:10:49,049 --> 00:10:49,914
our blinking right here.
219
00:10:49,916 --> 00:10:51,683
How are we actually gonna do the blinking?
220
00:10:51,685 --> 00:10:54,119
Blinking is really easy. We're gonna start blink,
221
00:10:54,121 --> 00:10:57,455
how do we start a blink in real life? We close our eyes,
222
00:10:57,457 --> 00:11:02,560
right? So, we're gonna have our faceView.eyesOpen = false.
223
00:11:02,562 --> 00:11:06,931
Okay, we just closed our eyes. And now, after a moment,
224
00:11:06,933 --> 00:11:09,868
open them again. That's what we do when we blink, right?
225
00:11:09,870 --> 00:11:12,604
We close them, and then pretty soon after that,
226
00:11:12,606 --> 00:11:12,971
we open them back up again.
227
00:11:12,973 --> 00:11:16,574
That's what a blink is, okay? So that's what we need to do.
228
00:11:16,576 --> 00:11:18,843
Now before we talk about how I'm gonna do this,
229
00:11:18,845 --> 00:11:21,112
let's look at faceView right here. This is interesting,
230
00:11:21,114 --> 00:11:24,249
faceView, where does that come from? Anyone know where that
231
00:11:24,251 --> 00:11:27,886
comes from? Yeah, inherited from this guy,
232
00:11:27,888 --> 00:11:31,389
from faceViewController. If I go back to faceViewController,
233
00:11:31,391 --> 00:11:32,457
here it is right here, faceView.
234
00:11:32,459 --> 00:11:36,961
Notice it's not private, okay? Usually, we make our outlets
235
00:11:36,963 --> 00:11:39,064
private. We didn't happen to make this one private,
236
00:11:39,066 --> 00:11:41,032
might've been an oversight on my part. But
237
00:11:41,034 --> 00:11:43,868
we didn't make it private. One of the interesting things
238
00:11:43,870 --> 00:11:47,505
about Swift is that there's no idea of protected. How many
239
00:11:47,507 --> 00:11:51,409
people know what protected means? Okay, not too many.
240
00:11:51,411 --> 00:11:53,611
So, we know about private and public, right?
241
00:11:53,613 --> 00:11:56,548
Private means you can only use it in this class, public means
242
00:11:56,550 --> 00:11:58,983
you can use it outside the framework you're in.
243
00:11:58,985 --> 00:12:00,485
Everything else in Swift is internal,
244
00:12:00,487 --> 00:12:02,053
which means you can use anything, anywhere,
245
00:12:02,055 --> 00:12:04,989
inside the framework you're in, including your app is kind
246
00:12:04,991 --> 00:12:07,792
of like a framework. So this faceView is really available
247
00:12:07,794 --> 00:12:12,897
to any class that's inside our app, okay? And, so,
248
00:12:12,899 --> 00:12:15,033
that's nice, but, it would be maybe better if
249
00:12:15,035 --> 00:12:17,869
there were another protection class called protected. And,
250
00:12:17,871 --> 00:12:22,707
what that would mean is, only subclasses can use this thing.
251
00:12:22,709 --> 00:12:23,608
Cuz, that's really what I'd like here.
252
00:12:23,610 --> 00:12:27,412
FaceView, I really don't want other classes even in my app,
253
00:12:27,414 --> 00:12:30,014
thinking they can start mucking with my faceView,
254
00:12:30,016 --> 00:12:33,752
okay? But I want to be able to allow people to subclass.
255
00:12:33,754 --> 00:12:35,153
So unfortunately can't do that, so
256
00:12:35,155 --> 00:12:36,988
if you want something to be subclassable, and
257
00:12:36,990 --> 00:12:39,624
it's used in a subclass, you have to leave it internal,
258
00:12:39,626 --> 00:12:43,995
okay? So we're gonna leave that internal, and
259
00:12:43,997 --> 00:12:46,564
that means we can use it here to open the eyes.
260
00:12:46,566 --> 00:12:49,134
Or to close the eyes. Okay, so now how are we going to
261
00:12:49,136 --> 00:12:51,169
do this thing where we wait a moment and open them? Well,
262
00:12:51,171 --> 00:12:54,372
I'm just gonna use Timer, of course, that's why we're here!
263
00:12:54,374 --> 00:12:57,509
So let's, some space here. NSTimer, it's called
264
00:12:57,511 --> 00:13:01,513
ScheduledTimerWithTimeInter- val, notice there's two of
265
00:13:01,515 --> 00:13:04,015
them here. See this one and this one.
266
00:13:04,017 --> 00:13:06,818
We don't want to use this one, this one has a NSInvocation
267
00:13:06,820 --> 00:13:09,320
which is really just a wrapper for a calling a method.
268
00:13:09,322 --> 00:13:11,756
But I'm not gonna show you that, you don't really need to
269
00:13:11,758 --> 00:13:14,592
know it because this one's just as good right here.
270
00:13:14,594 --> 00:13:16,194
This one's target and selector so
271
00:13:16,196 --> 00:13:18,263
this is what we saw on the slides. So here it is.
272
00:13:18,265 --> 00:13:23,434
To make this is a little more easy to see here I'm gonna add
273
00:13:23,436 --> 00:13:26,838
some carat returns here. Okay, so here's our
274
00:13:26,840 --> 00:13:28,640
scheduledTimerWithTimeInter- val.
275
00:13:28,642 --> 00:13:32,143
So, we need a time interval. I'm gonna be a good programmer
276
00:13:32,145 --> 00:13:34,846
here and I'm gonna create a little constant struct here.
277
00:13:34,848 --> 00:13:38,550
I'm gonna call it my BlinkRate and, so
278
00:13:38,552 --> 00:13:45,023
I'll have a static let here which is the ClosedDuration.
279
00:13:45,025 --> 00:13:46,191
Which will have the, I don't know,
280
00:13:46,193 --> 00:13:48,793
you close your eyes for less than half a second.
281
00:13:48,795 --> 00:13:51,963
Something like that. And then we'll have our OpenDuration.
282
00:13:51,965 --> 00:13:54,766
Now this really doesn't wanna be a constant. If you think
283
00:13:54,768 --> 00:13:58,336
about how you blink, you don't blink and then exactly five
284
00:13:58,338 --> 00:14:01,573
seconds later you blink again. Okay might seem like a robot,
285
00:14:01,575 --> 00:14:04,509
unfortunately this is kind of a robot. So this really would
286
00:14:04,511 --> 00:14:06,444
probably would be some sort of function you know,
287
00:14:06,446 --> 00:14:09,380
the kinda I don't know does some statistical distribution
288
00:14:09,382 --> 00:14:12,150
of your blinking, but we're just gonna make it every 2.5
289
00:14:12,152 --> 00:14:14,953
seconds okay. So we can this guy blinking pretty quick.
290
00:14:14,955 --> 00:14:16,721
All right, so he's gonna keep his eyes open for
291
00:14:16,723 --> 00:14:20,592
2.5 seconds, close them for 0.4. So here eyes are closed,
292
00:14:20,594 --> 00:14:23,361
so we want the interval that we're keeping it closed before
293
00:14:23,363 --> 00:14:25,730
we open it again. So this is going to be our
294
00:14:25,732 --> 00:14:30,535
BlinkRate.ClosedDuration, okay. The target is going to
295
00:14:30,537 --> 00:14:33,771
be our self. Now there's a restriction on this self,
296
00:14:33,773 --> 00:14:36,074
the same restriction we had with the other.
297
00:14:36,076 --> 00:14:39,310
A pound sign selector type of APIs,
298
00:14:39,312 --> 00:14:42,080
which is that this class that's receiving this has to
299
00:14:42,082 --> 00:14:46,317
be available to the Objective C run time, okay? And
300
00:14:46,319 --> 00:14:49,921
so that means it essentially has to inherit from MS Object.
301
00:14:49,923 --> 00:14:52,857
Now luckily, self, no problem because we inherit from
302
00:14:52,859 --> 00:14:56,461
faceViewController, which inherits from UIViewController
303
00:14:56,463 --> 00:14:59,397
which inherits eventually from NSObjects. So we're all good
304
00:14:59,399 --> 00:15:03,067
to go here, but just don't get too confused if you try and
305
00:15:03,069 --> 00:15:04,402
do this and it's like it doesn't work.
306
00:15:04,404 --> 00:15:07,405
Because the object you send this message to has to be
307
00:15:07,407 --> 00:15:11,309
Objective C compatible. So the selector we want here,
308
00:15:11,311 --> 00:15:13,344
let's call it, this is the start of the blink, so
309
00:15:13,346 --> 00:15:18,016
I'm gonna call, my selector on the other side end of blink.
310
00:15:18,018 --> 00:15:21,853
So, this is, we're in our BlinkingFaceViewController
311
00:15:21,855 --> 00:15:26,624
here, endBlink I'll call it, the argument on there, okay?
312
00:15:27,127 --> 00:15:29,193
Okay, so, that's what we're gonna, we're gonna call. So,
313
00:15:29,195 --> 00:15:34,299
we're gonna need a method down here, private func endBlink.
314
00:15:34,301 --> 00:15:37,769
It's gonna take that timer as an argument, okay.
315
00:15:37,771 --> 00:15:40,738
But we're gonna end our blink there. UserInfo, that's
316
00:15:40,740 --> 00:15:43,141
a little cookie, I don't really have anything to say.
317
00:15:43,143 --> 00:15:45,310
It's pretty obvious, I open and close my eyes. And
318
00:15:45,312 --> 00:15:50,448
repeat is false. Okay, I can't really have one repeating,
319
00:15:50,450 --> 00:15:52,784
timer, because you don't blink like this.
320
00:15:52,786 --> 00:15:54,585
Eyes closed for one second, eyes open for one second,
321
00:15:54,587 --> 00:15:56,587
eyes closed for one second, you know what I mean,
322
00:15:56,589 --> 00:15:57,221
they're two different timers.
323
00:15:57,223 --> 00:15:59,557
One is the closing one, and one is the opening one.
324
00:15:59,559 --> 00:16:03,294
So I really need Two different timers going back and
325
00:16:03,296 --> 00:16:06,564
forth rather than one just going repeatedly,
326
00:16:06,566 --> 00:16:11,135
okay? Missed that there. Okay, make sense?
327
00:16:11,137 --> 00:16:13,104
Now let's take a look here at this error.
328
00:16:13,106 --> 00:16:15,440
You see the error right here? I put this in here, it's like,
329
00:16:15,442 --> 00:16:18,743
what's the problem here? And it says right here, you have
330
00:16:18,745 --> 00:16:22,246
to add a objc to expose this method to Objective-C.
331
00:16:22,248 --> 00:16:25,650
So I said this had to be an Objective-C compatible method.
332
00:16:25,652 --> 00:16:26,351
Well, what the heck?
333
00:16:26,353 --> 00:16:28,653
Why is it making me put Objective-C there?
334
00:16:28,655 --> 00:16:33,057
And the answer is because I made it private. Okay?
335
00:16:33,059 --> 00:16:37,662
Private methods don't get exposed to Objective-C. So
336
00:16:37,664 --> 00:16:38,463
they inject a C runtime.
337
00:16:38,465 --> 00:16:42,200
As soon as I made that not private, now it worked.
338
00:16:42,435 --> 00:16:46,671
Okay so note that as well. It has to be public method,
339
00:16:46,673 --> 00:16:49,140
okay, or otherwise exposed to objective C. Which you can
340
00:16:49,142 --> 00:16:53,544
do with that at sign over J C as well. Okay, so that's good.
341
00:16:53,546 --> 00:16:56,614
Now another interesting thing about this endBlink here is
342
00:16:56,616 --> 00:16:58,349
that I actually don't need this timer.
343
00:16:58,351 --> 00:17:00,485
Because I don't have any user info,
344
00:17:00,487 --> 00:17:01,619
it's not a repeating timer that I
345
00:17:01,621 --> 00:17:03,921
would want to invalidate. Why do I even need that,
346
00:17:03,923 --> 00:17:05,456
I don't even need that thing. Get that outta there.
347
00:17:05,458 --> 00:17:09,227
And if I get that outta there then I don't need this, okay.
348
00:17:09,229 --> 00:17:13,264
And that's allowed as well. Okay, just like we have
349
00:17:13,266 --> 00:17:16,134
target action, or sometimes we pass the cinder along,
350
00:17:16,136 --> 00:17:20,405
same thing with the timer. All right, so that's it.
351
00:17:20,407 --> 00:17:21,873
What are we gonna do in our endBlink?
352
00:17:21,875 --> 00:17:25,209
In our endBlink, what happens when our blinking is ending?
353
00:17:25,211 --> 00:17:28,112
We got our eyes closed. Now, we open them back up again.
354
00:17:28,114 --> 00:17:28,846
So now we're just gonna say,
355
00:17:28,848 --> 00:17:33,151
faceView dot eyesOpen. Equals true.
356
00:17:33,153 --> 00:17:36,254
And now, we want to queue up another blink.
357
00:17:36,256 --> 00:17:39,424
'Cuz we just want to keep on blinking, right? So I'm going
358
00:17:39,426 --> 00:17:42,427
to do that with another timer. So we're going to copy and
359
00:17:42,429 --> 00:17:45,163
paste this right here. This time, though,
360
00:17:45,165 --> 00:17:47,999
it's our open duration. This is how long the ice cream will
361
00:17:48,001 --> 00:17:50,601
be open until we start blinking again. And instead of
362
00:17:50,603 --> 00:17:55,907
endBlink here, now we're start blinking again, okay. Again,
363
00:17:55,909 --> 00:18:00,411
error because this needs to be public, okay, cuz now we're
364
00:18:00,413 --> 00:18:03,848
calling this one, these guys are calling each other, okay.
365
00:18:03,850 --> 00:18:08,853
That good, everyone understand all that?
366
00:18:09,722 --> 00:18:13,724
Okay, so let's see if that works for us.
367
00:18:17,397 --> 00:18:20,431
All right, here's our guy right here. And sure enough,
368
00:18:20,433 --> 00:18:25,570
he's blinking, okay? And you know, if we go to other ones,
369
00:18:25,572 --> 00:18:30,808
they'll all blink. Got it?
370
00:18:30,810 --> 00:18:33,945
Okay so that's it for timer. Hopefully that's a simple
371
00:18:33,947 --> 00:18:37,215
example to show you how we use timer. So the eye blinking is
372
00:18:37,217 --> 00:18:40,118
not really, we wouldn't really necessarily animation but
373
00:18:40,120 --> 00:18:42,553
it's kind of involved in the animation,
374
00:18:42,555 --> 00:18:45,790
right? And the next demo I show you we're going to
375
00:18:45,792 --> 00:18:47,758
actually animate the opening and closing the eyes.
376
00:18:47,760 --> 00:18:50,027
Because right there the eyes are just popping open and
377
00:18:50,029 --> 00:18:52,997
popping shut, popping open. That's not really animating
378
00:18:52,999 --> 00:18:59,070
them okay? So we'll show that in our next demo. All right
379
00:18:59,072 --> 00:19:04,609
back to our slides here. Okay so
380
00:19:04,611 --> 00:19:07,979
let's do a little overview of the kinds of animation that
381
00:19:07,981 --> 00:19:10,948
are available in iOS. Okay this is most of the kinds.
382
00:19:10,950 --> 00:19:15,520
One is there are three UIView properties that you can
383
00:19:15,522 --> 00:19:19,390
animate the changing of okay which is the frame,
384
00:19:19,392 --> 00:19:21,926
the transform which is like that rotation thing and
385
00:19:21,928 --> 00:19:23,561
the alpha which is the transparency so
386
00:19:23,563 --> 00:19:26,030
you can animate those. We'll talk about doing that.
387
00:19:26,032 --> 00:19:29,367
You can also animate view controller transitions.
388
00:19:29,369 --> 00:19:31,002
You noticed that UINavigationController when
389
00:19:31,004 --> 00:19:34,705
you click to another one it slides into another it kinda,
390
00:19:34,707 --> 00:19:38,876
it animated, like cards sliding in, right? Or
391
00:19:38,878 --> 00:19:42,180
sliding off. So this whole mechanism for building your
392
00:19:42,182 --> 00:19:46,017
own UI navigation controller like things, right,
393
00:19:46,019 --> 00:19:50,888
that have little sub UI view controllers that move in and
394
00:19:50,890 --> 00:19:52,657
out, and we're not going to talk about that at all,
395
00:19:52,659 --> 00:19:54,358
because you're not going to be doing that in this class,
396
00:19:54,360 --> 00:19:57,361
it's a little bit of advanced design, but when you go out in
397
00:19:57,363 --> 00:19:58,930
the real world, you might figure out some way,
398
00:19:58,932 --> 00:20:01,832
you know. I know the Facebook app has a little tray that
399
00:20:01,834 --> 00:20:05,269
slides out from the left. OK? It's something they designed
400
00:20:05,271 --> 00:20:07,004
and it's kinda like U I navigation control, but
401
00:20:07,006 --> 00:20:10,107
it only slides out part way. Okay? So I'm sure they had to
402
00:20:10,109 --> 00:20:14,345
use this mechanism to make that animation happen.
403
00:20:14,347 --> 00:20:17,949
Core animation is a non object oriented,
404
00:20:17,951 --> 00:20:21,552
API that underlies almost all the animation we're gonna talk
405
00:20:21,554 --> 00:20:24,589
about here. Okay? It's the big beat engine
406
00:20:24,591 --> 00:20:28,125
that's gong on underneath all this, it's making it all work.
407
00:20:28,127 --> 00:20:29,760
Unfortunately I don't have time to talk about it,
408
00:20:29,762 --> 00:20:31,596
we're gonna talk about the higher level things that
409
00:20:31,598 --> 00:20:33,664
are build on top of core animation But
410
00:20:33,666 --> 00:20:36,334
you should know it's there. And then a lot of people do
411
00:20:36,336 --> 00:20:39,637
do Core Animation stuff in their final projects. Okay?
412
00:20:39,639 --> 00:20:41,439
It's a good not-covered-in-lecture thing,
413
00:20:41,441 --> 00:20:43,608
and there's things you can do in Core Animation that you
414
00:20:43,610 --> 00:20:47,745
can't do with UIView level animation. Okay? There's
415
00:20:47,747 --> 00:20:51,749
a window from your UIView into the Core Animation world,
416
00:20:51,751 --> 00:20:54,752
which is the layer. If you look and there's a CA layer,
417
00:20:54,754 --> 00:20:58,422
Core Animation layer, that's what CA layer stands for And
418
00:20:58,424 --> 00:20:59,090
there's a property in view for
419
00:20:59,092 --> 00:21:01,659
that, and that's how you can kind of get down to the core
420
00:21:01,661 --> 00:21:04,228
animation layer and start doing core animation things.
421
00:21:04,230 --> 00:21:07,064
Like core animation can do rounded recs on the edges
422
00:21:07,066 --> 00:21:09,133
of your views and things like that. Masks,
423
00:21:09,135 --> 00:21:14,071
all kinds of fun stuff. Okay? If you want to do 3-D,
424
00:21:14,073 --> 00:21:17,275
there's OpenGL. OpenGL is bundled with IOS, so,
425
00:21:17,277 --> 00:21:20,378
how many people here have experience with OpenGL or
426
00:21:20,380 --> 00:21:23,014
doing 3-D? Okay, so a few of you. So you can do that,
427
00:21:23,016 --> 00:21:26,717
full OpenGL support in IOS. There's also something called
428
00:21:26,719 --> 00:21:30,554
SpriteKit, which is like 2.5D. Okay, this is how,
429
00:21:30,556 --> 00:21:34,525
when you make an animated thing out of images that kind
430
00:21:34,527 --> 00:21:37,495
of are overlapping each other and moving around, right?
431
00:21:37,497 --> 00:21:40,097
Some guy walking though a castle and you know,
432
00:21:40,099 --> 00:21:42,233
you find some monster, and he's fighting against it.
433
00:21:42,235 --> 00:21:45,536
And it's not really being drawn in 3D like OpenGL.
434
00:21:45,538 --> 00:21:47,505
It's being drawn with images overlapping, but
435
00:21:47,507 --> 00:21:50,007
they're kind of 3D looking images. And so,
436
00:21:50,009 --> 00:21:53,611
SpriteKit manages not just the overlapping images and
437
00:21:53,613 --> 00:21:56,080
how they all move around each other. But,
438
00:21:56,082 --> 00:21:57,615
it also can do particles, so
439
00:21:57,617 --> 00:22:00,017
you can do things like explosions and fire and
440
00:22:00,019 --> 00:22:01,952
all kinds of things. So, it's a really great thing for
441
00:22:01,954 --> 00:22:06,624
building You know these two and a half D like animated
442
00:22:06,626 --> 00:22:09,293
things, we're not gonna talk anything about that in this
443
00:22:09,295 --> 00:22:12,263
class sorry about it, there's gotta be limits and there's
444
00:22:12,265 --> 00:22:18,102
one of them. There's also for views dynamic animation and
445
00:22:18,104 --> 00:22:20,838
this is essentially animating using physics so
446
00:22:20,840 --> 00:22:23,441
you assign gravity and collision boundaries and
447
00:22:23,443 --> 00:22:27,511
things like that to views, and then you just say go, and
448
00:22:27,513 --> 00:22:30,748
gravity starts pulling on it, and it hits the boundary and
449
00:22:30,750 --> 00:22:33,084
it bounces off and things like that, okay?
450
00:22:33,086 --> 00:22:37,588
So it's physics-based animation of views.
451
00:22:37,590 --> 00:22:38,823
That I am going to talk about again.
452
00:22:38,825 --> 00:22:40,691
I won't have time to get all the way through that, but
453
00:22:40,693 --> 00:22:43,527
I have a big demo on that in the next lecture. But
454
00:22:43,529 --> 00:22:46,564
today I'm going to focus on the UI View animation.
455
00:22:46,566 --> 00:22:50,534
The first one is this simple one where you can change
456
00:22:50,536 --> 00:22:53,037
any of these three properties; frame, transform or
457
00:22:53,039 --> 00:22:58,042
alpha. These are the primary ones you can change.
458
00:22:58,044 --> 00:23:00,678
It will animate the change, the from and
459
00:23:00,680 --> 00:23:04,348
the to, of the values of these three properties. So,
460
00:23:04,350 --> 00:23:06,984
it's done with UIView class methods.
461
00:23:06,986 --> 00:23:11,255
So you say, UIView, dot, animate duration, okay?
462
00:23:11,257 --> 00:23:14,625
And, inside there, you're going to provide a block.
463
00:23:14,627 --> 00:23:15,092
And inside that block,
464
00:23:15,094 --> 00:23:18,729
you're gonna change one of these three things. Okay? And
465
00:23:18,731 --> 00:23:21,065
it's going to animate that change over
466
00:23:21,067 --> 00:23:24,368
time. Okay? And it animates in a very configurable
467
00:23:24,370 --> 00:23:27,304
way, as you will see. What's interesting, though, is, even
468
00:23:27,306 --> 00:23:29,807
though you're going to give a block to these methods and
469
00:23:29,809 --> 00:23:31,375
it's going to animate these changes,
470
00:23:31,377 --> 00:23:34,912
it's going to execute that block you give it immediately.
471
00:23:34,914 --> 00:23:37,548
So these values are going to change instantly to the end
472
00:23:37,550 --> 00:23:40,284
point of your animation. So even while the animation is
473
00:23:40,286 --> 00:23:41,919
happening on screen, they're already changed.
474
00:23:41,921 --> 00:23:44,388
Okay, that's something really important to understand
475
00:23:44,390 --> 00:23:47,158
about how animation works in general, even core animation.
476
00:23:47,160 --> 00:23:48,793
When you change things, they change immediately.
477
00:23:48,795 --> 00:23:52,296
It's, on screen is really just taking your time to show
478
00:23:52,298 --> 00:23:54,532
the user what the world looks like okay.
479
00:23:54,534 --> 00:23:57,201
You're not actually, the animation doesn't change
480
00:23:57,203 --> 00:24:00,404
the things over time, it only shows the change over time.
481
00:24:00,406 --> 00:24:02,306
Okay, important thing to understand there. So
482
00:24:02,308 --> 00:24:04,975
here's the method, or one of the methods, kind of the one
483
00:24:04,977 --> 00:24:07,878
with the most arguments here, animate with duration right,
484
00:24:07,880 --> 00:24:12,183
class function on UI view. You can see here that it has
485
00:24:12,185 --> 00:24:14,718
a time interval for how long this animation's gonna take.
486
00:24:14,720 --> 00:24:17,288
You know, is it gonna take two seconds, half a second, or
487
00:24:17,290 --> 00:24:22,293
whatever? Also, you can delay the start of the animation,
488
00:24:22,295 --> 00:24:24,528
like start this animation two seconds from now.
489
00:24:24,530 --> 00:24:27,198
Why would you ever wanna delay that? Because maybe you're
490
00:24:27,200 --> 00:24:29,633
gonna have some animation going on during that two
491
00:24:29,635 --> 00:24:33,037
seconds, okay? So you might have three or four animations
492
00:24:33,039 --> 00:24:35,739
that you queue up, one of them to start in two seconds,
493
00:24:35,741 --> 00:24:37,475
one starts right now. It takes two seconds,
494
00:24:37,477 --> 00:24:39,877
whatever. You can do that, so that's why you might
495
00:24:39,879 --> 00:24:43,047
wanna delay the start of it, then options we'll talk about
496
00:24:43,049 --> 00:24:45,282
later. Here's that block I'm talking about,
497
00:24:45,284 --> 00:24:48,886
animations this first green one here, takes no arguments,
498
00:24:48,888 --> 00:24:49,587
returns no arguments.
499
00:24:49,589 --> 00:24:52,289
You can put anything in you want in there that changes
500
00:24:52,291 --> 00:24:56,060
the frame, the transform, and the alpha, okay?
501
00:24:56,062 --> 00:25:00,130
And then completion is another block, it has an argument to
502
00:25:00,132 --> 00:25:02,633
the block which is boolean, whether it finished
503
00:25:02,635 --> 00:25:06,704
this animation, you asked it to change to some end point,
504
00:25:06,706 --> 00:25:09,607
if it got there with the animation, then this will be
505
00:25:09,609 --> 00:25:12,510
called with this true. If it got interrupted somehow, and
506
00:25:12,512 --> 00:25:13,644
I'll talk about how that can happen,
507
00:25:13,646 --> 00:25:16,447
then this will get called with this finish being false.
508
00:25:16,449 --> 00:25:19,683
In other words, I wasn't able to complete the animation
509
00:25:19,685 --> 00:25:22,720
of those things you changed. All right, so
510
00:25:22,722 --> 00:25:25,689
here's an example of calling this. Let's say I have a view,
511
00:25:25,691 --> 00:25:28,392
my view, and it has an alpha of 1, in other words it's
512
00:25:28,394 --> 00:25:31,595
fully opaque. And if I get to this piece of code, and
513
00:25:31,597 --> 00:25:35,032
I find that my view is fully opaque, I'm going to make it
514
00:25:35,034 --> 00:25:37,935
fully transparent and then remove it from the super,
515
00:25:37,937 --> 00:25:40,638
from the view hierarchy, remove it from the super view.
516
00:25:40,640 --> 00:25:42,473
Okay, take it right out of there. Okay,
517
00:25:42,475 --> 00:25:45,409
that's what I'm gonna do. So, I'm not gonna,
518
00:25:45,411 --> 00:25:48,479
it's gonna take three seconds for it to fade away. Okay,
519
00:25:48,481 --> 00:25:51,315
I'm gonna be animating Alpha here, but I'm not gonna start
520
00:25:51,317 --> 00:25:53,817
for another two seconds. I don't know why. Okay, and
521
00:25:53,819 --> 00:25:55,619
again, maybe I'm making the view spin around for
522
00:25:55,621 --> 00:25:59,089
a couple seconds before it animates out, I don't know.
523
00:25:59,091 --> 00:26:02,026
Here are some options, for example, here the option I'm
524
00:26:02,028 --> 00:26:05,262
using is curve linear, that means the fade out is going
525
00:26:05,264 --> 00:26:07,998
to happen linearly, okay, it's going to fade
526
00:26:08,000 --> 00:26:09,633
out from 1.0 down to zero,
527
00:26:09,635 --> 00:26:12,803
which is where we're going linearly, evenly.
528
00:26:12,805 --> 00:26:15,539
Okay? Smoothly. There are other curves that you can do,
529
00:26:15,541 --> 00:26:19,910
and I'll talk about that in a second. Here's my block.
530
00:26:19,912 --> 00:26:22,212
In my block, I just set my alpha to 0.
531
00:26:22,214 --> 00:26:25,649
That's where I want it to be eventually, okay? So
532
00:26:25,651 --> 00:26:27,851
I set it to 0. Now again, when I call this,
533
00:26:27,853 --> 00:26:31,589
it immediately sets alpha to 0. Alpha's not going to be set
534
00:26:31,591 --> 00:26:33,924
to 0 in five seconds, it's going to be set to 0 now.
535
00:26:33,926 --> 00:26:36,694
It's just going to appear on screen in five seconds.
536
00:26:36,696 --> 00:26:39,897
And in the completion here, I'm saying 'if $0', which
537
00:26:39,899 --> 00:26:43,267
means if it finished, then remove it from the super view.
538
00:26:43,269 --> 00:26:45,869
So if this animation of the alpha got interrupted,
539
00:26:45,871 --> 00:26:48,872
by some other animation, then I'm not going to remove myself
540
00:26:48,874 --> 00:26:50,341
from the super view, I'm only going to do it if
541
00:26:50,343 --> 00:26:54,979
I make it all the way down to zero. This block, is going
542
00:26:54,981 --> 00:26:58,115
to be executed five seconds from now, or whenever this
543
00:26:58,117 --> 00:27:00,584
animation gets interrupted, if before then. Okay, so
544
00:27:00,586 --> 00:27:05,289
this block is going to be executed later. Notice that
545
00:27:05,291 --> 00:27:09,026
I put 'print("myView.alpha =\(myView.alpha)"' there.
546
00:27:09,028 --> 00:27:12,262
This is going to print 'myView.alpha = 0' because
547
00:27:12,264 --> 00:27:15,232
this 'animateWithDuration' executes this block and
548
00:27:15,234 --> 00:27:18,202
returns immediately. So, myView.alpha is gonna be 0.
549
00:27:18,204 --> 00:27:22,973
Okay? And it's gonna stay 0 all the way through this
550
00:27:22,975 --> 00:27:25,275
five seconds that it takes for this animation to happen
551
00:27:25,277 --> 00:27:27,277
because this animation really has nothing to do,
552
00:27:27,279 --> 00:27:30,981
has no effect on the setting of alpha. It just,
553
00:27:30,983 --> 00:27:35,152
is how we're presenting this change to the user. Okay
554
00:27:35,154 --> 00:27:37,287
let's talk about some of those options. There are tons and
555
00:27:37,289 --> 00:27:39,623
tons of options. I'm not gonna go over all of them for
556
00:27:39,625 --> 00:27:43,293
time reasons here, but you saw a CurveLinear there.
557
00:27:43,295 --> 00:27:46,597
Some other curves like CurveEaseinEaseout,
558
00:27:46,599 --> 00:27:49,333
that means it start off animating it slowly, and
559
00:27:49,335 --> 00:27:51,769
then picks up to a normal speed, and then slows down
560
00:27:51,771 --> 00:27:55,039
again at the end. That's an, curve that we usually use for
561
00:27:55,041 --> 00:27:57,574
moving things. Okay? If you have a view on screen and
562
00:27:57,576 --> 00:27:59,576
you want to move it, so you're changing its frame,
563
00:27:59,578 --> 00:28:02,112
you're animating its frame, you don't really want to go
564
00:28:02,114 --> 00:28:05,015
[SOUND], okay? You kinda want it to pick up speed, move,
565
00:28:05,017 --> 00:28:07,351
and then slow down at the end. Okay?
566
00:28:07,353 --> 00:28:10,721
It's just less abrupt, turns out, to do it that way.
567
00:28:10,723 --> 00:28:13,357
So you can specify that. Things like begin from current
568
00:28:13,359 --> 00:28:16,393
state that's an interesting one. If you have an animation
569
00:28:16,395 --> 00:28:18,862
in motion let's say our animation before, let's say
570
00:28:18,864 --> 00:28:21,932
we're half way through showing the alpha going to zeros so
571
00:28:21,934 --> 00:28:24,668
it's half transparent. And then some other animations
572
00:28:24,670 --> 00:28:27,805
comes along and wants to animate it going to full or
573
00:28:27,807 --> 00:28:31,308
peak. Okay, if the other animation said begin from
574
00:28:31,310 --> 00:28:34,945
current state, then when it starts, it would start at 0.5,
575
00:28:34,947 --> 00:28:39,083
alpha of 0.5. Okay, in other words it would grab onto
576
00:28:39,085 --> 00:28:41,685
the alpha of whatever animations and process.
577
00:28:41,687 --> 00:28:44,888
You can think of this as begin from current state means
578
00:28:44,890 --> 00:28:49,760
use the values of these in the animation world,
579
00:28:49,762 --> 00:28:51,428
not in the real world, because in the real world,
580
00:28:51,430 --> 00:28:55,099
the alpha is zero. It was zero as soon as I set it to zero.
581
00:28:55,101 --> 00:28:56,567
So, if you don't have BeginFromCurrentState,
582
00:28:56,569 --> 00:28:59,203
then this new one is gonna use the real world one, and
583
00:28:59,205 --> 00:29:01,505
it's gonna be going to half transparent,
584
00:29:01,507 --> 00:29:04,041
back to zero, up to one, okay?
585
00:29:04,043 --> 00:29:06,143
Because if you don't put that BeginFromCurrentState,
586
00:29:06,145 --> 00:29:09,480
it pays no attention to what's going on in the animation
587
00:29:09,482 --> 00:29:11,648
alone. AllowsUserInteraction, exactly what you think.
588
00:29:11,650 --> 00:29:13,484
You need to put gesture, you've got some view flying
589
00:29:13,486 --> 00:29:16,587
across the screen, can you touch on it while it's moving?
590
00:29:16,589 --> 00:29:18,589
That kind of stuff, you know auto reversing,
591
00:29:18,591 --> 00:29:21,125
does the animation repeat all these things you can go look
592
00:29:21,127 --> 00:29:25,129
this up in the documentation of UIView. Okay.
593
00:29:25,131 --> 00:29:29,399
Sometimes you wanna make other changes to views, not frame,
594
00:29:29,401 --> 00:29:33,937
transform, or Alpha, okay? And you wanna animate the entire
595
00:29:33,939 --> 00:29:39,576
change in there In, at once with some kind of cool looking
596
00:29:39,578 --> 00:29:42,546
thing on screen. For example, dissolve you'd want
597
00:29:42,548 --> 00:29:46,150
to change to dissolve in from the old what it looked like
598
00:29:46,152 --> 00:29:48,185
before to what it's going to look like have it dissolve
599
00:29:48,187 --> 00:29:50,687
over time. Or have it flip over, like I'm gonna use
600
00:29:50,689 --> 00:29:53,524
an example of a playing card. If you have a playing card and
601
00:29:53,526 --> 00:29:54,925
you change it from face down to face up,
602
00:29:54,927 --> 00:29:58,629
you'd like it to flip. Okay you don't want to just [NOISE]
603
00:29:58,631 --> 00:30:00,197
change to face up, that's very abrupt.
604
00:30:00,199 --> 00:30:02,800
You want it to kind of flip over. Okay. And there's even
605
00:30:02,802 --> 00:30:05,235
a curl up if you have a view that fills the whole screen,
606
00:30:05,237 --> 00:30:07,638
it can curl up from the bottom like it's a piece of
607
00:30:07,640 --> 00:30:08,572
paper that someone is looking under.
608
00:30:08,574 --> 00:30:12,176
Okay you've probably seen that sometimes. You do this with
609
00:30:12,178 --> 00:30:16,480
another U-I view class method called transition with a view.
610
00:30:16,482 --> 00:30:21,485
Kay, you specify the view like a playing card view or
611
00:30:21,487 --> 00:30:27,224
whatever, so this is the view that's changing,
612
00:30:27,226 --> 00:30:30,160
and this is duration of the animation just like we
613
00:30:30,162 --> 00:30:31,128
have the duration of the previous one,
614
00:30:31,130 --> 00:30:33,197
how long it's gonna take the card to flip over or
615
00:30:33,199 --> 00:30:36,934
whatever. More options the same options as before.
616
00:30:36,936 --> 00:30:40,437
Here's the closure okay? This closure this green closure
617
00:30:40,439 --> 00:30:43,974
here it can change anything at once about the view okay?
618
00:30:43,976 --> 00:30:46,476
Because it doesn't have to be restricted to those three that
619
00:30:46,478 --> 00:30:48,478
we know because it's not gonna actually animate any
620
00:30:48,480 --> 00:30:51,348
in between states. It's just gonna take the end state and
621
00:30:51,350 --> 00:30:53,784
the beginning state and flip between the two or
622
00:30:53,786 --> 00:30:56,320
cross dissolve between the two. You see what I mean?
623
00:30:56,322 --> 00:30:58,255
It's not going to show intermediate positions of
624
00:30:58,257 --> 00:31:01,558
the frame moving around or anything like that, okay? Umm,
625
00:31:01,560 --> 00:31:05,128
so this contains anything you want and it also gets executed
626
00:31:05,130 --> 00:31:09,066
immediately, okay, even though the animation will take time
627
00:31:09,068 --> 00:31:11,368
to show the flip, the change gets made immediately.
628
00:31:11,370 --> 00:31:13,904
And then here's completion, the same completion thing that
629
00:31:13,906 --> 00:31:15,906
will let you know whether it finished. This one is almost
630
00:31:15,908 --> 00:31:19,009
always going to finish here, it's hard to interrupt. And
631
00:31:19,011 --> 00:31:22,312
one like this. All right, so here's the playing card
632
00:31:22,314 --> 00:31:24,648
example. I've got my playing card view up here.
633
00:31:24,650 --> 00:31:26,950
I'm gonna take a little less than a second.
634
00:31:26,952 --> 00:31:29,786
I'm using the transition flip from left option.
635
00:31:29,788 --> 00:31:32,489
So, it's gonna flip my card over from the left edge being
636
00:31:32,491 --> 00:31:36,326
lifted up and over. All I'm doing in my block is changing
637
00:31:36,328 --> 00:31:39,363
some attribute of my playing card view. Card is face up
638
00:31:39,365 --> 00:31:42,566
to card is not face up, so I'm toggling card is face up,
639
00:31:42,568 --> 00:31:45,702
now when I do this that's going to change the whole look
640
00:31:45,704 --> 00:31:48,739
of my playing card view from the back of the card to one
641
00:31:48,741 --> 00:31:52,175
of the front, you know, cards face up. So essentially,
642
00:31:52,177 --> 00:31:54,578
the system's gonna take a snapshot of what it looked
643
00:31:54,580 --> 00:31:57,614
like before, take a snapshot of what it looked like after.
644
00:31:57,616 --> 00:32:00,517
And then do a flip animation to show the new thing.
645
00:32:00,519 --> 00:32:04,788
Or cross dissolve or whatever options teaches,
646
00:32:04,790 --> 00:32:07,758
okay. So that's that.
647
00:32:08,327 --> 00:32:11,595
Now if you're animating a change to the view hierarchy,
648
00:32:11,597 --> 00:32:13,230
which is to say you've got a view in there and
649
00:32:13,232 --> 00:32:14,932
you're going to remove it from consumer view and
650
00:32:14,934 --> 00:32:17,868
replace it with another view, okay? You've got a view that
651
00:32:17,870 --> 00:32:21,338
you're gonna hide and unhide some other view in its place.
652
00:32:21,340 --> 00:32:22,506
Then you're going to want to use this one,
653
00:32:22,508 --> 00:32:26,410
transitionFromView(toView So, the fromView is the view
654
00:32:26,412 --> 00:32:30,213
that's in the view hierarchy, that's going to leave or be
655
00:32:30,215 --> 00:32:33,617
hidden. And the toView is the one that is going to appear.
656
00:32:33,619 --> 00:32:36,586
All right? And here's the time, and here's the options.
657
00:32:36,588 --> 00:32:38,889
And here's the completion. Okay? Now if you use
658
00:32:38,891 --> 00:32:40,991
the option Show Hide Transition Views,
659
00:32:40,993 --> 00:32:43,327
then instead of removing it from view hierarchy and
660
00:32:43,329 --> 00:32:44,962
adding the other one at the same place,
661
00:32:44,964 --> 00:32:46,863
it'll actually just set dot hidden. Right?
662
00:32:46,865 --> 00:32:50,400
Remember the dot hidden of our on a view, which makes it just
663
00:32:50,402 --> 00:32:52,002
not appear. It's still in the view hierarchy, but
664
00:32:52,004 --> 00:32:55,772
doesn't appear. This will set the .hidden of one, clear
665
00:32:55,774 --> 00:32:58,475
the .hidden of one and, set the .hidden of one to true and
666
00:32:58,477 --> 00:33:04,114
tip the other one to false so that it appears. Okay. So,
667
00:33:04,116 --> 00:33:05,582
that's basically View Animation.
668
00:33:05,584 --> 00:33:07,317
So let me show you a demo of that.
669
00:33:07,319 --> 00:33:08,585
And the demo we're going to do,
670
00:33:08,587 --> 00:33:09,453
two parts here in Face View.
671
00:33:09,455 --> 00:33:13,123
One, we're gonna make our blinking actually, be smooth.
672
00:33:13,125 --> 00:33:14,958
And to do that we're gonna use the flip. But
673
00:33:14,960 --> 00:33:17,527
instead of flip from left, I'm gonna do flip from top.
674
00:33:17,529 --> 00:33:19,429
As you can imagine, if you had the eye open and
675
00:33:19,431 --> 00:33:22,099
you flipped from top, it would, you know,
676
00:33:22,101 --> 00:33:24,968
more smoothly look like it's blinking. And
677
00:33:24,970 --> 00:33:27,571
then for the head shake, so I'm gonna have
678
00:33:27,573 --> 00:33:30,273
this face. It's only 2D, so to shake its head it has to turn
679
00:33:30,275 --> 00:33:34,044
its head sideways like this. It's gonna shake its head,
680
00:33:34,046 --> 00:33:34,244
and to do that,
681
00:33:34,246 --> 00:33:36,880
I'm going to use the things where I change the transform,
682
00:33:36,882 --> 00:33:38,849
which is the rotation of that view. Right,
683
00:33:38,851 --> 00:33:41,518
remember transform is scale, rotation, and translation.
684
00:33:41,520 --> 00:33:43,620
So I'm going to change that to make it shake its head,
685
00:33:43,622 --> 00:33:46,556
basically. Okay, now that's an interesting one because
686
00:33:46,558 --> 00:33:49,159
there's actually multiple moves there, and we're going
687
00:33:49,161 --> 00:33:53,096
to have to chain them together to make it shakes its head.
688
00:33:53,332 --> 00:33:58,135
All right, so let's go back over to A face view,
689
00:33:58,137 --> 00:34:01,004
same place we were there. Now,
690
00:34:01,006 --> 00:34:05,675
to make face view eyes blink, all right, right here.
691
00:34:05,677 --> 00:34:11,281
These eyes need to be views. Okay, because I'm going to use
692
00:34:11,283 --> 00:34:14,985
view animation. So if I want them to flip, okay, they're
693
00:34:14,987 --> 00:34:18,221
going to have to be views. So I have to change my code. It
694
00:34:18,223 --> 00:34:22,325
will turn these eyes into sub views of my face view. Now,
695
00:34:22,327 --> 00:34:25,395
it's really not gonna change the face view that much, okay.
696
00:34:25,397 --> 00:34:27,564
Let's see what it's really like to do that. So
697
00:34:27,566 --> 00:34:30,934
to save a little bit of time, I create an a Eyeview which
698
00:34:30,936 --> 00:34:33,070
we're gonna look at don't worry, okay. So
699
00:34:33,072 --> 00:34:34,504
this is Eyeview. It's just going to be
700
00:34:34,506 --> 00:34:38,775
a view that represents one of these eyes, okay. So,
701
00:34:38,777 --> 00:34:42,546
let's take a look at the eye view. Here it is right here.
702
00:34:42,548 --> 00:34:44,781
The eye view, this code in eye view is
703
00:34:44,783 --> 00:34:47,717
identical to the code I used to have in face view, or still
704
00:34:47,719 --> 00:34:49,719
have in face view which we're going to have to get rid of,
705
00:34:49,721 --> 00:34:52,022
which is this path for eye. Remember path for
706
00:34:52,024 --> 00:34:55,092
eye? We got either a circle, or it was a line, depending
707
00:34:55,094 --> 00:34:57,694
on whether it was open or closed. If you look at this
708
00:34:57,696 --> 00:35:01,264
code here path for eye, it looks just like this.
709
00:35:01,266 --> 00:35:04,101
Okay, now I had to bring over line with color and
710
00:35:04,103 --> 00:35:07,671
eyes open as VARs in my eye view, which I'll have to set
711
00:35:07,673 --> 00:35:10,006
those from here in my face view. Okay, so
712
00:35:10,008 --> 00:35:12,609
I don't need this path for eye anymore, because I'm going to
713
00:35:12,611 --> 00:35:16,880
use subviews to draw my eyes. This is also good, another
714
00:35:16,882 --> 00:35:20,717
way to show you using subviews of other views in code and
715
00:35:20,719 --> 00:35:23,320
how we do that. All right, so I don't need path for i,
716
00:35:23,322 --> 00:35:27,057
I also don't need to do path for i stroke over here.
717
00:35:27,059 --> 00:35:30,227
Because I'm not drawing my i's in my drawRect anymore,
718
00:35:30,229 --> 00:35:34,631
I'm letting a sub-view of mine draw them. Okay? All right, so
719
00:35:34,633 --> 00:35:38,235
how we gonna do this, thing here? So I have a little,
720
00:35:38,237 --> 00:35:41,471
again a little code here. That will, this little piece of
721
00:35:41,473 --> 00:35:44,341
code right here okay this is how we're gonna do this. One,
722
00:35:44,343 --> 00:35:47,377
I'm creating a couple of vars one for the left eye one for
723
00:35:47,379 --> 00:35:52,015
the right eye. It's an EyeView right EyeView is just a UIView
724
00:35:52,017 --> 00:35:55,519
okay that draws those eyes. So it's an EyeView, and
725
00:35:55,521 --> 00:35:58,889
I'm creating it by calling this method create eye
726
00:35:58,891 --> 00:36:02,159
on myself. Now, I'm gonna take a time out
727
00:36:02,161 --> 00:36:04,361
here to talk about these lazys right here.
728
00:36:04,363 --> 00:36:07,831
If I take these lazys out, this will generate an error.
729
00:36:07,833 --> 00:36:11,801
Can anyone tell me why that's an error right there?
730
00:36:13,639 --> 00:36:17,841
Nobody knows? What? >> [INAUDIBLE]
731
00:36:17,843 --> 00:36:18,742
>> Yes. Exactly.
732
00:36:18,744 --> 00:36:22,012
We're in initialization right here. We're initializing these
733
00:36:22,014 --> 00:36:25,749
during initialization, self is not fully initialized.
734
00:36:25,751 --> 00:36:29,452
We can't send it any methods. We can't send create eye to
735
00:36:29,454 --> 00:36:32,889
self right here in the middle of initialization. Okay?
736
00:36:32,891 --> 00:36:34,958
This is part of this class being initialized.
737
00:36:34,960 --> 00:36:36,526
Until the class is fully initialized,
738
00:36:36,528 --> 00:36:39,362
we can't call our own method. That's why putting,
739
00:36:39,364 --> 00:36:42,332
by the way the error message you get in here
740
00:36:42,334 --> 00:36:45,001
is kinda cryptic. So, kinda what it'd look like.
741
00:36:45,003 --> 00:36:47,804
So if you see something like this, all right?
742
00:36:47,806 --> 00:36:50,774
Then you might get the idea, maybe it's that thing where
743
00:36:50,776 --> 00:36:54,377
I'm trying to call a method on myself while I'm initializing.
744
00:36:54,379 --> 00:36:59,015
So why does putting lazy in here fix it? Because lazy
745
00:36:59,017 --> 00:37:04,020
means that the initialization here doesn't happen until
746
00:37:04,022 --> 00:37:08,358
someone asks for this var. Okay, until someone asks for
747
00:37:08,360 --> 00:37:10,260
the leftEye or asks for the rightEye,
748
00:37:10,262 --> 00:37:13,330
these =self.createEye are not gonna happen, and
749
00:37:13,332 --> 00:37:14,497
no one is allowed to ask for
750
00:37:14,499 --> 00:37:20,103
these vars until this thing is fully initialized. So,
751
00:37:20,105 --> 00:37:20,403
all is well.
752
00:37:20,405 --> 00:37:23,573
Because now we can call this createEye to initialize it. So
753
00:37:23,575 --> 00:37:27,711
you see how lazy gets you out of that little conundrum of,
754
00:37:27,713 --> 00:37:30,680
initializing there? And don't forget also you want to put
755
00:37:30,682 --> 00:37:34,651
self dot right here when you're doing a createEye.
756
00:37:34,653 --> 00:37:36,786
Okay? If you're gonna say this equals, you need to make it
757
00:37:36,788 --> 00:37:39,389
clear in your initialization that you're accessing your
758
00:37:39,391 --> 00:37:44,394
self. This creates each of these two eyes.
759
00:37:44,396 --> 00:37:47,264
What does this create eye look like. Let's just create a new
760
00:37:47,266 --> 00:37:49,699
eye view. I'm just calling the initializer here.
761
00:37:49,701 --> 00:37:53,737
I'm going to set it to be not opaque, because eye view
762
00:37:53,739 --> 00:37:56,439
doesn't draw background or have any background set, so
763
00:37:56,441 --> 00:37:57,741
if there was something behind my eye view,
764
00:37:57,743 --> 00:38:00,510
maybe some eye shadow eyeliner or something,
765
00:38:00,512 --> 00:38:04,648
it would show through. Also, I got to transfer the color and
766
00:38:04,650 --> 00:38:06,516
the line with on to the eye view, and
767
00:38:06,518 --> 00:38:09,419
in fact, I need to do this not only here when I create it,
768
00:38:09,421 --> 00:38:12,822
but I probably want to do up here if someone sets the color
769
00:38:12,824 --> 00:38:16,393
right here. In addition to doing set needs display,
770
00:38:16,395 --> 00:38:17,861
I probably want to set my left
771
00:38:17,863 --> 00:38:19,362
eye's color equal to the color and
772
00:38:19,364 --> 00:38:24,768
I want to set my right eye's Color equal to the color. And
773
00:38:24,770 --> 00:38:26,436
same thing here with line width,
774
00:38:26,438 --> 00:38:29,005
in addition to set needs display left eye dot line
775
00:38:29,007 --> 00:38:34,177
width goes to the line width. And right eye dot line width
776
00:38:34,179 --> 00:38:38,548
goes to the line width, and interestingly,
777
00:38:38,550 --> 00:38:42,085
eyes open. Of course, we also want to pass that along,
778
00:38:42,087 --> 00:38:45,021
but we don't actually need set needs display anymore for
779
00:38:45,023 --> 00:38:50,760
eyes open, do we? Because eyes open is no longer drawn by us.
780
00:38:50,762 --> 00:38:53,730
So if it changed, the eye view is the thing that
781
00:38:53,732 --> 00:38:53,763
here we just need to say lefteye.eyesopen = eyesOpen.
782
00:38:53,765 --> 00:38:59,102
needs to change, so
783
00:38:59,104 --> 00:39:02,906
And rightEye.eyesOpen = eyesOpen,
784
00:39:02,908 --> 00:39:08,778
okay? Since there and don't understand why we don't need
785
00:39:08,780 --> 00:39:10,914
setNeedsDisplay? Because we're not drawing the eyes anymore.
786
00:39:10,916 --> 00:39:15,151
We have a subview that does it for us now. Okay, so
787
00:39:15,153 --> 00:39:16,286
here we've got the eye created.
788
00:39:16,288 --> 00:39:18,621
Now after we've set the color and line width,
789
00:39:18,623 --> 00:39:21,991
then we're just going to add it as a subview of ourselves,
790
00:39:21,993 --> 00:39:22,892
which are great, okay. Now,
791
00:39:22,894 --> 00:39:27,530
a little bit of a problem here, where is this eye? Okay,
792
00:39:27,532 --> 00:39:28,932
we haven't specified where this eye.
793
00:39:28,934 --> 00:39:31,735
We created it with this initializer which doesn't
794
00:39:31,737 --> 00:39:35,105
specify the frame. So, it's like, we don't even know where
795
00:39:35,107 --> 00:39:38,041
this eye is. It's probably at zero, zero and it's also size
796
00:39:38,043 --> 00:39:40,677
zero, zero. So, we obviously need to position and
797
00:39:40,679 --> 00:39:44,514
size each of these two eyes. Okay, to make this work. So
798
00:39:44,516 --> 00:39:48,218
I created this little function right here to position an eye
799
00:39:48,220 --> 00:39:51,588
okay, and it just takes the eye you want to position and
800
00:39:51,590 --> 00:39:54,824
where you want the center of the eye to be. Okay, now it
801
00:39:54,826 --> 00:39:57,794
knows the size the eye is supposed to be because it has
802
00:39:57,796 --> 00:39:59,662
this skull radius to eye radius thing, so
803
00:39:59,664 --> 00:40:02,665
it's setting the size to that. And it creates a rectangle
804
00:40:02,667 --> 00:40:06,336
that's initially in the upper left with the proper size. And
805
00:40:06,338 --> 00:40:10,206
then it sets the eye center to be whatever center you wanted,
806
00:40:10,208 --> 00:40:13,309
okay? So, trivial little piece of code that just positions
807
00:40:13,311 --> 00:40:15,945
the eye. So now the question is,
808
00:40:15,947 --> 00:40:19,482
where do we call this position eye? Okay,
809
00:40:19,484 --> 00:40:24,120
where do we set the position of these eyes. I can't do it
810
00:40:24,122 --> 00:40:28,024
here, this create eye is could be happening any time.
811
00:40:28,026 --> 00:40:29,993
I don't what my bounds are at this point.
812
00:40:29,995 --> 00:40:32,395
Might not even be, my bounds might be zero zero also, so
813
00:40:32,397 --> 00:40:35,665
I don't even know where my eye goes, so that's no good.
814
00:40:35,667 --> 00:40:40,437
There's no view did layout sub views, okay. This is a UI
815
00:40:40,439 --> 00:40:44,841
view, not UI view controller. Okay, so there's no view to,
816
00:40:44,843 --> 00:40:47,010
there's no view controller life cycle. We're in a view,
817
00:40:47,012 --> 00:40:49,813
not a view controller. So, we can't do that. I can't call it
818
00:40:49,815 --> 00:40:52,015
from my controller. Here's my controller.
819
00:40:52,017 --> 00:40:55,552
Because my controller has no idea that faceView is using
820
00:40:55,554 --> 00:41:00,190
a subview to draw its eye. If you go look back in face view,
821
00:41:00,192 --> 00:41:03,493
all this stuff about the eye, it's all private. Private.
822
00:41:03,495 --> 00:41:06,162
Private. It's all private. So there's absolutely no way that
823
00:41:06,164 --> 00:41:10,934
my controller can reach down into me and do that. This
824
00:41:10,936 --> 00:41:14,337
is going to show you how you find out when your view needs
825
00:41:14,339 --> 00:41:19,476
to lay out it's subviews. It's a method called conveniently.
826
00:41:19,478 --> 00:41:21,711
layoutSubviews(). Okay? So,
827
00:41:21,713 --> 00:41:25,949
layoutSubviews() is the method in view that's called whenever
828
00:41:25,951 --> 00:41:29,686
the system wants the view to lay it's subviews out. Okay?
829
00:41:29,688 --> 00:41:32,255
And those two eyes are subviews of ours. So we get to
830
00:41:32,257 --> 00:41:36,926
lay them out. Okay? So, we'll call super.layoutSubviews().
831
00:41:36,928 --> 00:41:37,961
Always nice to do that,
832
00:41:37,963 --> 00:41:38,528
definitely wanna do that for
833
00:41:38,530 --> 00:41:41,564
that. And then we're gonna position these eyes so
834
00:41:41,566 --> 00:41:44,367
we're just going to call it positionEye with position to
835
00:41:44,369 --> 00:41:48,771
leftEye. Where does the leftEye center belong.
836
00:41:48,773 --> 00:41:51,207
Actually, believe it or not, from our old code,
837
00:41:51,209 --> 00:41:54,978
we have this getEyeCenter method here, okay. So
838
00:41:54,980 --> 00:41:57,580
we'll just use that that tells us the center of the eye.
839
00:41:57,582 --> 00:42:01,251
Awesome. So we'll say GetEyeCenter,
840
00:42:01,253 --> 00:42:04,654
the left eye. Okay? And we'll do the same thing for
841
00:42:04,656 --> 00:42:12,428
the right eye. Okay? There we go.
842
00:42:12,430 --> 00:42:14,030
So be don't confused in the difference between layout
843
00:42:14,032 --> 00:42:16,466
subviews and view did layout subviews, okay?
844
00:42:16,468 --> 00:42:18,701
If you did layout subviews as part of the view controller
845
00:42:18,703 --> 00:42:20,537
life cycle, that's a view controller thing.
846
00:42:20,539 --> 00:42:23,573
Layout subviews is a view method where a view's
847
00:42:23,575 --> 00:42:27,377
being asked to actually lay it's subviews out, okay? Now,
848
00:42:27,379 --> 00:42:31,714
a lot of times your subviews get laid out by auto layout.
849
00:42:31,716 --> 00:42:34,150
Right, in that case you don't need to do anything in layout
850
00:42:34,152 --> 00:42:38,121
subviews. But here these eyes, we can't really put an auto
851
00:42:38,123 --> 00:42:40,690
layout for the eyes, maybe we could I'd have to
852
00:42:40,692 --> 00:42:42,425
think about that actually, it might be possible,
853
00:42:42,427 --> 00:42:44,894
believe it or not, but we're just going to do it in code,
854
00:42:44,896 --> 00:42:46,863
so you can learn how to do it in code as well, and so
855
00:42:46,865 --> 00:42:48,798
when layout subjects happen, we're just always going to be
856
00:42:48,800 --> 00:42:51,067
moving our eyes to the right thing. Layout subjects is
857
00:42:51,069 --> 00:42:53,503
always going to be called if our bounds changed. Because of
858
00:42:53,505 --> 00:42:55,838
our balance change, we clearly need to lay our subviews out.
859
00:42:55,840 --> 00:42:59,075
This ads of view right here is gonna call layout subviews,
860
00:42:59,077 --> 00:43:01,010
because layout subviews to be called. Because anytime you
861
00:43:01,012 --> 00:43:07,150
change your subviews you gotta lay them out again, okay?
862
00:43:07,152 --> 00:43:12,221
All right, so see if anything else we wanna do here.
863
00:43:12,223 --> 00:43:15,558
I think that's pretty much everything. Hopefully
864
00:43:15,560 --> 00:43:19,596
this will work. Let's see if this works. I just want to
865
00:43:19,598 --> 00:43:21,397
make sure I haven't broken anything. Obviously,
866
00:43:21,399 --> 00:43:24,067
we haven't done any animation for the eye blinking here.
867
00:43:24,069 --> 00:43:28,905
But hopefully, we've replaced our view. Or our eyes, rather,
868
00:43:28,907 --> 00:43:32,809
with a view. And it looks like it's working, okay? Got these.
869
00:43:32,811 --> 00:43:33,309
Let's see if we can rotate. Yep,
870
00:43:33,311 --> 00:43:37,080
it's all still working. Okay, so we haven't broken anything.
871
00:43:37,082 --> 00:43:41,150
So now, we want to make these eyes animate closing and
872
00:43:41,152 --> 00:43:45,254
opening, okay, by using that transition flip from top,
873
00:43:45,256 --> 00:43:46,756
okay? So, how are we going to do that?
874
00:43:46,758 --> 00:43:48,891
So I'm going to do all that in the eye view,
875
00:43:48,893 --> 00:43:50,460
I am going to be object oriented here.
876
00:43:50,462 --> 00:43:53,796
The animation of the eye, that's an eye thing so
877
00:43:53,798 --> 00:43:54,597
it should be in the eye view,
878
00:43:54,599 --> 00:43:56,199
that shouldn't be in the face view. Okay,
879
00:43:56,201 --> 00:44:00,203
to be in the EyeView. So we're gonna animate this thing right
880
00:44:00,205 --> 00:44:04,941
here, this eyesOpening, okay. We want to animate eyesOpen.
881
00:44:04,943 --> 00:44:06,142
So, how are we gonna animate eyesOpen?
882
00:44:06,144 --> 00:44:10,213
Well, basically anytime an eyesOpen gets set, okay,
883
00:44:10,215 --> 00:44:12,815
we need to set it in an animated way. So I'd
884
00:44:12,817 --> 00:44:15,685
almost like to put something in didSet, except for
885
00:44:15,687 --> 00:44:19,322
didSet would be too late, because it already got set
886
00:44:19,324 --> 00:44:21,591
Okay, I have to animate the setting of it.
887
00:44:21,593 --> 00:44:23,760
I have to provide a little closure there, right,
888
00:44:23,762 --> 00:44:27,063
to that animate method that does the setting of it. So
889
00:44:27,065 --> 00:44:28,431
it did set a little too late.
890
00:44:28,433 --> 00:44:32,769
So I'm really gonna have to have eyesOpen via var
891
00:44:33,772 --> 00:44:38,341
that has its own get and set. So
892
00:44:38,343 --> 00:44:42,745
that in this set right here I can animate the setting, okay.
893
00:44:42,747 --> 00:44:46,015
But I still need storage for the eyes open so here's what
894
00:44:46,017 --> 00:44:48,351
I'm gonna do, this trick. If you ever find yourself in
895
00:44:48,353 --> 00:44:52,221
a situation where you need to do something when something,
896
00:44:52,223 --> 00:44:55,024
you need to animate the setting of something or
897
00:44:55,026 --> 00:44:57,493
otherwise be Involved in the actual setting.
898
00:44:57,495 --> 00:45:00,530
Not the react to the setting, this would didSet is for.
899
00:45:00,532 --> 00:45:03,800
But you actually have to do the setting of it. And you ni,
900
00:45:03,802 --> 00:45:07,770
make a public var like this that's computed. You can take
901
00:45:07,772 --> 00:45:09,906
the storage and put an under bar in front of it.
902
00:45:09,908 --> 00:45:12,475
That's kinda the convention we use to mean this is
903
00:45:12,477 --> 00:45:16,446
the storage for another property that is computed.
904
00:45:16,448 --> 00:45:20,316
Okay? We just call underbar. So I still have_eyesOpen.
905
00:45:20,318 --> 00:45:24,754
It's just that I'm gonna set this, in here, animated.
906
00:45:24,756 --> 00:45:29,759
Right? I'm gonna, say _eyesOpen = newValue in here.
907
00:45:29,761 --> 00:45:33,129
But I'm gonna have to animate this happening. Now the get is
908
00:45:33,131 --> 00:45:36,799
just return_eyesOpen I don't have to animate the getting of
909
00:45:36,801 --> 00:45:40,970
this value. But this I have to animate. Okay, so I'm going to
910
00:45:40,972 --> 00:45:46,075
animate this of course using UI view, transition with view.
911
00:45:46,077 --> 00:45:48,978
Okay so here's transition, see there's the with view on and
912
00:45:48,980 --> 00:45:52,315
here's the from view two view. So I'm going to use the with
913
00:45:52,317 --> 00:45:55,051
view. And we'll do the same thing You do when there's
914
00:45:55,053 --> 00:45:58,354
lots of arguments like this so you can see them all.
915
00:46:03,194 --> 00:46:07,096
Okay, so we just have to animate the setting of this.
916
00:46:07,098 --> 00:46:12,301
Now, let's start with the actual setting of this. That
917
00:46:12,303 --> 00:46:15,004
happens here in this block. You see this animations block,
918
00:46:15,006 --> 00:46:17,673
right here? So I'm just going to double click on this block.
919
00:46:17,675 --> 00:46:20,843
Here it is. And I'm gonna put, what I want to happen,
920
00:46:20,845 --> 00:46:25,481
inside here. See? So I'm animating this change. Okay.
921
00:46:25,483 --> 00:46:28,985
This is gonna complain that this needs to be self. That's
922
00:46:28,987 --> 00:46:34,056
because this is a closure. Okay? So we have to be clear.
923
00:46:34,058 --> 00:46:36,659
Now, is there any problem with memory cycles here?
924
00:46:36,661 --> 00:46:40,563
No, because this animation's only going to take
925
00:46:40,565 --> 00:46:42,532
less than a second and this this closure will be gone,
926
00:46:42,534 --> 00:46:45,568
so there's no worries about someone holding onto it and
927
00:46:45,570 --> 00:46:49,739
it pointing back to us and keeping a cycle there. So
928
00:46:49,741 --> 00:46:53,976
what view are we doing this in? We're doing this in self,
929
00:46:53,978 --> 00:46:57,780
it's the eye view itself that's being animated,
930
00:46:57,782 --> 00:47:02,218
it's being flipped over. And the time interval? Well,
931
00:47:02,220 --> 00:47:05,521
we know that we close our eyes for 0.4, in 0.4 seconds, for
932
00:47:05,523 --> 00:47:09,192
0.4 seconds. So, our time of causing the,
933
00:47:09,194 --> 00:47:11,160
animating the change better be less than that.
934
00:47:11,162 --> 00:47:13,996
So, maybe we'll say 0.2 seconds?
935
00:47:13,998 --> 00:47:16,866
Something like that. We could play with these numbers.
936
00:47:16,868 --> 00:47:18,768
Again, I should make a constant for this, but for
937
00:47:18,770 --> 00:47:22,004
time, I won't do it. The animation options here,
938
00:47:22,006 --> 00:47:26,475
we talked about. We want this transition, sure I find out
939
00:47:26,477 --> 00:47:29,312
what it's called here. Transition flip from top,
940
00:47:29,314 --> 00:47:35,651
I think it's called, yes. TransitionFlipFromTop, okay.
941
00:47:35,987 --> 00:47:38,221
The addition, and we could put other ones in here. Maybe we,
942
00:47:38,223 --> 00:47:43,092
you know, dot curve linear, or whatever. Put whatever
943
00:47:43,094 --> 00:47:48,064
other options we want in here. And then completion. Once this
944
00:47:48,066 --> 00:47:51,734
thing has closed we don't need to do anything. So
945
00:47:51,736 --> 00:47:57,540
my completion here is nil. Everybody cool with that?
946
00:47:57,542 --> 00:48:04,347
All right, let's see it work. All right, there it is.
947
00:48:04,349 --> 00:48:08,517
So you see how the eyes kind of, see how they're kind of
948
00:48:08,519 --> 00:48:11,787
squinching closed instead of jumping closed?
949
00:48:11,789 --> 00:48:18,461
Yeah? All right. So it's getting better by the second.
950
00:48:18,463 --> 00:48:20,363
All right, now let's do this head shake.
951
00:48:20,365 --> 00:48:22,131
So now I want this little head to, to shake.
952
00:48:22,133 --> 00:48:24,667
I don't know, to say, like he's saying no or something.
953
00:48:24,669 --> 00:48:26,702
I'm not sure what. But let's do the head shake. So
954
00:48:26,704 --> 00:48:30,239
the head shake, we're going to do by changing one of those
955
00:48:30,241 --> 00:48:31,274
three special properties,
956
00:48:31,276 --> 00:48:34,110
namely the transform. Okay, we're going to have
957
00:48:34,112 --> 00:48:35,945
the transform of that thing changed.
958
00:48:35,947 --> 00:48:36,779
And I'm gonna do that, mm,
959
00:48:36,781 --> 00:48:38,481
I'm gonna do that in my FaceView right here.
960
00:48:38,483 --> 00:48:40,816
Okay, it doesn't really have anything to do with blinking,
961
00:48:40,818 --> 00:48:42,285
so there's no reason to put it there. So
962
00:48:42,287 --> 00:48:46,422
I can put it in my FaceView. And so let's do it so that
963
00:48:46,424 --> 00:48:50,760
it shakes its head when we tap on it. Right now, when we tap
964
00:48:50,762 --> 00:48:54,630
on our FaceView, it does this, toggleEyes, right. If we
965
00:48:54,632 --> 00:48:56,365
go look at our storyboard right here and
966
00:48:56,367 --> 00:48:59,201
we look at this Tap Gesture Recognizer and right-click
967
00:48:59,203 --> 00:49:01,737
on it, you see that toggleEye is doing toggleEyes in
968
00:49:01,739 --> 00:49:04,840
our FaceViewController. So I'm gonna disconnect that and
969
00:49:04,842 --> 00:49:07,977
create a new thing, which is gonna be a head shake. Okay,
970
00:49:07,979 --> 00:49:11,881
so let's do a head shake. Now, I want to put head shake into
971
00:49:11,883 --> 00:49:14,917
the FaceViewController superclass, okay?
972
00:49:14,919 --> 00:49:17,720
I do not want head shake in BlinkingFaceViewController,
973
00:49:17,722 --> 00:49:20,389
because it's not a blinking thing, it's nothing to do with
974
00:49:20,391 --> 00:49:23,559
blinking, okay. But when I have this on automatic up
975
00:49:23,561 --> 00:49:27,763
here, okay, it picks the BlinkingFaceViewController.
976
00:49:27,765 --> 00:49:33,970
So how can I Ctrl+drag to put this in the superclass?
977
00:49:33,972 --> 00:49:36,772
The answer is, just go to Manual. Okay,
978
00:49:36,774 --> 00:49:37,807
if you go to Manual over here and
979
00:49:37,809 --> 00:49:41,243
pick the FaceViewController and then you try to Ctrl+drag
980
00:49:41,245 --> 00:49:44,413
in here, we'll put it right near toggle eyes, right here,
981
00:49:44,415 --> 00:49:49,018
it's going to work. Because Xcode is smart enough to know
982
00:49:49,020 --> 00:49:51,921
that even though this is a BlinkingFaceViewController
983
00:49:51,923 --> 00:49:56,058
you're connecting it to one of its superclasses.
984
00:49:56,060 --> 00:50:00,129
Okay, so we'll make a tap gesture action here,
985
00:50:00,131 --> 00:50:04,100
we'll call it headShake. Okay, to tap gesture, right?
986
00:50:04,102 --> 00:50:08,571
There's our tap gesture right there. Let's go ahead and
987
00:50:08,573 --> 00:50:11,374
get the wider screen here. So,
988
00:50:11,376 --> 00:50:14,377
like a real wide screen. All right, so here's headShake.
989
00:50:14,379 --> 00:50:15,778
This is going to happen when we tap,
990
00:50:15,780 --> 00:50:19,448
okay? So here's where we need to modify our transform,
991
00:50:19,450 --> 00:50:21,350
and we want it, we know we want it to be animated,
992
00:50:21,352 --> 00:50:24,086
so let's start right off the bat by doing our
993
00:50:24,088 --> 00:50:25,988
UIView.animateWithDuration. Now,
994
00:50:25,990 --> 00:50:29,191
you'll notice there's quite a few animateWithDurations here.
995
00:50:29,193 --> 00:50:34,296
There's even one down here that has springiness in it so
996
00:50:34,298 --> 00:50:36,465
that you can animate things springing,
997
00:50:36,467 --> 00:50:41,103
things like that. But we're gonna pick the simplest one,
998
00:50:41,105 --> 00:50:44,373
which is right here, almost [INAUDIBLE].
999
00:50:44,375 --> 00:50:49,645
Let's do the same thing where we put this on separate lines.
1000
00:50:50,481 --> 00:50:52,381
All right, so here's our animateWithDuration.
1001
00:50:52,383 --> 00:50:56,619
So here I am going to make a little private struct, struct,
1002
00:50:56,621 --> 00:51:01,090
which I'm gonna have be my animation constants. And so
1003
00:51:01,092 --> 00:51:06,328
let's do static let, let's have the ShakeAngle. So that's
1004
00:51:06,330 --> 00:51:09,398
gonna be the angle of the head that we're going to shake, and
1005
00:51:09,400 --> 00:51:13,102
we'll go, let's see. It's gonna be a CGFloat. It,
1006
00:51:13,104 --> 00:51:17,640
let's go pi over 6. It's gonna be in radians, so we'll do pi
1007
00:51:17,642 --> 00:51:20,676
over 6, which is a little bit of a head shake there.
1008
00:51:20,678 --> 00:51:24,380
And then I'm also gonna have the shake duration,
1009
00:51:24,382 --> 00:51:27,616
how long it takes to move. And for that one,
1010
00:51:27,618 --> 00:51:28,851
let's do maybe half a second.
1011
00:51:28,853 --> 00:51:31,187
So it's gonna do half a second to move each direction.
1012
00:51:31,189 --> 00:51:35,191
So it'll take about a second a half to do the full shake,
1013
00:51:35,193 --> 00:51:39,161
okay. So here, let's use our duration here, which is
1014
00:51:39,163 --> 00:51:43,032
Animation.ShakeDuration. And here's
1015
00:51:43,034 --> 00:51:45,734
our animation, okay, so we're gonna put our animation. This
1016
00:51:45,736 --> 00:51:48,871
is where we're gonna change one of those three special
1017
00:51:48,873 --> 00:51:51,474
things. And then here's our completion,
1018
00:51:51,476 --> 00:51:54,610
which is gonna have this finished boolean and
1019
00:51:54,612 --> 00:51:58,314
then some code here when we're finished, okay?
1020
00:51:58,316 --> 00:52:01,917
And actually, in this case, I'm not going to, well, yeah.
1021
00:52:01,919 --> 00:52:05,121
I'm not gonna use closing trailing syntax here.
1022
00:52:05,123 --> 00:52:07,857
This is an interesting thing to talk about. So
1023
00:52:07,859 --> 00:52:11,527
this is one of the few times when I don't like the trailing
1024
00:52:11,529 --> 00:52:14,730
closure syntax here, because there's really nothing special
1025
00:52:14,732 --> 00:52:16,232
about this closure versus this closure,
1026
00:52:16,234 --> 00:52:19,168
they're kinda equal in power, maybe this one's even
1027
00:52:19,170 --> 00:52:22,138
more powerful. So I'm gonna keep them all inside here,
1028
00:52:22,140 --> 00:52:26,308
just kinda, to me, I think it reads a little better. But
1029
00:52:26,310 --> 00:52:26,675
it's totally a style thing,
1030
00:52:26,677 --> 00:52:28,944
whatever you wanna do. All right, so in here,
1031
00:52:28,946 --> 00:52:30,446
we get to change one of these three things. Well,
1032
00:52:30,448 --> 00:52:34,917
I'm gonna change the transform of this, of our FaceView.
1033
00:52:34,919 --> 00:52:35,518
So I'm gonna say face, oops,
1034
00:52:35,520 --> 00:52:38,120
and I'm gonna do self because I'm in a closure here.
1035
00:52:38,122 --> 00:52:42,224
faceView.transform, and I'm gonna use this nice,
1036
00:52:42,226 --> 00:52:47,696
function called CGAffineTransformRotate.
1037
00:52:47,698 --> 00:52:48,831
It takes an existing transform,
1038
00:52:48,833 --> 00:52:52,568
which I'm gonna take the FaceView's existing transform.
1039
00:52:52,570 --> 00:52:55,404
Okay, transform, is, remember, it's just a var in UIView,
1040
00:52:55,406 --> 00:52:58,641
that's all it is is a var, and it encapsulates the rotation,
1041
00:52:58,643 --> 00:53:01,010
scaling, and translation, okay. So
1042
00:53:01,012 --> 00:53:04,346
we're gonna be messing with the rotation here. And it's,
1043
00:53:04,348 --> 00:53:07,950
we'll rotate it by this angle which we know is our
1044
00:53:07,952 --> 00:53:11,554
Animation.Head, or, ShakeAngle.
1045
00:53:13,291 --> 00:53:17,660
Okay? So that's going to do the rotation there. All right,
1046
00:53:17,662 --> 00:53:20,329
let's see, what do we got here, what are we missing?
1047
00:53:20,331 --> 00:53:25,434
Completion. Yeah, sorry. Completion.
1048
00:53:25,436 --> 00:53:28,470
It's part of that trailing closure syntax, it deleted
1049
00:53:28,472 --> 00:53:31,774
the last keyword. All right, so everyone cool with this?
1050
00:53:31,776 --> 00:53:35,644
Now, what to do in here, we'll see later, okay. First we're
1051
00:53:35,646 --> 00:53:37,913
gonna see if this is working at all. So we're gonna see,
1052
00:53:37,915 --> 00:53:40,382
this is only doing the first part of the head shake, so
1053
00:53:40,384 --> 00:53:42,818
let's see if that animation works.
1054
00:53:46,624 --> 00:53:48,257
Okay, so our eyes, our eyes are still blinking,
1055
00:53:48,259 --> 00:53:51,527
they're still doing the flip from top. And if we click,
1056
00:53:51,529 --> 00:53:54,830
sure enough, it rotated it by pi over 6.
1057
00:53:54,832 --> 00:53:55,331
In fact, if we click again,
1058
00:53:55,333 --> 00:53:58,367
it'll keep on rotating. Now, notice the other animation
1059
00:53:58,369 --> 00:54:00,970
keeps working completely independently. Okay, all
1060
00:54:00,972 --> 00:54:04,006
animations, they, completely independent. They're only
1061
00:54:04,008 --> 00:54:06,842
gonna start interacting with each other if you're animating
1062
00:54:06,844 --> 00:54:09,278
the same thing, the same transform, the same alpha or
1063
00:54:09,280 --> 00:54:12,147
whatever. Here these are completely different views,
1064
00:54:12,149 --> 00:54:15,217
okay, and even though that view is being transformed,
1065
00:54:15,219 --> 00:54:17,319
it still continued to flip from top, okay,
1066
00:54:17,321 --> 00:54:20,155
which is kinda cool, it lets you make, lets you build
1067
00:54:20,157 --> 00:54:22,691
your UI animation very object-oriented. Just make
1068
00:54:22,693 --> 00:54:26,462
each piece animate however it wants to animate. Okay, but
1069
00:54:26,464 --> 00:54:29,064
our head shake, it needs to turn back the other way and
1070
00:54:29,066 --> 00:54:31,100
then back to the middle. So how are we gonna do that?
1071
00:54:31,102 --> 00:54:35,271
Well, it can't turn back to the other side until it's
1072
00:54:35,273 --> 00:54:38,073
finished turning to the right. Okay? So
1073
00:54:38,075 --> 00:54:42,311
we're gonna use the completion block to animate it going back
1074
00:54:42,313 --> 00:54:44,847
the other way. So as soon as it's finished, if it's
1075
00:54:44,849 --> 00:54:50,052
successfully moved over, if it finished. If it finished,
1076
00:54:50,054 --> 00:54:51,387
then we're gonna animate back.
1077
00:54:51,389 --> 00:54:53,722
Well, how the heck are we gonna animate back? Well,
1078
00:54:53,724 --> 00:54:57,593
let's just copy and paste this, because we know this,
1079
00:54:57,595 --> 00:55:00,162
right, this whole thing right here does an animation. So
1080
00:55:00,164 --> 00:55:02,798
we wanna do almost the exact same animation. I'm just gonna
1081
00:55:02,800 --> 00:55:06,368
copy and paste it right in there. The only difference is
1082
00:55:06,370 --> 00:55:08,637
that instead of the shake angle going to the right,
1083
00:55:08,639 --> 00:55:13,108
now it wants to go backwards times two. Okay, it wants to
1084
00:55:13,110 --> 00:55:16,512
go all the way back past the middle and to the other side.
1085
00:55:16,514 --> 00:55:18,981
And then sure enough, when that one's finished,
1086
00:55:18,983 --> 00:55:21,383
we want to do it again and get back to the middle.
1087
00:55:21,385 --> 00:55:24,486
So we're just doing three transforms here, and
1088
00:55:24,488 --> 00:55:28,457
we're just chaining them, okay, in the completion blocks
1089
00:55:28,459 --> 00:55:32,494
of each of them, we're just doing the next animation. That
1090
00:55:32,496 --> 00:55:35,431
make sense? Right? So that's how we chain animations.
1091
00:55:35,433 --> 00:55:40,969
So let's see if that works. All right?
1092
00:55:40,971 --> 00:55:42,638
So, here we go, he's blinking, and
1093
00:55:42,640 --> 00:55:48,143
he shakes his head. And again, we could play with this,
1094
00:55:48,145 --> 00:55:51,313
do we want to curve in, ease in, ease out. Do we want to
1095
00:55:51,315 --> 00:55:53,682
take a little longer maybe to swing back over.
1096
00:55:53,684 --> 00:55:57,186
And so you can play with those numbers really easy in here
1097
00:55:57,188 --> 00:56:00,489
just by setting options or Or durations. I didn't use
1098
00:56:00,491 --> 00:56:03,692
the version of animate durations that had the options
1099
00:56:03,694 --> 00:56:07,296
as an argument, but you could certainly add that back in,
1100
00:56:07,298 --> 00:56:10,165
okay? Any questions about that? You getting a good feel
1101
00:56:10,167 --> 00:56:15,304
for this whole UI view based animation? All right. Okay,
1102
00:56:15,306 --> 00:56:21,043
in the time we have remaining here, hopefully I can get
1103
00:56:21,045 --> 00:56:26,815
us going on this next kind of animation which is this
1104
00:56:26,817 --> 00:56:30,119
physic based one. So now let's talk more about animation.
1105
00:56:30,121 --> 00:56:31,954
We're gonna talk about dynamic animation,
1106
00:56:31,956 --> 00:56:35,557
which is this physics based animation and it's a little
1107
00:56:35,559 --> 00:56:37,626
different approach than the UIView-based. The
1108
00:56:37,628 --> 00:56:40,229
UIView-based, your pretty much directly changing the things
1109
00:56:40,231 --> 00:56:42,931
you wanna change and then animating that change, okay.
1110
00:56:42,933 --> 00:56:45,868
Here you're just describing how things interact and
1111
00:56:45,870 --> 00:56:49,304
then letting them go, okay? However they, they wanna go
1112
00:56:49,306 --> 00:56:53,208
based on whatever restrictions and physics you put upon them,
1113
00:56:53,210 --> 00:56:56,145
okay? So, here's how you do dynamic animation, here's
1114
00:56:56,147 --> 00:56:59,348
the steps. One, you're gonna create a UIDynamicAnimator.
1115
00:56:59,350 --> 00:57:01,750
This is the thing that does the animation. Very simple
1116
00:57:01,752 --> 00:57:05,421
object to create, it, but very powerful implementation,
1117
00:57:05,423 --> 00:57:07,890
it's the thing that actually is doing the animation.
1118
00:57:07,892 --> 00:57:11,493
Then you're gonna create UIDynamicBehaviors and
1119
00:57:11,495 --> 00:57:15,397
add them to the animator. Behaviors are things like,
1120
00:57:15,399 --> 00:57:19,334
gravity is a behavior. Collisions are a behavior.
1121
00:57:19,336 --> 00:57:22,438
Pushing things is a behavior, okay?
1122
00:57:22,440 --> 00:57:25,140
Then you're gonna add UIDynamicItems, which
1123
00:57:25,142 --> 00:57:28,911
are usually UIViews, but not always, but 90% of the time.
1124
00:57:28,913 --> 00:57:32,047
You're gonna add those to the behaviors, and that's gonna
1125
00:57:32,049 --> 00:57:34,516
make those behaviors start acting on those items,
1126
00:57:34,518 --> 00:57:36,852
okay? So it's a three way thing there. Items get
1127
00:57:36,854 --> 00:57:39,721
added to behaviors, behaviors get added to the animator and
1128
00:57:39,723 --> 00:57:41,190
as soon as those are all hooked up,
1129
00:57:41,192 --> 00:57:43,225
things will just start moving, okay?
1130
00:57:43,227 --> 00:57:44,393
Nothing else, you don't need to say go.
1131
00:57:44,395 --> 00:57:46,128
It just automatically goes. As soon as you add it,
1132
00:57:46,130 --> 00:57:50,199
it starts going. All right, so let's talk about each of those
1133
00:57:50,201 --> 00:57:52,734
steps. First creating the dynamic animator,
1134
00:57:52,736 --> 00:57:56,605
there's only two initializers of it. One takes no arguments,
1135
00:57:56,607 --> 00:57:58,407
that just creates a dynamic animator. But
1136
00:57:58,409 --> 00:58:02,044
if you're animating views, if the UIDynamic items you want
1137
00:58:02,046 --> 00:58:05,447
to animate are views, then you're going to want to create
1138
00:58:05,449 --> 00:58:09,084
it with a referenceView, okay? And that's just gonna be like
1139
00:58:09,086 --> 00:58:12,521
your top level view, usually, where all the animation is
1140
00:58:12,523 --> 00:58:16,225
happening. All right? So that's it.
1141
00:58:16,227 --> 00:58:16,291
That's easy,
1142
00:58:16,293 --> 00:58:19,127
it couldn't be easier creating a UIDynamicAnimator.
1143
00:58:19,129 --> 00:58:21,964
Next you're gonna create the DynamicBehaviors, okay.
1144
00:58:21,966 --> 00:58:23,532
The behaviors, generally you create,
1145
00:58:23,534 --> 00:58:27,002
their initializers take no arguments, okay? And so
1146
00:58:27,004 --> 00:58:30,439
you just create them, and add them to the animator here
1147
00:58:30,441 --> 00:58:32,608
using addBehavior, this addBehavior method.
1148
00:58:32,610 --> 00:58:36,378
Now, oftentimes, you'll create something like gravity or
1149
00:58:36,380 --> 00:58:38,647
the collider there and you're going to configure it.
1150
00:58:38,649 --> 00:58:41,350
Because the gravity can be strong gravity or light
1151
00:58:41,352 --> 00:58:43,785
gravity, like Martian gravity or Earth gravity, okay?
1152
00:58:43,787 --> 00:58:46,455
You can change that. Same thing with collider obviously.
1153
00:58:46,457 --> 00:58:49,391
You've got to decide what's gonna collide with what, okay?
1154
00:58:49,393 --> 00:58:52,461
But once you create them, you're going to just add them
1155
00:58:52,463 --> 00:58:54,730
to the animator using addBehavior. And
1156
00:58:54,732 --> 00:58:58,200
then you add the dynamic items to the behavior and you do
1157
00:58:58,202 --> 00:59:01,703
that with addItem on the behavior. So gravity.addItem,
1158
00:59:01,705 --> 00:59:04,573
collider.addItem, gravity.addItem 2. Now if you
1159
00:59:04,575 --> 00:59:08,010
did these three things right here, notice that item1 and
1160
00:59:08,012 --> 00:59:11,213
item2 would both be affected by gravity cuz I added them to
1161
00:59:11,215 --> 00:59:15,450
that. But notice that item2 would not collide with item1.
1162
00:59:15,452 --> 00:59:18,887
If item2 and item1 came, next to each other on screen,
1163
00:59:18,889 --> 00:59:21,456
they'd pass right through because they're not
1164
00:59:21,458 --> 00:59:25,661
both being affected by the same collider, okay?
1165
00:59:25,663 --> 00:59:26,895
Now if I said collider add item2,
1166
00:59:26,897 --> 00:59:29,264
then they would smash into each other and
1167
00:59:29,266 --> 00:59:34,102
bounce off, okay? So, which, behaviors you add, which
1168
00:59:34,104 --> 00:59:38,040
items to really determines how they're gonna interact.
1169
00:59:38,042 --> 00:59:40,609
All right, so UIDynamicItem, I keep mentioning this.
1170
00:59:40,611 --> 00:59:43,612
What is this? I said that usually this would be views,
1171
00:59:43,614 --> 00:59:48,450
UIViews. But UIDynamicItem is actually a protocol, okay? And
1172
00:59:48,452 --> 00:59:52,254
it has these three vars in it, bounds, which is the bounds of
1173
00:59:52,256 --> 00:59:57,626
the thing being animated, notice that that is get only,
1174
00:59:57,628 --> 01:00:03,699
okay? You cannot animate the bounds of something
1175
01:00:03,701 --> 01:00:07,536
in the animator. However, you can animate the center,
1176
01:00:07,538 --> 01:00:09,805
which is where it is, because that one is get and
1177
01:00:09,807 --> 01:00:13,575
set, okay? So the position of something in the animation
1178
01:00:13,577 --> 01:00:16,078
world is animatable, but not the bounds. In other words,
1179
01:00:16,080 --> 01:00:21,550
not the size, okay? However, the transform which
1180
01:00:21,552 --> 01:00:26,355
views have is settable. So you can rotate things, okay?
1181
01:00:26,357 --> 01:00:31,293
And scale them, things like that, as part of the animation
1182
01:00:31,295 --> 01:00:35,464
as well. So, this like I said, is usually a UIView but
1183
01:00:35,466 --> 01:00:37,899
you could actually animate off screen things or
1184
01:00:37,901 --> 01:00:40,469
things that you draw on screen using sprites or
1185
01:00:40,471 --> 01:00:45,741
something else, using this whole mechanism as well, okay.
1186
01:00:45,743 --> 01:00:48,243
But in this class you are only gonna do UIViews,
1187
01:00:48,245 --> 01:00:51,213
it's the most straightforward obvious way to do it.
1188
01:00:51,215 --> 01:00:53,248
Now if you think about these being UIViews, and
1189
01:00:53,250 --> 01:00:55,250
they've been added to all these behaviors. And
1190
01:00:55,252 --> 01:00:57,719
gravity is pulling on them, and they're colliding on,
1191
01:00:57,721 --> 01:01:00,088
off each other and bouncing and moving all around.
1192
01:01:00,090 --> 01:01:02,591
Maybe they're spinning around because they hit a corner or
1193
01:01:02,593 --> 01:01:05,327
something. Their transform is being changed.
1194
01:01:05,329 --> 01:01:08,296
If you want to change any of those views, if you want to
1195
01:01:08,298 --> 01:01:12,100
change their transform or center, okay. Then you have to
1196
01:01:12,102 --> 01:01:15,270
let the animator know, otherwise, it's gonna be
1197
01:01:15,272 --> 01:01:17,939
changing them to something else. And you do that
1198
01:01:17,941 --> 01:01:20,442
by sending this message to the animator which is
1199
01:01:20,444 --> 01:01:23,578
updateItemUsingCurrentState. When you pass the item, this
1200
01:01:23,580 --> 01:01:27,115
would be a UIView, right, cuz UIView implements in protocol.
1201
01:01:27,117 --> 01:01:30,485
And, then it will pick up whatever the current state is.
1202
01:01:30,487 --> 01:01:33,622
So if you move the view to a certain spot the animator
1203
01:01:33,624 --> 01:01:36,291
which moved the item to the spot in its world and
1204
01:01:36,293 --> 01:01:40,395
then keep applying all the behaviors to it, okay? But
1205
01:01:40,397 --> 01:01:41,229
it you don't do this,
1206
01:01:41,231 --> 01:01:43,665
you're gonna be fighting the animator. Okay,
1207
01:01:43,667 --> 01:01:46,902
so don't fight the animator, work with the animator.
1208
01:01:47,638 --> 01:01:49,471
Okay, so let's talk about some of these behaviors in
1209
01:01:49,473 --> 01:01:54,276
detail here. Gravity behavior, so gravity isn't always down.
1210
01:01:54,278 --> 01:01:56,978
Okay, gravity can mean any direction you want. Okay,
1211
01:01:56,980 --> 01:02:00,415
you specify the direction with this angle right here,
1212
01:02:00,417 --> 01:02:05,020
angles in radians, 0 is off to the right. So if you set your
1213
01:02:05,022 --> 01:02:08,056
gravity angle to 0 and put some views on there, as long
1214
01:02:08,058 --> 01:02:10,025
as your magnitude is greater than 0 everything starts
1215
01:02:10,027 --> 01:02:13,528
sliding off, accelerating off, cuz gravity is acceleration.
1216
01:02:13,530 --> 01:02:14,529
Right everyone know what gravity is?
1217
01:02:14,531 --> 01:02:17,432
It's acceleration of gravity, 9.8 meters per second squared,
1218
01:02:17,434 --> 01:02:19,634
right? So it's gonna start accelerating
1219
01:02:19,636 --> 01:02:22,237
off to the right. So if you wanted it going down,
1220
01:02:22,239 --> 01:02:25,507
let's say, down towards the home button,
1221
01:02:25,509 --> 01:02:29,978
then you would want it to be what, three pi over two or
1222
01:02:29,980 --> 01:02:32,147
something like that? Yeah, three pi over two I guess,
1223
01:02:32,149 --> 01:02:34,549
I know my radians. Cuz you want it pointing all the way
1224
01:02:34,551 --> 01:02:38,653
to down, okay. We're gonna talk about next week,
1225
01:02:38,655 --> 01:02:42,090
what if you wanted it pointing where real gravity was. And
1226
01:02:42,092 --> 01:02:43,792
once you know core motion, and you know how to use
1227
01:02:43,794 --> 01:02:46,061
the accelerometer that's in the device you could actually
1228
01:02:46,063 --> 01:02:49,631
be constantly setting this gravity to be actual gravity.
1229
01:02:49,633 --> 01:02:51,166
And so all your things could fall and
1230
01:02:51,168 --> 01:02:52,067
no matter way they turn their phone,
1231
01:02:52,069 --> 01:02:55,871
things could fall down towards the Earth. And then magnitude
1232
01:02:55,873 --> 01:02:59,574
is how, how fast the acceleration is happening.
1233
01:02:59,576 --> 01:03:00,375
What's interesting is 1.0,
1234
01:03:00,377 --> 01:03:05,580
the magnitude of 1.0 is 1000 points per second squared.
1235
01:03:05,582 --> 01:03:07,883
Okay, it's accelerating 1000 points a second,
1236
01:03:07,885 --> 01:03:10,819
per second, that's how fast it's accelerating.
1237
01:03:10,821 --> 01:03:15,423
This looks amazingly like 9.8 meters per second squared.
1238
01:03:15,425 --> 01:03:17,959
Okay, if you put something the top of your screen and
1239
01:03:17,961 --> 01:03:20,595
let it fall at this magnitude, it'll fall
1240
01:03:20,597 --> 01:03:23,632
very similar to how a real object would fall in space.
1241
01:03:23,634 --> 01:03:26,101
And it's amazing that this round number is so
1242
01:03:26,103 --> 01:03:30,906
close to 9.8 meters per second squared. But it's not,
1243
01:03:30,908 --> 01:03:34,309
it's not the same thing, but it's close. All right,
1244
01:03:34,311 --> 01:03:36,945
UI attachment behaviors. So what is this behavior?
1245
01:03:36,947 --> 01:03:40,749
This behavior allows you to take two items or an item and
1246
01:03:40,751 --> 01:03:43,351
a point and attach them. Basically attach
1247
01:03:43,353 --> 01:03:45,253
them with like an iron bar between them. But
1248
01:03:45,255 --> 01:03:49,157
the bar can pivot at either side, either at the point or
1249
01:03:49,159 --> 01:03:51,793
at the item or at the two items can pivot.
1250
01:03:51,795 --> 01:03:53,762
So basically if you wanna think about two items,
1251
01:03:53,764 --> 01:03:56,164
those two items will always stay the same distance apart
1252
01:03:56,166 --> 01:03:59,467
no matter what's happening to them. Okay? Now, if you had
1253
01:03:59,469 --> 01:04:02,604
a point, a fixed point, in an item, and this item had
1254
01:04:02,606 --> 01:04:05,507
gravity and an attachment behavior going onto it,
1255
01:04:05,509 --> 01:04:09,511
it would fall down and swing. See why? Cuz it's always
1256
01:04:09,513 --> 01:04:11,847
attached to this point, but gravity is pulling it down,
1257
01:04:11,849 --> 01:04:15,851
and eventually it would settle down. That make sense? So
1258
01:04:15,853 --> 01:04:18,086
attachment is basically a way to attach two things, or
1259
01:04:18,088 --> 01:04:19,788
attach something to a point. Now, what's
1260
01:04:19,790 --> 01:04:23,558
really cool about it is, while things are happening, you can
1261
01:04:23,560 --> 01:04:26,328
change the length of that attachment. Okay?
1262
01:04:26,330 --> 01:04:29,231
Right, right in the middle, so it's falling down,
1263
01:04:29,233 --> 01:04:32,534
you could just pull it up in close. Like a yo-yo. Right?
1264
01:04:32,536 --> 01:04:34,502
If you're doing yo-yo, you throw it around, and
1265
01:04:34,504 --> 01:04:37,772
you start pulling in, while it's still turning? Okay.
1266
01:04:37,774 --> 01:04:38,039
You can do the same thing.
1267
01:04:38,041 --> 01:04:41,977
You could build Yoyo quite easily here, by changing
1268
01:04:41,979 --> 01:04:45,981
the length of the attachment. Okay, the anchor point, okay,
1269
01:04:45,983 --> 01:04:48,083
if you have a point and an item, that can also change.
1270
01:04:48,085 --> 01:04:50,085
It's like maybe it's attached to the finger,
1271
01:04:50,087 --> 01:04:52,821
using a gesture. And you're moving your finger around and
1272
01:04:52,823 --> 01:04:55,790
the attachment changing, is pulling the thing around with
1273
01:04:55,792 --> 01:04:57,893
it. Okay, and I'm actually going to show that.
1274
01:04:57,895 --> 01:05:00,795
In the demo on Wednesday. So there's a lot of power in
1275
01:05:00,797 --> 01:05:04,232
an attachment behavior. Collision behaviors.
1276
01:05:04,234 --> 01:05:06,468
Another thing,obviously, that you want. Right?
1277
01:05:06,470 --> 01:05:09,237
Let's say you're gonna build your homework six which is
1278
01:05:09,239 --> 01:05:11,706
a breakout game. Okay? Obviously the breakout game
1279
01:05:11,708 --> 01:05:14,175
wants to collide with the bricks and knock them out, and
1280
01:05:14,177 --> 01:05:16,411
wants to bounce of the edge and bounce off the paddle.
1281
01:05:16,413 --> 01:05:19,814
So you need these collisions, right? There you can either
1282
01:05:19,816 --> 01:05:22,317
have things colliding off of boundaries so
1283
01:05:22,319 --> 01:05:25,053
like in your breakout game the bricks would probably be these
1284
01:05:25,055 --> 01:05:27,422
fixed boundaries. Okay your paddle's probably
1285
01:05:27,424 --> 01:05:29,791
a boundary that you're moving all right or
1286
01:05:29,793 --> 01:05:32,427
it can bounce off other things that are being animated.
1287
01:05:32,429 --> 01:05:34,996
Although you wouldn't want your bricks to do that because
1288
01:05:34,998 --> 01:05:36,331
your bricks when you hit them they disappear.
1289
01:05:36,333 --> 01:05:39,467
They don't go flying off the screen okay they They despair
1290
01:05:39,469 --> 01:05:42,270
well maybe would have a flat screen, I don't know.
1291
01:05:42,272 --> 01:05:46,741
So collision lets you set up collisions between boundaries
1292
01:05:46,743 --> 01:05:51,379
or between items, okay. You can have as many items as you
1293
01:05:51,381 --> 01:05:52,981
want in there, things all bouncing off each other,
1294
01:05:52,983 --> 01:05:54,582
the demo I'm going to do on Wednesday we're gonna have
1295
01:05:54,584 --> 01:05:57,352
a lot of things colliding with each other.
1296
01:05:57,354 --> 01:05:58,320
There's a nice little var here,
1297
01:05:58,322 --> 01:06:01,423
a bool translates reference bounce into boundary. And
1298
01:06:01,425 --> 01:06:04,693
what that'll do it'll make a boundary which is the edge
1299
01:06:04,695 --> 01:06:06,828
of your animator's reference view right?
1300
01:06:06,830 --> 01:06:09,264
So the reference view remember at the beginning of this that
1301
01:06:09,266 --> 01:06:10,899
kind of contains the whole space.
1302
01:06:10,901 --> 01:06:13,134
You can make it so that it's basically a box. So everything
1303
01:06:13,136 --> 01:06:16,104
that happens in the animation world as long as you add it to
1304
01:06:16,106 --> 01:06:21,676
this collision behavior will stay inside that box okay?
1305
01:06:22,579 --> 01:06:25,280
One thing that's nice to know about the collision is when
1306
01:06:25,282 --> 01:06:27,882
a collision happens. And the collision behavior has
1307
01:06:27,884 --> 01:06:31,886
a delegate, okay, which lets you find out when a collision
1308
01:06:31,888 --> 01:06:34,689
happens. So you get this delegate method sent to you
1309
01:06:34,691 --> 01:06:37,926
collision behavior. Here's the behavior that sent me to you.
1310
01:06:37,928 --> 01:06:41,162
And it'll tell you that contact began or ended for
1311
01:06:41,164 --> 01:06:45,000
an item with a boundary, or for an item with another item.
1312
01:06:45,002 --> 01:06:48,837
So there's the thing with item as well. Notice
1313
01:06:48,839 --> 01:06:54,009
the type of this with boundary identifier. NScopying okay.
1314
01:06:54,011 --> 01:06:59,614
NScopying is there for historical reasons but
1315
01:06:59,616 --> 01:07:01,216
suffice it to say NSstring and
1316
01:07:01,218 --> 01:07:05,387
NSnumber are what's intended to be passed there. And
1317
01:07:05,389 --> 01:07:07,789
we know that those are bridged to string and Int and
1318
01:07:07,791 --> 01:07:10,892
Double, nicely bridged. So you can pass strings and doubles,
1319
01:07:10,894 --> 01:07:14,829
the only tricky thing is that you're gonna have to use as
1320
01:07:14,831 --> 01:07:18,133
to cast this to be a string or an Int or a Double okay
1321
01:07:18,135 --> 01:07:24,139
because it's an NSCopying. Small quirk of the API there.
1322
01:07:24,141 --> 01:07:27,242
Next behavior is SnapBehavior. This is how you move something
1323
01:07:27,244 --> 01:07:29,944
to a new location. So, you got something sitting there and
1324
01:07:29,946 --> 01:07:31,813
you want it, move it over here, okay.
1325
01:07:31,815 --> 01:07:35,316
And, it's called SnapBehavior because it moves over there
1326
01:07:35,318 --> 01:07:38,219
but when it gets there it's like it's attached with
1327
01:07:38,221 --> 01:07:41,022
four springs to its corners so that it gets there and
1328
01:07:41,024 --> 01:07:44,359
goes [NOISE]. It vibrates a little bit. It doesn't just
1329
01:07:44,361 --> 01:07:48,863
slide up and get stuck. It slides kind of softly.
1330
01:07:48,865 --> 01:07:51,566
You can say how much the springs are dampened,
1331
01:07:51,568 --> 01:07:53,334
whether it really shakes when it gets there, or
1332
01:07:53,336 --> 01:07:56,738
if you get a slight shake or whatever. But snap behavior's
1333
01:07:56,740 --> 01:07:59,741
kinda the preferred way to move something from one place
1334
01:07:59,743 --> 01:08:02,811
to another, okay. Now a lot of things might be moving cuz
1335
01:08:02,813 --> 01:08:05,213
they have gravity and other things bouncing on them. But
1336
01:08:05,215 --> 01:08:06,714
sometimes you have a stationary thing that you want
1337
01:08:06,716 --> 01:08:10,151
to move and snap behavior is a good way to do that.
1338
01:08:10,153 --> 01:08:13,588
Then there's push behavior, okay. Push behavior means push
1339
01:08:13,590 --> 01:08:16,891
on this thing, okay, and you can either push it once and
1340
01:08:16,893 --> 01:08:20,829
see what happens to it or you can continue to push on it,
1341
01:08:20,831 --> 01:08:25,633
okay. So that's continuous versus instantaneous pushing.
1342
01:08:25,635 --> 01:08:27,502
And you just specify the direction or
1343
01:08:27,504 --> 01:08:30,472
you can do an angle and a magnitude kind of like you
1344
01:08:30,474 --> 01:08:33,007
do with gravity and it's going to push in that direction.
1345
01:08:33,009 --> 01:08:34,843
So, for example, let's say you had a view, and
1346
01:08:34,845 --> 01:08:36,878
gravity is working on it, so it's falling down the screen,
1347
01:08:36,880 --> 01:08:39,447
and you pushed up on it with a certain amount of force.
1348
01:08:39,449 --> 01:08:42,684
It would fly back up, but then gravity would still be working
1349
01:08:42,686 --> 01:08:43,585
on it, so it would come back down.
1350
01:08:43,587 --> 01:08:46,888
See what I mean? So pushing imparts a certain
1351
01:08:46,890 --> 01:08:50,492
force to it, but if it's an instantaneous push it's not
1352
01:08:50,494 --> 01:08:52,694
going to apply that force after the initial push and
1353
01:08:52,696 --> 01:08:57,065
other forces are going to continue to work on it. Right.
1354
01:08:57,067 --> 01:08:57,565
Okay?
1355
01:08:57,567 --> 01:09:01,669
Now it's an interesting about push behaviors is that if
1356
01:09:01,671 --> 01:09:05,140
they're instantaneous once you push on them you kind of want
1357
01:09:05,142 --> 01:09:07,208
to remove them as behaviors cuz they're dead.
1358
01:09:07,210 --> 01:09:09,077
They're never gonna push again. And
1359
01:09:09,079 --> 01:09:10,378
instance behavior only and
1360
01:09:10,380 --> 01:09:12,147
instantaneous behavior only pushes one and
1361
01:09:12,149 --> 01:09:15,650
then it's done so you want to really remove that behavior.
1362
01:09:15,652 --> 01:09:18,887
And doing that you kind of do that in an unusual way.
1363
01:09:18,889 --> 01:09:20,255
And I'm gonna talk about that in a few slides,
1364
01:09:20,257 --> 01:09:25,193
after I talk about a different method in a second. Then
1365
01:09:25,195 --> 01:09:27,695
there's this behavior. Okay, we're talking about behaviors,
1366
01:09:27,697 --> 01:09:28,863
right, for gravity and collisions.
1367
01:09:28,865 --> 01:09:30,965
Here's one called UIDynamicItemBehavior.
1368
01:09:30,967 --> 01:09:33,635
And this is kind of a meta behavior, okay.
1369
01:09:33,637 --> 01:09:37,038
This has a bunch of vars like allowsRotation, friction,
1370
01:09:37,040 --> 01:09:41,209
elasticity. These are settings that you make, okay, that, so
1371
01:09:41,211 --> 01:09:43,845
all the items that are added to this behavior will kind of
1372
01:09:43,847 --> 01:09:48,416
have these attributes. Okay. They'll be allowed to rotate,
1373
01:09:48,418 --> 01:09:48,650
they'll have a certain friction.
1374
01:09:48,652 --> 01:09:52,220
They'll have a certain elasticity when they bounce of
1375
01:09:52,222 --> 01:09:56,191
other things, okay? So these are really kind of effect,
1376
01:09:56,193 --> 01:10:01,196
affecting other behaviors. Behaviors.
1377
01:10:01,198 --> 01:10:04,632
So you can think of this as a configuration behavior.
1378
01:10:04,634 --> 01:10:08,169
You really are probably only going to have one.
1379
01:10:08,171 --> 01:10:10,338
Each item will in one item behavior.
1380
01:10:10,340 --> 01:10:12,974
It is actually legal to have multiple item
1381
01:10:12,976 --> 01:10:16,144
behaviors on same items. But you better be sure that you
1382
01:10:16,146 --> 01:10:18,846
understand how they are interacting with each other.
1383
01:10:18,848 --> 01:10:22,951
Generally though we think of them as just settings. For
1384
01:10:22,953 --> 01:10:25,286
the items, how they're gonna behave with collisions and
1385
01:10:25,288 --> 01:10:28,356
all these other things. The UIDynamicItemBehavior,
1386
01:10:28,358 --> 01:10:29,424
you almost always have one, by the way,
1387
01:10:29,426 --> 01:10:32,026
cuz you want to set these things. It's also really cool,
1388
01:10:32,028 --> 01:10:34,596
because you can ask the dynamic item behavior for
1389
01:10:34,598 --> 01:10:36,731
any item that's added to that behavior.
1390
01:10:36,733 --> 01:10:40,735
You can say, what's its current linear velocity?
1391
01:10:40,737 --> 01:10:43,671
I got some viewage bouncing along the screen because it
1392
01:10:43,673 --> 01:10:46,441
collided and gravity's pulling on it and
1393
01:10:46,443 --> 01:10:47,675
I want to know what's it's velocity?
1394
01:10:47,677 --> 01:10:50,278
Right now at this instant, how fast is it moving and in what
1395
01:10:50,280 --> 01:10:53,681
direction? Okay, so that's really nice to know especially
1396
01:10:53,683 --> 01:10:56,818
if you're gonna pause your game. Or pause what's going on
1397
01:10:56,820 --> 01:11:00,388
and then continue. You want to grab its linear velocity,
1398
01:11:00,390 --> 01:11:03,324
stop the animation and when it starts again set the linear
1399
01:11:03,326 --> 01:11:06,527
velocity back to what it was, cause you can set it as well,
1400
01:11:06,529 --> 01:11:08,263
by adding linear velocity back in.
1401
01:11:08,265 --> 01:11:11,099
You can also find angular velocity, if the thing is
1402
01:11:11,101 --> 01:11:14,269
spinning, k how fast it's spinning, spinning really fast
1403
01:11:14,271 --> 01:11:19,274
or just kind of slowly rotating, whatever okay.
1404
01:11:20,110 --> 01:11:23,177
UI dynamic behavior as opposed to UI dynamic
1405
01:11:23,179 --> 01:11:25,680
item behavior the last I was here UI dynamic item behavior.
1406
01:11:25,682 --> 01:11:28,650
This is UI dynamic behavior. This is the super class of all
1407
01:11:28,652 --> 01:11:33,621
behaviors okay? And you can create your own behaviors as
1408
01:11:33,623 --> 01:11:38,393
a composite of other behaviors by using this addChildBehavior
1409
01:11:38,395 --> 01:11:40,895
method and UIDynamicBehavior. So you simply
1410
01:11:40,897 --> 01:11:44,699
subclass UIDynamicBehavior, addChildBehavior for
1411
01:11:44,701 --> 01:11:47,402
all the composite behaviors you want. The gravity and
1412
01:11:47,404 --> 01:11:50,605
the dynamic item behaviors and collision, whatever.
1413
01:11:50,607 --> 01:11:51,639
You add all those child behaviors.
1414
01:11:51,641 --> 01:11:55,543
And then add any items to all of those behaviors, and you've
1415
01:11:55,545 --> 01:11:59,213
created a composite new kind of behavior, which collects
1416
01:11:59,215 --> 01:12:02,250
all those into one. And if we have some set of behaviors
1417
01:12:02,252 --> 01:12:05,119
that's operating on a whole bunch of items, we are very
1418
01:12:05,121 --> 01:12:08,056
often going to create our own little composite subclass.
1419
01:12:08,058 --> 01:12:11,159
And I'll do that in the demo cuz it's pretty common, okay?
1420
01:12:11,161 --> 01:12:14,962
One thing that's cool about UIDynamicBehavior is it has
1421
01:12:14,964 --> 01:12:17,999
a var called DynamicAnimator, which will get you
1422
01:12:18,001 --> 01:12:21,102
the UIDynamicAnimator that this behavior is in.
1423
01:12:21,104 --> 01:12:23,971
Because remember we add behaviors to dynamic animators
1424
01:12:23,973 --> 01:12:25,773
as like the second slide I showed you.
1425
01:12:25,775 --> 01:12:27,342
So you can find out what it is. And
1426
01:12:27,344 --> 01:12:30,311
in fact it'll even tell you if you move to an animator.
1427
01:12:30,313 --> 01:12:33,348
That's usually you, the animator being taken away from
1428
01:12:33,350 --> 01:12:35,316
you because they wanna stop animation, or
1429
01:12:35,318 --> 01:12:37,885
add it back to you to continue animation, okay? So
1430
01:12:37,887 --> 01:12:42,090
that's a good way to find out that that's happening. Okay,
1431
01:12:42,092 --> 01:12:46,561
UIDynamicBehavior also has a really cool var
1432
01:12:46,563 --> 01:12:50,965
called action which is a closure, okay?
1433
01:12:50,967 --> 01:12:54,402
You can set this closure to anything you want and
1434
01:12:54,404 --> 01:12:56,671
every time this behavior does anything,
1435
01:12:56,673 --> 01:13:00,575
it's gonna call this closure. So some behaviors call this
1436
01:13:00,577 --> 01:13:03,811
thing all the time cuz they're always acting on
1437
01:13:03,813 --> 01:13:07,382
the particular items, okay? So you wawnna be careful to make
1438
01:13:07,384 --> 01:13:09,917
sure whatever's in this closure is really efficient.
1439
01:13:09,919 --> 01:13:12,787
This is one of those cases where you do wanna kinda
1440
01:13:12,789 --> 01:13:15,990
think about optimizing prematurely here because
1441
01:13:15,992 --> 01:13:16,824
this could be called a lot.
1442
01:13:16,826 --> 01:13:18,960
So you don't wanna do anything expensive in here.
1443
01:13:18,962 --> 01:13:22,296
Okay, but it's really kinda cool because you can then
1444
01:13:22,298 --> 01:13:25,433
do things as the animation is happening, finding as things
1445
01:13:25,435 --> 01:13:28,970
are happening in the animation you can be getting involved.
1446
01:13:28,972 --> 01:13:31,072
Okay, so it's really kind of a cool method.
1447
01:13:31,074 --> 01:13:32,407
Now one of the uses we can do for
1448
01:13:32,409 --> 01:13:33,875
this is that push behavior conundrum,
1449
01:13:33,877 --> 01:13:37,011
you got an instantaneous push, it's already pushed. Now you
1450
01:13:37,013 --> 01:13:39,147
wanna remove it. Well this would be a great way to do it.
1451
01:13:39,149 --> 01:13:43,017
Just have an action on that push behavior then set
1452
01:13:43,019 --> 01:13:46,220
the push behavior to remove itself when you're done and
1453
01:13:46,222 --> 01:13:49,357
this is what it would look like. Oops, no, sorry.
1454
01:13:49,359 --> 01:13:51,826
One more thing before I talk about that. This is
1455
01:13:51,828 --> 01:13:54,962
the dynamic animator also has a delegate. And it'll tell
1456
01:13:54,964 --> 01:13:59,267
you when the animator paused or resumed. Now what causes
1457
01:13:59,269 --> 01:14:03,137
the animator to pause? It's when all the objects reach
1458
01:14:03,139 --> 01:14:06,707
stasis. In other words, none of the behaviors are causing
1459
01:14:06,709 --> 01:14:09,410
any of the objects to change, okay? Then it pauses.
1460
01:14:09,412 --> 01:14:13,147
As soon as a push comes in or something like that,
1461
01:14:13,149 --> 01:14:16,250
causes things to start moving again, it will resume. So you
1462
01:14:16,252 --> 01:14:20,087
can find out when things have reached a steady state and
1463
01:14:20,089 --> 01:14:23,157
when they resume, using these delegate methods
1464
01:14:23,159 --> 01:14:26,594
of the dynamic animator, okay? All right, back to that push
1465
01:14:26,596 --> 01:14:28,463
state that I was talking about. All right, so
1466
01:14:28,465 --> 01:14:30,631
here I've created a push behavior with a certain
1467
01:14:30,633 --> 01:14:33,267
magnitude and angle, right? And look what I've got.
1468
01:14:33,269 --> 01:14:36,237
I've set the push behavior's action to be,
1469
01:14:36,239 --> 01:14:40,241
remove this pushBehavaior from the dynamic animator.
1470
01:14:40,243 --> 01:14:45,046
You see that? And since it's instantaneous,
1471
01:14:45,048 --> 01:14:47,748
see how its mode is instantaneous? This is fine.
1472
01:14:47,750 --> 01:14:49,817
I want this thing removed. I don't want it sitting around
1473
01:14:49,819 --> 01:14:52,820
there collecting dust because it's not gonna fire ever
1474
01:14:52,822 --> 01:14:56,057
again. It fired, the action method got called,
1475
01:14:56,059 --> 01:14:57,792
I put this closure method in here to do this. But
1476
01:14:57,794 --> 01:15:01,963
of course this has a memory cycle, a really bad one, okay?
1477
01:15:01,965 --> 01:15:06,534
Because, this push behavior has a var
1478
01:15:06,536 --> 01:15:11,105
action which is a closure. So, it has a strong pointer to
1479
01:15:11,107 --> 01:15:13,841
this closure. Push behavior's action is a strong pointer to
1480
01:15:13,843 --> 01:15:17,979
this closure. And this closure references the pushBehavior,
1481
01:15:17,981 --> 01:15:21,282
so it has a strong pointer back to the pushBehavior. So
1482
01:15:21,284 --> 01:15:22,583
this will happen just fine and
1483
01:15:22,585 --> 01:15:25,419
now the only people pointing to this pushBehavior,
1484
01:15:25,421 --> 01:15:28,623
since it got removed from the dynamicAnimator, the only
1485
01:15:28,625 --> 01:15:33,361
thing pointing to it is this closure. And it's pointing to
1486
01:15:33,363 --> 01:15:36,964
closure, so they're keeping each other in the heap, okay?
1487
01:15:36,966 --> 01:15:39,767
So this is our classic memory cycle.
1488
01:15:39,769 --> 01:15:41,669
And just to remind how we break it here,
1489
01:15:41,671 --> 01:15:44,872
we're just gonna put unowned pushBehavior in here. Cuz we
1490
01:15:44,874 --> 01:15:48,476
know that this can never be out of the heap when action is
1491
01:15:48,478 --> 01:15:52,013
called by definition. Okay, action is only called when his
1492
01:15:52,015 --> 01:15:55,216
pushBehavior does anything. So it has to clearly still be in
1493
01:15:55,218 --> 01:15:57,251
the heap [INAUDIBLE] done anything, okay?
1494
01:15:57,253 --> 01:15:59,420
So we can just put out known pushBehavior, boom,
1495
01:15:59,422 --> 01:16:02,924
breaks our cycle. This will be just a refresher course in
1496
01:16:02,926 --> 01:16:08,262
breaking the recycles. Okay, I made it. Okay, so as before,
1497
01:16:08,264 --> 01:16:10,698
here's what we're doing coming up. If you have any
1498
01:16:10,700 --> 01:16:13,801
questions I will be here as usual.
1499
01:16:14,771 --> 01:16:15,036
>> For more,
1500
01:16:15,038 --> 01:16:15,069
please visit us at stanford.edu
================================================
FILE: subtitles/14. Animation and Core Motion.srt
================================================
1
00:00:00,001 --> 00:00:03,635
[MUSIC]
2
00:00:03,637 --> 00:00:08,774
Stanford University. >> Okay well welcome
3
00:00:08,776 --> 00:00:13,379
then to Lecture 14 of CS Stanford CS193P,
4
00:00:13,381 --> 00:00:18,384
Spring of 2016. Today, we have two topics. One is,
5
00:00:18,386 --> 00:00:21,587
I'm gonna continue the dynamic animation we talked about in
6
00:00:21,589 --> 00:00:24,456
the last lecture and slides with a big ol' demo. Okay,
7
00:00:24,458 --> 00:00:28,660
that really shows a lot of dynamic, animation in action
8
00:00:28,662 --> 00:00:31,897
and homework Assignment Six is all about dynamic animation,
9
00:00:31,899 --> 00:00:34,433
so be preparing you for that. And then I'm gonna do a new
10
00:00:34,435 --> 00:00:37,336
topic. I had said last time it was probably gonna be Alerts,
11
00:00:37,338 --> 00:00:40,172
but actually I'm gonna do CoreMotion now, okay.
12
00:00:40,174 --> 00:00:43,842
And we'll, I'm gonna redo some slides on CoreMation,
13
00:00:43,844 --> 00:00:46,645
Core Motion, and then I'll also do a demo. Hopefully I'll
14
00:00:46,647 --> 00:00:50,849
have enough time to do all of that today, all right. Okay,
15
00:00:50,851 --> 00:00:54,386
so let's dive right into this demo. It's called Dropit. It's
16
00:00:54,388 --> 00:00:57,756
basically as if I were gonna go write a Tetris app, okay,
17
00:00:57,758 --> 00:01:00,259
with the little falling blocks that you put in place and
18
00:01:00,261 --> 00:01:02,761
then you complete a row and the row would disappear and
19
00:01:02,763 --> 00:01:04,730
then more blocks would come down. Kinda like that,
20
00:01:04,732 --> 00:01:06,732
but this is a demo. We're starting from scratch.
21
00:01:06,734 --> 00:01:08,300
So we're just gonna have little individuals
22
00:01:08,302 --> 00:01:11,070
blocks come down and when they all make a row then it'll
23
00:01:11,072 --> 00:01:14,606
disappear. So we're not gonna do the L-shaped and T-shaped
24
00:01:14,608 --> 00:01:17,509
blocks from Tetris. We're just gonna do squares only. Okay,
25
00:01:17,511 --> 00:01:20,279
so it's kind of like we're getting started on Tetris.
26
00:01:20,281 --> 00:01:23,515
So I'm not calling it Tetris because it doesn't look enough
27
00:01:23,517 --> 00:01:23,782
like Tetris for that.
28
00:01:23,784 --> 00:01:25,984
I'll call it DropIt because we're gonna dropping,
29
00:01:25,986 --> 00:01:30,055
little squares. Okay, so I'm gonna create a new project
30
00:01:30,057 --> 00:01:34,426
here. It's an iOS application as always. Single view..
31
00:01:34,428 --> 00:01:39,531
I'm gonna call it Dropit. Okay, we don't need core data.
32
00:01:39,533 --> 00:01:40,766
Okay it can be universal app,
33
00:01:40,768 --> 00:01:42,267
I don't see any reason why this app wouldn't be
34
00:01:42,269 --> 00:01:45,270
universal as you'll see. Okay. So here's Dropit,
35
00:01:45,272 --> 00:01:48,640
we'll put it where we always put everything. Here it is.
36
00:01:48,642 --> 00:01:51,009
Now what's interesting about Dropit is I'm gonna build
37
00:01:51,011 --> 00:01:55,380
the entire user interface in code. So my story board all
38
00:01:55,382 --> 00:01:58,984
it's gonna have is one custom view. Okay, and then I'm
39
00:01:58,986 --> 00:02:01,587
gonna do all in code because we're gonna use the animator.
40
00:02:01,589 --> 00:02:03,722
We're gonna have a lot of animated views going on.
41
00:02:03,724 --> 00:02:06,992
That's what's gonna be the entire UI. So,um,
42
00:02:06,994 --> 00:02:09,428
I don't really want this generic ViewController here.
43
00:02:09,430 --> 00:02:12,965
So I'm just gonna take this generic ViewController and
44
00:02:12,967 --> 00:02:15,501
delete it, okay, move it to the trash. And
45
00:02:15,503 --> 00:02:18,470
then I'm going to do the thing we always do here where we
46
00:02:18,472 --> 00:02:21,306
take these little guys and move them off into supporting
47
00:02:21,308 --> 00:02:26,111
files because we don't need to do anything, supporting files,
48
00:02:26,113 --> 00:02:28,914
because we don't really need to do anything with them. And
49
00:02:28,916 --> 00:02:32,284
so I'm gonna right off the bat create a custom ViewController
50
00:02:32,286 --> 00:02:36,522
for this view controller that I have here in my storyboard.
51
00:02:36,524 --> 00:02:38,323
And I'm also gonna create a custom UIView.
52
00:02:38,325 --> 00:02:41,360
And that UIView is really where the logic of my game is,
53
00:02:41,362 --> 00:02:43,862
you can almost think of it as like a gameView. Right, and
54
00:02:43,864 --> 00:02:47,099
I could actually put this view anywhere in any UI and
55
00:02:47,101 --> 00:02:47,332
it would play this game,
56
00:02:47,334 --> 00:02:51,436
the Tetris-like game we gonna call DropIt. So let's go and
57
00:02:51,438 --> 00:02:54,973
create those two classes. The UI view controller and
58
00:02:54,975 --> 00:02:59,878
UI view. So here's to UI view, we'll call it DropItView.
59
00:02:59,880 --> 00:03:00,879
Okay, it's gonna be the DropItView.
60
00:03:00,881 --> 00:03:04,650
I'll put it in the same place here not in supporting files.
61
00:03:04,652 --> 00:03:09,188
I'm gonna put where all the rest of the things are. Hey,
62
00:03:09,190 --> 00:03:09,855
there's my DropItView.
63
00:03:09,857 --> 00:03:13,625
My DropItView actually is not gonna implement DrawRect,
64
00:03:13,627 --> 00:03:15,894
okay, it's gonna implement its entire existence with
65
00:03:15,896 --> 00:03:20,098
subviews. Okay, so it's not even gonna have, a DrawRect in
66
00:03:20,100 --> 00:03:24,836
my DropItView. And then I also need, my controller, okay.
67
00:03:24,838 --> 00:03:27,873
So same thing here. But this is gonna be UIViewController,
68
00:03:27,875 --> 00:03:30,909
and I'm gonna call it my DropItViewController, okay.
69
00:03:30,911 --> 00:03:35,147
Same place. Okay, there's my DropItViewController,
70
00:03:35,149 --> 00:03:40,152
you're gonna clear all this stuff out. All right, now that
71
00:03:40,154 --> 00:03:42,754
I have these two classes, I'm gonna make my storyboard
72
00:03:42,756 --> 00:03:46,458
just make it so that this ViewController that I have,
73
00:03:46,460 --> 00:03:49,494
I'm gonna go to the Identity Inspector over here and
74
00:03:49,496 --> 00:03:51,563
instead of having it be just
75
00:03:51,565 --> 00:03:52,097
plain ViewController,
76
00:03:52,099 --> 00:03:54,700
it's gonna be a DropItViewController.
77
00:03:54,702 --> 00:03:58,870
Okay, and same thing inside here I'm just gonna put a view
78
00:03:58,872 --> 00:04:02,441
a generic UI view, so let's scroll down and find that,
79
00:04:02,443 --> 00:04:06,044
where that is. Where is that? Right here it is. Okay,
80
00:04:06,046 --> 00:04:07,746
generic UI view I'm gonna put in here and
81
00:04:07,748 --> 00:04:13,185
I'm gonna change its identify to be DropItView. Okay.
82
00:04:13,187 --> 00:04:16,288
Now I'm gonna have, use constraints here to have this
83
00:04:16,290 --> 00:04:20,025
fill my entire view controller here. You're gonna put
84
00:04:20,027 --> 00:04:22,694
it right there. Use the blue lines to put it on the edges
85
00:04:22,696 --> 00:04:27,399
then we'll go down to here, oops, down to here,
86
00:04:27,401 --> 00:04:29,735
and we'll say reset to suggested constraints.
87
00:04:29,737 --> 00:04:32,304
I always like to look in the Size Inspector, make sure it
88
00:04:32,306 --> 00:04:36,074
did what I wanted, which it looks like it did. Okay. So
89
00:04:36,076 --> 00:04:38,844
I've got this DropItView in here inside my
90
00:04:38,846 --> 00:04:41,880
DropItViewController. I'm also gonna create an outlet from
91
00:04:41,882 --> 00:04:45,050
one to the other. Okay? Okay, so I'm just gonna control drag
92
00:04:45,052 --> 00:04:47,452
into my DropItViewController to create this view.
93
00:04:47,454 --> 00:04:51,323
I'm gonna call it my gameView. You can see there's of type
94
00:04:51,325 --> 00:04:54,393
DropItView, which is good. It's what I want. Okay, so
95
00:04:54,395 --> 00:04:56,795
I've got my gameView. So this is my basic setup of
96
00:04:56,797 --> 00:05:00,132
my game, all right. And really all the controller's gonna do,
97
00:05:00,134 --> 00:05:02,968
maybe set up some gestures, possibly a little bit
98
00:05:02,970 --> 00:05:05,370
of ViewController life cycle here and there, but most
99
00:05:05,372 --> 00:05:10,075
of the logic is going to be inside this DropItView class.
100
00:05:10,077 --> 00:05:16,148
Okay. So Let's make our screen bigger here. All right.
101
00:05:16,150 --> 00:05:19,951
So let's dive right in to this DropItView. I'm gonna have my
102
00:05:19,953 --> 00:05:24,489
DropItView start by being able to put a little square. Okay,
103
00:05:24,491 --> 00:05:27,326
these things that are gonna fall down like Tetric, Tetris,
104
00:05:27,328 --> 00:05:28,260
except for there's gonna be squares.
105
00:05:28,262 --> 00:05:31,063
I'm gonna have a little method that puts a square there.
106
00:05:31,065 --> 00:05:33,899
Okay so I'm gonna need a little bit of some private
107
00:05:33,901 --> 00:05:39,438
data here. How about Private let dropsPerRow = 10.
108
00:05:39,440 --> 00:05:42,941
So this is how many of these little drops that are gonna
109
00:05:42,943 --> 00:05:45,577
fall down will fit across the top of my view,
110
00:05:45,579 --> 00:05:47,846
they're falling down from the top, right? So
111
00:05:47,848 --> 00:05:50,048
this is how many. So ten across is a good number,
112
00:05:50,050 --> 00:05:53,752
I think. And then I'm gonna have a dropSize, okay,
113
00:05:53,754 --> 00:05:56,888
which is gonna be a CGSize. That's gonna be the size
114
00:05:56,890 --> 00:05:58,790
of each of the little things that falls down.
115
00:05:58,792 --> 00:06:01,993
And I'm gonna have that be calculated. Kay?
116
00:06:01,995 --> 00:06:03,628
And I'm gonna calculate it by having the size,
117
00:06:03,630 --> 00:06:07,232
have it be square. And we'll have the size be the width,
118
00:06:07,234 --> 00:06:10,168
whatever our width is at the time we ask this,
119
00:06:10,170 --> 00:06:13,805
divided by the number of drops per row. Right, so
120
00:06:13,807 --> 00:06:17,242
I'm gonna have the size be so that they'll fit across
121
00:06:17,244 --> 00:06:20,011
the width of our view. Now, by the way,
122
00:06:20,013 --> 00:06:22,714
our app might rotate and this width might change, so
123
00:06:22,716 --> 00:06:25,317
the drops might actually change as we rotate and stuff.
124
00:06:25,319 --> 00:06:28,453
But we can decide if that's what we wanted or not, but
125
00:06:28,455 --> 00:06:29,421
this is perfectly fine.
126
00:06:29,423 --> 00:06:32,891
So, now I'm just gonna create a CG size here whose width
127
00:06:32,893 --> 00:06:36,328
is the size and whose height is the size. So it's square,
128
00:06:36,330 --> 00:06:38,230
basically. So this is gonna be the size of a drop,
129
00:06:38,232 --> 00:06:40,232
a square that fits ten across the top.
130
00:06:40,234 --> 00:06:44,035
Now I'm gonna have a function called addDrop, that just
131
00:06:44,037 --> 00:06:47,305
adds one of these things. One of these little squares that's
132
00:06:47,307 --> 00:06:49,975
gonna fall down from the top to the view. So, of course,
133
00:06:49,977 --> 00:06:53,078
we need the size of this drop, to be put into a frame.
134
00:06:53,080 --> 00:06:56,782
So I'm gonna create a frame which is CGRect. The origin
135
00:06:56,784 --> 00:07:00,752
of the frame is gonna be at CGPoint.zero to start.
136
00:07:00,754 --> 00:07:02,287
I'm gonna put it up in the upper left hand corner and
137
00:07:02,289 --> 00:07:05,023
then I'm gonna move it over into one of the slots along
138
00:07:05,025 --> 00:07:08,894
the top. And the size of course is the dropSize. So
139
00:07:08,896 --> 00:07:11,329
let's do that frame movement. So I am gonna move
140
00:07:11,331 --> 00:07:16,435
the origin.x = and really what I want here is a random
141
00:07:16,437 --> 00:07:21,373
float somewhere between 0 and the drops per row. Okay,
142
00:07:21,375 --> 00:07:24,242
that's pretty much where I am going to put it.
143
00:07:24,244 --> 00:07:27,512
So, if I like to sometimes, if I want to have a function on
144
00:07:27,514 --> 00:07:30,816
CG float called random I'll actually make extensions and
145
00:07:30,818 --> 00:07:34,119
so I've made some extensions, here they are right here,
146
00:07:34,121 --> 00:07:38,089
UIKitExtensions.swift which I'm gonna copy in here. And
147
00:07:38,091 --> 00:07:41,493
these extensions are just kinda some convenient things
148
00:07:41,495 --> 00:07:43,929
that I've created that are gonna help my demo go
149
00:07:43,931 --> 00:07:47,098
quicker. So you can see I have created this random function
150
00:07:47,100 --> 00:07:50,302
on CGFloat. I've created a random function on UIColor
151
00:07:50,304 --> 00:07:54,439
which gives me a random color. Kind of fun, right?
152
00:07:54,441 --> 00:07:57,476
And then some CGRect things here, other stuff,
153
00:07:57,478 --> 00:08:01,146
so you can look through this later at your leisure.
154
00:08:01,148 --> 00:08:03,849
But I'm going to use one of here,
155
00:08:03,851 --> 00:08:08,253
which is this CGFloat.random That takes an int,
156
00:08:08,255 --> 00:08:13,325
dropsPerRow. Okay, and so that's gonna create a random
157
00:08:13,327 --> 00:08:16,228
float between 0 and dropsPerRow, and
158
00:08:16,230 --> 00:08:20,599
then I'm going to multiply that times our dropSize width.
159
00:08:20,601 --> 00:08:25,070
So I've picked a spot across the top a random spot across
160
00:08:25,072 --> 00:08:27,539
the top there. So now, I'm gonna create the drop.
161
00:08:27,541 --> 00:08:31,309
I'm just gonna say it's a UIView a generic UIView
162
00:08:31,311 --> 00:08:34,646
whose frame is frame. Okay, that this frame I just created
163
00:08:34,648 --> 00:08:39,117
right here. So I got the drop. And let's set the drops back
164
00:08:39,119 --> 00:08:42,254
color to a random color equals UIColor, that random.
165
00:08:42,256 --> 00:08:44,589
Yeah, that's one of my UI kit extensions that I created
166
00:08:44,591 --> 00:08:48,360
there And then we're gonna add sub-view, the drop, okay?
167
00:08:48,362 --> 00:08:52,330
Just put it, on screen, okay? Make sense? Now,
168
00:08:52,332 --> 00:08:56,835
I'm gonna cause this add drop to happen whenever I tap, so
169
00:08:56,837 --> 00:08:58,870
I'm gonna put a gesture recognizer back here at my
170
00:08:58,872 --> 00:09:01,273
control. A lot of this, by the way, is review hopefully for
171
00:09:01,275 --> 00:09:05,544
you guys, but I'm going to, when my game view gets set
172
00:09:05,546 --> 00:09:08,747
here I'm gonna add a gesture recognizer. So
173
00:09:08,749 --> 00:09:12,551
I'm just gonna say gameView.addGestureRecognizer.
174
00:09:12,553 --> 00:09:15,620
It's gonna be a tap. UITapGestureRecognizer.
175
00:09:15,622 --> 00:09:19,658
The target: is gonna be my self,
176
00:09:19,660 --> 00:09:22,827
and the action: will be, let's say a #selector.
177
00:09:22,829 --> 00:09:25,997
We'll call it addDrop. This one has an argument. Okay?
178
00:09:25,999 --> 00:09:29,434
Cuz it's a gesture recognizer. Okay, so we'll just do that.
179
00:09:29,436 --> 00:09:33,104
And then, I'm gonna have a little func here. Add drop,
180
00:09:33,106 --> 00:09:37,876
which takes a recognizer which is a UITapGestureRecognizer.
181
00:09:37,878 --> 00:09:39,978
What did I do here that's no good? That's good.
182
00:09:39,980 --> 00:09:43,949
We fix that. All right, so when we have TapGesture
183
00:09:43,951 --> 00:09:47,352
we basically wanna check to see if the recognizer state
184
00:09:47,354 --> 00:09:52,390
has moved to ended if it does then there was a tap. And
185
00:09:52,392 --> 00:09:54,059
now, I'm just gonna tell my game view,
186
00:09:54,061 --> 00:09:58,730
okay go ahead and add one of those drops. Okay everybody
187
00:09:58,732 --> 00:10:01,666
see what I did there? Just create a little tap gesture,
188
00:10:01,668 --> 00:10:05,136
just gonna call this method addDrop over in my drop it
189
00:10:05,138 --> 00:10:08,273
view. So let's switch over to the DropItView it again.
190
00:10:08,275 --> 00:10:11,076
It's gonna call it, cause this add drop to happen right here.
191
00:10:11,078 --> 00:10:13,411
All right, so let's go ahead and run,
192
00:10:13,413 --> 00:10:18,116
we'll run on iPhone 6 here. So now, when we tap,
193
00:10:18,118 --> 00:10:24,422
it should put a random color block along the top. Okay,
194
00:10:24,424 --> 00:10:28,293
so tap, there it is, a green one, purple, okay, excellent.
195
00:10:28,295 --> 00:10:28,793
You see, as I click,
196
00:10:28,795 --> 00:10:31,463
it puts more along the top. Now of course, we want these
197
00:10:31,465 --> 00:10:34,366
things to fall down. Okay, we want them to fall down to
198
00:10:34,368 --> 00:10:36,968
the bottom and we know how to do that with Dynamic Animator.
199
00:10:36,970 --> 00:10:39,104
We're just gonna create a gravity behavior, okay, and
200
00:10:39,106 --> 00:10:42,240
the default gravity behavior is downward gravity at 1,000
201
00:10:42,242 --> 00:10:45,977
points per second squared. So that's exactly what we want.
202
00:10:45,979 --> 00:10:48,913
So that's all we need to do is create a gravity behavior and
203
00:10:48,915 --> 00:10:49,180
add it to an animator.
204
00:10:49,182 --> 00:10:53,518
So let's do that. Up here, we're gonna create a private
205
00:10:53,520 --> 00:10:58,990
let gravity equal a ui gravity behavior, okay?
206
00:10:58,992 --> 00:11:01,760
And of course, I also need an animator so I'm gonna say
207
00:11:01,762 --> 00:11:06,297
private let animator which is gonna be a ui dynamic
208
00:11:06,299 --> 00:11:12,037
animator. And actually, let's just create one right here.
209
00:11:12,039 --> 00:11:14,506
You and I dynamic animator. And we know that when
210
00:11:14,508 --> 00:11:16,875
we created dynamic animator that's gonna be animating
211
00:11:16,877 --> 00:11:20,712
views. We wanna say what the reference view is. Okay. Which
212
00:11:20,714 --> 00:11:25,083
in this case is, self. Okay. Where Drop it view is a view.
213
00:11:25,085 --> 00:11:28,486
So referring to yourself. Now, we have an error here.
214
00:11:28,488 --> 00:11:33,091
Anyone know what the problem here is? Okay,
215
00:11:33,093 --> 00:11:37,328
what are we doing right here? Initializing, right?
216
00:11:37,330 --> 00:11:40,231
We're initializing. Can we refer to self when we're
217
00:11:40,233 --> 00:11:44,669
initializing? No, because self is not fully initialized yet.
218
00:11:44,671 --> 00:11:47,405
So we can get around this though with our favorite
219
00:11:47,407 --> 00:11:51,943
lazy var. Okay. So now, it's not gonna initialize this
220
00:11:51,945 --> 00:11:53,044
until someone ask for the animator.
221
00:11:53,046 --> 00:11:54,879
And that's not gonna happen until we're finish the slide.
222
00:11:54,881 --> 00:12:00,485
So we're winning, right? Okay. So we got that. Now, I'm gonna
223
00:12:00,487 --> 00:12:04,589
actually make a public var called here called animating,
224
00:12:04,591 --> 00:12:07,258
which is gonna be a boll, and we start it out equal
225
00:12:07,260 --> 00:12:11,696
to false. And this is gonna be basically how we turn on and
226
00:12:11,698 --> 00:12:13,465
off the animating. If you say, animating = true,
227
00:12:13,467 --> 00:12:15,934
then we're gonna have these blocks animating. If you say,
228
00:12:15,936 --> 00:12:18,770
animating = false, they'll stop animating. Okay, so
229
00:12:18,772 --> 00:12:22,373
it's kind of like the on/off switch for our DropItView. So
230
00:12:22,375 --> 00:12:25,910
what are we gonna do here? I'm gonna say if someone sets
231
00:12:25,912 --> 00:12:30,982
this, then if they set it to true, then I'm going
232
00:12:30,984 --> 00:12:34,619
to animate this. Which all I need to do to animate, is
233
00:12:34,621 --> 00:12:38,423
to take the animator and add the behavior of the gravity.
234
00:12:38,425 --> 00:12:40,959
Okay? As soon as you add the behavior
235
00:12:40,961 --> 00:12:43,061
to the animator it's going to start animating it.
236
00:12:43,063 --> 00:12:47,465
Okay? And similarly if they turn animating off,
237
00:12:47,467 --> 00:12:50,135
then I'm just going to do the opposite, I'm going to remove
238
00:12:50,137 --> 00:12:55,373
That gravity behaviour. Okay, and that's going to stop
239
00:12:56,176 --> 00:13:01,446
it animating. Okay, got that? Let's go ahead and set this.
240
00:13:01,448 --> 00:13:03,148
We're going to set this in our controllers.
241
00:13:03,150 --> 00:13:05,817
I'm going to go back to our controller right here, and
242
00:13:05,819 --> 00:13:07,819
let's do this in our view controller live cycle. So
243
00:13:07,821 --> 00:13:11,556
how about in View Did Appear? As soon as View Did Appear
244
00:13:11,558 --> 00:13:17,996
happens, let's go ahead and set this gameView's animating
245
00:13:18,465 --> 00:13:21,933
To true. Okay, so that's gonna start it animating.
246
00:13:21,935 --> 00:13:28,439
And similarly, on viewWillDissapear, We
247
00:13:28,441 --> 00:13:31,943
will turn the animation off. Cuz we really don't want this
248
00:13:31,945 --> 00:13:36,114
thing animating while we're not even on screen. So. It's
249
00:13:36,116 --> 00:13:40,685
a good place to turn it on and off. Everyone understand that?
250
00:13:40,687 --> 00:13:42,987
Okay. If you don't understand anything I'm saying,
251
00:13:42,989 --> 00:13:45,824
feel free to stop me. Okay? All right. So
252
00:13:45,826 --> 00:13:48,927
that turns it on and off. We got this thing going. The only
253
00:13:48,929 --> 00:13:52,397
thing we need to do here is this behavior, currently there
254
00:13:52,399 --> 00:13:54,599
are no items that have been added to this behavior.
255
00:13:54,601 --> 00:13:57,268
So nothing, this gravity is not affecting anything.
256
00:13:57,270 --> 00:14:00,572
So of course, every time we add a br-, a drop down here,
257
00:14:00,574 --> 00:14:03,308
we're gonna want to add it to the behavior,
258
00:14:03,310 --> 00:14:06,144
gravity.addItem this (drop). Okay, and
259
00:14:06,146 --> 00:14:10,715
that makes that gravity start affecting that drop.
260
00:14:11,184 --> 00:14:13,685
We've got all that? So that's, we've seen all the steps now,
261
00:14:13,687 --> 00:14:15,353
dynamic animation of making it work, right?
262
00:14:15,355 --> 00:14:18,156
We created the animator, we created our behavior, we added
263
00:14:18,158 --> 00:14:20,425
the behavior to the animator, and then we added items that
264
00:14:20,427 --> 00:14:22,694
we wanted affected by that behavior to the behavior.
265
00:14:22,696 --> 00:14:26,231
All right, so let's go see this work.
266
00:14:33,440 --> 00:14:35,406
Okay, so here we go, let's run,
267
00:14:35,408 --> 00:14:36,307
let's try tap this user or
268
00:14:36,309 --> 00:14:40,111
tap just just to here, and look they're falling down.
269
00:14:40,113 --> 00:14:42,680
See they all fall and we can click as many as we want and
270
00:14:42,682 --> 00:14:46,184
they all fall. Now the problem with this from a Tetris point
271
00:14:46,186 --> 00:14:47,819
of view is they just fall off the bottom and
272
00:14:47,821 --> 00:14:51,789
we really want them to hit the bottom and stop, right? And
273
00:14:51,791 --> 00:14:53,324
pile up so that they will make rows and
274
00:14:53,326 --> 00:14:56,427
we can clear the rows out. Right? So the next thing we're
275
00:14:56,429 --> 00:14:58,396
going to do is put a boundary and we're going to put
276
00:14:58,398 --> 00:15:01,065
the boundary all the way around this thing. Okay.
277
00:15:01,067 --> 00:15:03,801
It's a real simple way to create a collision boundary
278
00:15:03,803 --> 00:15:05,703
all the way around this reference view right here.
279
00:15:05,705 --> 00:15:10,742
So let's do that. All right? Go back over here.
280
00:15:10,744 --> 00:15:13,678
So do that we need to create another behavior like
281
00:15:13,680 --> 00:15:17,749
the gravity behavior. And this one I'm gonna call my collider
282
00:15:17,751 --> 00:15:22,420
and it's gonna be UICollisionBehavior. Okay?
283
00:15:22,422 --> 00:15:25,290
Now I could just do that. The problem is I need to
284
00:15:25,292 --> 00:15:28,092
configure this, cuz I wanna tell this collision behavior,
285
00:15:28,094 --> 00:15:32,230
make the bounds of the reference view be a boundary.
286
00:15:32,232 --> 00:15:34,599
So I'm gonna create this in kind of a cool way,
287
00:15:34,601 --> 00:15:37,035
hopefully you'll remember that you can do this.
288
00:15:37,037 --> 00:15:38,870
I'm gonna do it with a closure.
289
00:15:38,872 --> 00:15:42,473
I'm gonna initialize this variable by executing
290
00:15:42,475 --> 00:15:47,412
this closure right here, and remember that we can do that.
291
00:15:47,414 --> 00:15:49,614
So I'm going to create a little local variable here.
292
00:15:49,616 --> 00:15:51,015
Happens to be called the same thing but
293
00:15:51,017 --> 00:15:54,185
wouldn't have to be of the same thing as a variable.
294
00:15:54,187 --> 00:15:56,554
This is just a local inside this enclosure. And
295
00:15:56,556 --> 00:16:02,060
I'm gonna set the collider to translateReferenceBoundsIntoB-
296
00:16:02,062 --> 00:16:04,262
oundary = true. And if you do that,
297
00:16:04,264 --> 00:16:08,132
the UICollisionBehavior will automatically have the bounds
298
00:16:08,134 --> 00:16:11,035
of the reference view be a collision boundary.
299
00:16:11,037 --> 00:16:14,706
Then, we'll return the collider here, okay?
300
00:16:14,708 --> 00:16:17,041
Make sense? Now, this collider,
301
00:16:17,043 --> 00:16:21,412
just like the gravity, here, sorry. If you do this when you
302
00:16:21,414 --> 00:16:24,916
make a closure, don't forget that you have to explicitly
303
00:16:24,918 --> 00:16:30,888
type the var here, and that's because it wants to be
304
00:16:30,890 --> 00:16:34,225
able to check to make sure the return value here matches what
305
00:16:34,227 --> 00:16:37,729
you want it to assign to here. Okay, so
306
00:16:37,731 --> 00:16:39,197
just like we added the gravity behavior,
307
00:16:39,199 --> 00:16:41,132
we're gonna have to add the collider as well, so
308
00:16:41,134 --> 00:16:45,570
just like, here's the gravity, we're gonna do collider, and
309
00:16:45,572 --> 00:16:47,538
same thing obviously when we stop animating,
310
00:16:47,540 --> 00:16:50,975
we want to remove the collider. Okay, and also down
311
00:16:50,977 --> 00:16:53,845
here, we want gravity to work on the drops, we also want
312
00:16:53,847 --> 00:16:56,848
the collider to work on the drops. If I didn't do this,
313
00:16:56,850 --> 00:17:00,385
then the drops would still keep falling off the end,
314
00:17:00,387 --> 00:17:03,121
because the collider would not be behaving,
315
00:17:03,123 --> 00:17:06,557
the collider behavior would not apply to them, okay?
316
00:17:06,559 --> 00:17:13,564
Everybody got that? So let's see if that works. All right,
317
00:17:13,566 --> 00:17:15,633
here we go, drop, and, sure enough, look,
318
00:17:15,635 --> 00:17:21,039
they're piling up. And here when, no, we're never gonna
319
00:17:21,041 --> 00:17:24,409
get a completed row there, how are we gonna do that?
320
00:17:24,411 --> 00:17:28,546
All right, well, we obviously need more behaviors here than
321
00:17:28,548 --> 00:17:31,349
we have. And the next behavior we want to do is a behavior
322
00:17:31,351 --> 00:17:33,818
that makes it so that those things don't rotate like that,
323
00:17:33,820 --> 00:17:37,021
okay, they don't tip over and start piling on top of each
324
00:17:37,023 --> 00:17:40,258
other right there. We want them to stay in nice rows, so
325
00:17:40,260 --> 00:17:42,560
we want to turn rotation off. And to do that,
326
00:17:42,562 --> 00:17:45,029
we need a dynamic item behavior, which I talked about
327
00:17:45,031 --> 00:17:47,632
in lecture. But it's starting to get to a point where I've
328
00:17:47,634 --> 00:17:50,435
kinda got a lot of messy code cuz I'm adding behavior,
329
00:17:50,437 --> 00:17:51,636
behavior, behavior, another one.
330
00:17:51,638 --> 00:17:54,372
I'm gonna add, add, add, remove, remove, add item,
331
00:17:54,374 --> 00:17:57,075
add item. So, I'm gonna do the thing I talk about in lecture
332
00:17:57,077 --> 00:17:59,811
where I create a new behavior which is all these other
333
00:17:59,813 --> 00:18:04,415
behaviors combined, like a composite behavior, okay. So
334
00:18:04,417 --> 00:18:07,652
let's go, File > New up here.
335
00:18:07,654 --> 00:18:11,923
Okay, it's a Cocoa Touch Class. This one is a subclass
336
00:18:11,925 --> 00:18:16,861
of UIDynamicBehavior, okay. And I'm gonna call this
337
00:18:16,863 --> 00:18:20,264
the FallingObjectBehavior, okay, cuz that's what it is,
338
00:18:20,266 --> 00:18:24,502
this is a a thing falling down, a FallingObjectBehavior.
339
00:18:24,504 --> 00:18:26,671
Okay, I don't wanna put it in the top level,
340
00:18:26,673 --> 00:18:28,573
drop it here, I wanna put it down here,
341
00:18:28,575 --> 00:18:30,007
where all the rest of my classes are.
342
00:18:30,009 --> 00:18:32,510
I notice some of you sometimes make that mistake in your
343
00:18:32,512 --> 00:18:36,247
homework. Really nice to keep your files together there.
344
00:18:36,249 --> 00:18:38,116
Okay, so here's my FallingObjectBehavior,
345
00:18:38,118 --> 00:18:42,253
it's a UIDynamicBehavior. And I'm just gonna go and copy and
346
00:18:42,255 --> 00:18:45,123
paste those two behaviors I already have,
347
00:18:45,125 --> 00:18:49,627
these two guys right here, out of here, and over into here,
348
00:18:49,629 --> 00:18:52,763
okay. Cuz now, cuz I want this falling object behavior to
349
00:18:52,765 --> 00:18:56,701
have these two behaviors right here. The other thing
350
00:18:56,703 --> 00:18:59,036
we do when we create a DynamicBehavior subclass is,
351
00:18:59,038 --> 00:19:02,607
we're obviously going to have an init. So I'm gonna override
352
00:19:02,609 --> 00:19:05,443
the init that comes with UIDynamicBehavior,
353
00:19:05,445 --> 00:19:08,646
call super.init, okay, because I, I need to that, from
354
00:19:08,648 --> 00:19:10,615
the rules of init. We haven't talked a lot about inits,
355
00:19:10,617 --> 00:19:13,918
by the way. Again, hopefully you haven't needed them too
356
00:19:13,920 --> 00:19:17,054
much in your, your homeworks. And hopefully you understood
357
00:19:17,056 --> 00:19:19,924
what the reading assignment said about inits. But anyway,
358
00:19:19,926 --> 00:19:22,059
you need to call super.init before you can do stuff.
359
00:19:22,061 --> 00:19:25,062
So I'm going to then add a child behavior,
360
00:19:25,064 --> 00:19:29,867
which is gravity, and I'm gonna add a child behavior,
361
00:19:29,869 --> 00:19:31,169
which is the collider.
362
00:19:31,171 --> 00:19:33,971
Okay, so this is how you build these composites.
363
00:19:33,973 --> 00:19:37,108
You add children behavior, okay. Now,
364
00:19:37,110 --> 00:19:41,179
another nice thing to do is to have a func addItem, okay,
365
00:19:41,181 --> 00:19:44,815
which takes an item which would be a UIDynamicItem,
366
00:19:44,817 --> 00:19:47,885
okay? Remember that UIView implements this protocol.
367
00:19:47,887 --> 00:19:51,088
This is a protocol, okay, UIView implements it.
368
00:19:51,090 --> 00:19:53,891
And here, I just want to make sure that I add this item to
369
00:19:53,893 --> 00:19:58,863
all of my sub, my children, basically, behaviors. So
370
00:19:58,865 --> 00:20:04,802
let's do that. And collider. Okay, and similarly,
371
00:20:04,804 --> 00:20:07,371
we probably, we don't need it for this demo, but
372
00:20:07,373 --> 00:20:11,709
to be complete, we probably want a removeItem as well.
373
00:20:13,379 --> 00:20:16,681
Okay, so now, we've created this new behavior that
374
00:20:16,683 --> 00:20:19,884
we can add items to and that this behavior can be added
375
00:20:19,886 --> 00:20:23,854
to an animator to cause this group of behaviors to happen.
376
00:20:23,856 --> 00:20:26,657
So now let's go back to our DropItView,
377
00:20:26,659 --> 00:20:30,094
and instead having these individual behaviors,
378
00:20:30,096 --> 00:20:34,932
let's create a new behavior, we'll call it dropBehavior,
379
00:20:34,934 --> 00:20:38,436
which is a FallingObjectBehavior.
380
00:20:38,438 --> 00:20:41,105
Okay, so this is one behavior encapsulate all those
381
00:20:41,107 --> 00:20:46,077
behaviors. So now we can put that instead of all these
382
00:20:46,079 --> 00:20:50,348
details. Okay, same thing down here.
383
00:20:52,218 --> 00:20:55,486
All right? So see how this code has gotten so
384
00:20:55,488 --> 00:20:58,589
simple now it fits on one page to do this. And
385
00:20:58,591 --> 00:21:01,359
the actual behavior, the dynamic behavior is now
386
00:21:01,361 --> 00:21:05,663
encapsulated over here in this FallingObjectBehavior instead.
387
00:21:05,965 --> 00:21:08,032
All right? Okay, let's run this and
388
00:21:08,034 --> 00:21:10,301
make sure we haven't broken anything by doing that.
389
00:21:10,303 --> 00:21:12,103
Okay, haven't really changed anything,
390
00:21:12,105 --> 00:21:14,939
I've just moved the code for a different encapsulation.
391
00:21:14,941 --> 00:21:17,575
Here it is, we click, hopefully these things will
392
00:21:17,577 --> 00:21:21,112
still hit the bottom, they do, okay? So all is well. Now,
393
00:21:21,114 --> 00:21:25,249
they're still, can be tippy. See there, they're starting to
394
00:21:25,251 --> 00:21:26,450
tip over. No, they're tipping over, okay.
395
00:21:26,452 --> 00:21:29,020
So they're still tipping, so we need to fix the tippiness.
396
00:21:29,022 --> 00:21:31,355
But now we're gonna fix this tippiness here in
397
00:21:31,357 --> 00:21:34,792
our FallingObjectBehavior. And we're gonna do that by adding
398
00:21:34,794 --> 00:21:38,296
another behavior, okay? This is gonna be the itemBehavior.
399
00:21:38,298 --> 00:21:41,599
This is gonna describe how each of these items behaves
400
00:21:41,601 --> 00:21:45,870
when it goes in all the other behaviors. And we do that with
401
00:21:45,872 --> 00:21:48,239
a UIDynamicItemBehavior, okay, and
402
00:21:48,241 --> 00:21:52,643
I'm gonna initialize this in the same way that I did here,
403
00:21:52,645 --> 00:21:57,248
okay, with a closure. So let's let dib,
404
00:21:57,250 --> 00:22:03,120
a dynamic item behavior, equal a UIDynamicItemBehavior.
405
00:22:03,122 --> 00:22:07,024
And now we can set all kinds of things that dib, this dib,
406
00:22:07,026 --> 00:22:09,026
UIDynamicItemBehavior, knows how to do.
407
00:22:09,028 --> 00:22:13,564
For example, allows rotation? No, so now, our block won't
408
00:22:13,566 --> 00:22:17,268
rotate. We could also do stuff like friction and
409
00:22:17,270 --> 00:22:21,238
also elasticity. So I'm gonna do elasticity of 0.75.
410
00:22:21,240 --> 00:22:23,407
Elasticity of 1 is perfect elasticity.
411
00:22:23,409 --> 00:22:26,677
In other words, no energy is lost by the collision.
412
00:22:26,679 --> 00:22:26,977
They bounce off each other.
413
00:22:26,979 --> 00:22:30,281
So 0.75 is a little energy lost. But, you know, so
414
00:22:30,283 --> 00:22:33,417
I'm not sure what the default elasticity is. But it was low,
415
00:22:33,419 --> 00:22:34,752
because those things came down to the bottom,
416
00:22:34,754 --> 00:22:38,155
they didn't bounce much. They kind of just sat there.
417
00:22:38,157 --> 00:22:40,691
So I'll do that. And now let's return dib.
418
00:22:40,693 --> 00:22:44,061
And of course we need to do the same thing here,
419
00:22:44,063 --> 00:22:48,933
add this child behavior or item behavior. And
420
00:22:48,935 --> 00:22:53,270
same thing here, we want to be able to add items to this. And
421
00:22:53,272 --> 00:23:01,011
we want to be able to remove items as well, okay? So
422
00:23:01,013 --> 00:23:03,581
we've just added this behavior now. These things are all
423
00:23:03,583 --> 00:23:06,751
going to have this kind of behavior as they bounce and
424
00:23:06,753 --> 00:23:09,453
are affected by gravity and things like that. And
425
00:23:09,455 --> 00:23:12,823
notice that to add this stuff, we didn't touch DropItView.
426
00:23:12,825 --> 00:23:15,559
DropItView has not changed, okay? We didn't change
427
00:23:15,561 --> 00:23:17,361
anything there. We're only changing this in
428
00:23:17,363 --> 00:23:20,498
this custom behavior that we've built. Okay, so let's go
429
00:23:20,500 --> 00:23:24,301
ahead and run. See what those changes did, what having no
430
00:23:24,303 --> 00:23:26,771
rotation elasticity did. So here it comes like this, so
431
00:23:26,773 --> 00:23:29,407
here we go. Look, they're much bouncier. And
432
00:23:29,409 --> 00:23:31,742
notice how they'll bounce into each other, okay?
433
00:23:31,744 --> 00:23:33,978
They behave like real objects in the real world.
434
00:23:33,980 --> 00:23:36,747
They're bouncing, and they're not rotating, so they're not
435
00:23:36,749 --> 00:23:39,016
tipping over anymore. They're still bouncing up and down,
436
00:23:39,018 --> 00:23:41,619
though, see? When something lands on a row, it causes them
437
00:23:41,621 --> 00:23:45,489
to bounce up a little bit. But now we're on track for Tetris,
438
00:23:45,491 --> 00:23:49,527
because now we're getting completed rows that we can
439
00:23:49,529 --> 00:23:51,529
have them, have the rows remove.
440
00:23:51,531 --> 00:23:54,732
Now, the code for removing, for kind of removing a row,
441
00:23:54,734 --> 00:23:57,802
it's a little tedious for a demo here. I'm really just
442
00:23:57,804 --> 00:24:01,005
gonna go across the row and do what's called a hit test.
443
00:24:01,007 --> 00:24:02,940
In UI view, it has a hit test, which basically I'm
444
00:24:02,942 --> 00:24:04,375
just looking to see if there's a view there.
445
00:24:04,377 --> 00:24:07,077
So I'm gonna go all the way across each row looking to see
446
00:24:07,079 --> 00:24:09,547
if they're view. And if there's a view in every single
447
00:24:09,549 --> 00:24:12,149
spot, then I'm gonna assume that row is complete and
448
00:24:12,151 --> 00:24:14,752
I'm gonna remove all those views. Okay, so
449
00:24:14,754 --> 00:24:18,889
I wrote that code outside, let's go take a look at it.
450
00:24:18,891 --> 00:24:22,827
We'll put it, let's put it right here. Okay,
451
00:24:22,829 --> 00:24:24,128
it's called removeCompletedRow.
452
00:24:24,130 --> 00:24:28,232
It's just this one method right here, okay?
453
00:24:28,234 --> 00:24:30,801
You can see it goes through, it's doing this hit testing,
454
00:24:30,803 --> 00:24:33,571
it's trying to find the drops to remove. If it finds
455
00:24:33,573 --> 00:24:36,974
the whole row, then it goes through each of the drops and
456
00:24:36,976 --> 00:24:37,441
the drops removed, and
457
00:24:37,443 --> 00:24:41,378
look what it does. It removes it from the drop behavior and
458
00:24:41,380 --> 00:24:42,413
then removes it from superview, so
459
00:24:42,415 --> 00:24:44,748
it just takes that drop right out of there. Okay?
460
00:24:44,750 --> 00:24:48,652
So we're literally just throwing it away, okay? Now,
461
00:24:48,654 --> 00:24:52,857
the question is, when do we call remove completed row?
462
00:24:53,426 --> 00:24:56,760
Okay, do we just kind of call it randomly with an NS timer
463
00:24:56,762 --> 00:25:00,831
or something? No, we're gonna call remove completed row
464
00:25:00,833 --> 00:25:06,103
when all the bouncing stops. Okay, when our
465
00:25:06,105 --> 00:25:09,807
dynamic animation engine pauses, then we're gonna try
466
00:25:09,809 --> 00:25:12,376
to remove a row. Cuz if things are still bouncing, you can't
467
00:25:12,378 --> 00:25:14,478
really be removing rows while things are still bouncing.
468
00:25:14,480 --> 00:25:15,145
So when things have settled down,
469
00:25:15,147 --> 00:25:18,148
then we'll try and remove a complete row if we can.
470
00:25:18,150 --> 00:25:19,517
So, if you remember from lecture,
471
00:25:19,519 --> 00:25:21,952
how do we find out the things have stopped?
472
00:25:21,954 --> 00:25:25,656
We use a dynamic animators delegate, the dynamic animator
473
00:25:25,658 --> 00:25:27,858
will tell it's delegate when things have paused.
474
00:25:27,860 --> 00:25:30,928
When things have stasis, okay? So we're gonna have to do
475
00:25:30,930 --> 00:25:34,298
the same trick here that we did with these other ones, and
476
00:25:34,300 --> 00:25:37,001
use a closure Create this because we just have
477
00:25:37,003 --> 00:25:39,670
the delegates of it. So let's go here and
478
00:25:39,672 --> 00:25:43,607
we'll create a little let animator equal this thing.
479
00:25:43,609 --> 00:25:48,846
And we are just going to set the animator's delegate
480
00:25:48,848 --> 00:25:51,916
to self and return animator.
481
00:25:53,686 --> 00:25:56,353
Okay, and we can use self in here because this is lazy.
482
00:25:56,355 --> 00:26:00,190
Okay. Eh, right? You do that but there is as error here.
483
00:26:00,192 --> 00:26:03,527
What's this error? Can't assign the DropItView to
484
00:26:03,529 --> 00:26:06,630
UIDynamicDeleg eh,DynamicAnimatorDelegate so
485
00:26:06,632 --> 00:26:09,366
we need to go here and say we are a UIDynamic.
486
00:26:09,368 --> 00:26:13,504
Animator delegate, okay? Say we implement this
487
00:26:13,506 --> 00:26:16,440
protocol. Now they're all optional so we don't have to
488
00:26:16,442 --> 00:26:18,776
actually implement any, there's no errors here, but
489
00:26:18,778 --> 00:26:21,045
the one we want is that it will pause.
490
00:26:21,047 --> 00:26:23,781
And that we'll just start typing dynamicAnimator, and
491
00:26:23,783 --> 00:26:25,182
you can se it's actually the very first one.
492
00:26:25,184 --> 00:26:27,585
Here's resume and here's pause. So let's go ahead and
493
00:26:27,587 --> 00:26:30,120
do the pause. So this gets called whenever everything
494
00:26:30,122 --> 00:26:33,123
reach stasis and I'm just going to removeCompletedRow.
495
00:26:33,125 --> 00:26:36,026
If there are no completed rows removeCompletedRow does
496
00:26:36,028 --> 00:26:37,828
nothing, it only removes completed rows
497
00:26:37,830 --> 00:26:42,333
if there are some. Okay, so let's try it see if it works.
498
00:26:42,335 --> 00:26:47,371
Could it really be that easy? Yes. All right here we go.
499
00:26:47,373 --> 00:26:49,306
Drops them down here. Okay, we'll let it
500
00:26:49,308 --> 00:26:53,811
reach stasis where there's no completed row. Okay,
501
00:26:53,813 --> 00:26:57,448
nothing removed, okay, let's add a whole bunch more,
502
00:26:57,450 --> 00:27:03,253
bad enough so that something becomes complete here. Okay,
503
00:27:03,255 --> 00:27:05,522
oops, nope, still need one more,
504
00:27:05,524 --> 00:27:05,889
this column right here, so
505
00:27:05,891 --> 00:27:08,926
we'll keep clicking until we get something in that column.
506
00:27:09,261 --> 00:27:12,162
There it is. Okay, here we go.
507
00:27:13,666 --> 00:27:18,402
Cross your fingers. Boom. Now, notice that
508
00:27:18,404 --> 00:27:21,839
when I remove those views, the animator leapt into action
509
00:27:21,841 --> 00:27:24,908
again. Because it noticed, woah, gravity can now work on
510
00:27:24,910 --> 00:27:26,944
these ones that are not blocked by the other ones.
511
00:27:26,946 --> 00:27:29,947
Okay? And same thing here. And this will happen repeatedly.
512
00:27:29,949 --> 00:27:34,518
Yeah, question. >> Yeah, so the last row or so
513
00:27:34,520 --> 00:27:37,755
sometimes the bottom squares are like a little bit
514
00:27:37,757 --> 00:27:38,689
off the ground. >> Mm-hm.
515
00:27:38,691 --> 00:27:39,390
>> What's going on there?
516
00:27:39,392 --> 00:27:39,957
>> Yeah, you see a little
517
00:27:39,959 --> 00:27:40,391
>> Little bit of white in
518
00:27:40,393 --> 00:27:45,095
there. I'm not 100% sure what that is, but I think
519
00:27:45,097 --> 00:27:48,866
it's just an artifact that all this animation stuff happened,
520
00:27:48,868 --> 00:27:52,603
is happening, in CGFloat space, right?
521
00:27:52,605 --> 00:27:54,538
It's not happening on integer boundaries. And so
522
00:27:54,540 --> 00:27:57,675
it might be that these things landing, maybe they're 0.01,
523
00:27:57,677 --> 00:28:00,711
and so the, it's up enough, round enough so
524
00:28:00,713 --> 00:28:01,712
that it shows the one pixel there.
525
00:28:01,714 --> 00:28:04,415
I'm not really 100% sure why it's not exactly on there.
526
00:28:04,417 --> 00:28:06,917
Remember that, you know, the physics you got a lot
527
00:28:06,919 --> 00:28:09,086
of things bouncing here, off of each other.
528
00:28:09,088 --> 00:28:10,954
Different, amounts and stuff like that.
529
00:28:10,956 --> 00:28:12,356
And these things, and when they could rotate,
530
00:28:12,358 --> 00:28:14,958
you saw, they started tipping over even, and nicking into
531
00:28:14,960 --> 00:28:17,895
each other. So this stuff is not all happening on integer
532
00:28:17,897 --> 00:28:20,864
boundaries, I think that's the bottom line on that.
533
00:28:20,866 --> 00:28:23,634
But I'm not 100% certain. All right, so anyway, this
534
00:28:23,636 --> 00:28:27,871
is working great, okay? We got our tetris engine really
535
00:28:27,873 --> 00:28:30,774
ready to go here, if we just made these not be squares.
536
00:28:30,776 --> 00:28:35,245
And I don't have time to make them be actual you know,
537
00:28:35,247 --> 00:28:35,713
L shapes and all that stuff.
538
00:28:35,715 --> 00:28:38,515
But you get the idea. And you understand how this works.
539
00:28:38,517 --> 00:28:42,953
So, I'm going to Let's turn the rotation back on.
540
00:28:42,955 --> 00:28:46,423
I want to show you something fun here.
541
00:28:46,425 --> 00:28:51,495
Let's make the rotation be true again. Run.
542
00:28:54,834 --> 00:28:59,269
Okay, so put it in here. And these things are gonna bounce
543
00:28:59,271 --> 00:29:00,537
off each other. Uhp! No! No!. No! But, let's
544
00:29:00,539 --> 00:29:02,840
see if removeCompletedRow can work here. Because, actually,
545
00:29:02,842 --> 00:29:05,843
some of these rows still might have a in every slot.
546
00:29:05,845 --> 00:29:09,513
Let's see. There we go! Remove that row. So
547
00:29:09,515 --> 00:29:16,186
it doesn't work quite as well, but it can kind of work.
548
00:29:16,188 --> 00:29:19,857
There we go. Okay? All right.
549
00:29:19,859 --> 00:29:23,060
What's next? Next I'm gonna make it so
550
00:29:23,062 --> 00:29:26,463
that just to show you how the collide, collisions work cuz w
551
00:29:26,465 --> 00:29:27,698
only have collisions with the edges.
552
00:29:27,700 --> 00:29:31,401
I'm gonna put a little round thing in the middle, okay?
553
00:29:31,403 --> 00:29:34,838
That this squares are gonna collide against. Just a little
554
00:29:34,840 --> 00:29:36,940
round circles. So the bottom, there's squeezing him down and
555
00:29:36,942 --> 00:29:39,710
boop bounce of a bit. With just a little round boundary,
556
00:29:39,712 --> 00:29:42,279
okay? Cuz you can put, with these, with the collision
557
00:29:42,281 --> 00:29:45,883
behavior you can put any arbitrary Bezier path as a,
558
00:29:45,885 --> 00:29:46,617
as a boundary anywhere.
559
00:29:46,619 --> 00:29:47,551
And when you're doing your breakout game.
560
00:29:47,553 --> 00:29:49,486
You know, you're probably gonna use those boundaries for
561
00:29:49,488 --> 00:29:52,923
the bricks, for example, okay? And maybe for your paddle
562
00:29:52,925 --> 00:29:54,424
even. You'll be moving the boundary constantly,
563
00:29:54,426 --> 00:29:56,593
because the bricks and the paddle, they don't go flying
564
00:29:56,595 --> 00:29:58,896
around when you hit them they disappear or whatever and
565
00:29:58,898 --> 00:30:00,998
the paddle, when you hit it nothing happens to it,
566
00:30:01,000 --> 00:30:04,067
it stays perfectly the same. Okay, so let's go ahead and
567
00:30:04,069 --> 00:30:06,069
do that. We're just going to put a little round circle
568
00:30:06,071 --> 00:30:10,374
here that is a collision. So, to do that I'm going to go
569
00:30:10,376 --> 00:30:12,676
in my Falling Object behavior here.
570
00:30:12,678 --> 00:30:13,644
And I'm going to make this,
571
00:30:13,646 --> 00:30:16,547
unfortunately my collider is private. And I actually like
572
00:30:16,549 --> 00:30:19,683
to keep it private in my little dynamic behavior here.
573
00:30:19,685 --> 00:30:21,985
But I am going to allow people to add a barrier.
574
00:30:21,987 --> 00:30:25,756
So, I'm gonna create a public function addBarrier. And
575
00:30:25,758 --> 00:30:29,126
you can specify any path you want, a UIBezierPath, that you
576
00:30:29,128 --> 00:30:32,296
want to be the barrier. And let's go ahead and you can
577
00:30:32,298 --> 00:30:35,866
name it as well. So, it will let you give it a name.
578
00:30:35,868 --> 00:30:39,236
And that's because when you put these barriers into
579
00:30:39,238 --> 00:30:41,238
the collider, you name them there as well.
580
00:30:41,240 --> 00:30:44,141
That's how you know which Barrier got hit, okay, and
581
00:30:44,143 --> 00:30:47,077
which barrier you're gonna add or remove or change. So those
582
00:30:47,079 --> 00:30:51,982
are all named. And you just do this with a collider, method.
583
00:30:51,984 --> 00:30:57,788
AddBoundaryWithIdentifier. Okay, here it is, and
584
00:30:57,790 --> 00:31:00,757
so the identifier is the name, and the path is the path that
585
00:31:00,759 --> 00:31:03,660
was passed along in here. If someone calls us, by the way,
586
00:31:03,662 --> 00:31:08,665
I'm also going to conveniently remove any old boundary with
587
00:31:08,667 --> 00:31:12,769
that name. Okay? So if you call this repeatedly,
588
00:31:12,771 --> 00:31:17,174
it'll move the barrier around, just for convenience here.
589
00:31:17,810 --> 00:31:19,743
Okay? So this is a nice public function for
590
00:31:19,745 --> 00:31:23,113
adding a barrier. Let's use that in our drop it view to
591
00:31:23,115 --> 00:31:28,518
add a little barrier in the middle. Now, Where do I
592
00:31:28,520 --> 00:31:31,455
want to put the code that adds that little thing? Well, I
593
00:31:31,457 --> 00:31:34,224
don't know where the middle of my bounds are until my bounds
594
00:31:34,226 --> 00:31:37,494
is set, so I certainly have to wait until my bounds are set,
595
00:31:37,496 --> 00:31:39,863
and actually my bounds could change, right?
596
00:31:39,865 --> 00:31:42,432
They could get rotated. Now my middle is in a different
597
00:31:42,434 --> 00:31:45,702
place, and I want the barrier to always be in the middle. So
598
00:31:45,704 --> 00:31:46,737
you'll remember from our last
599
00:31:46,739 --> 00:31:50,540
demo That when a UI views bounds change,
600
00:31:50,542 --> 00:31:53,543
what gets called? Layout subviews.
601
00:31:53,545 --> 00:31:55,846
All right, it doesn't have a view did layout subviews, it
602
00:31:55,848 --> 00:31:58,248
has the actual layout subviews that lays the subviews out.
603
00:31:58,250 --> 00:32:00,517
So that's a good place to put this thing in the middle.
604
00:32:00,519 --> 00:32:03,553
Because layout subviews gonna get called every single time.
605
00:32:03,555 --> 00:32:06,490
The bounds change. So, that's layoutSubviews.
606
00:32:06,492 --> 00:32:11,161
I'm just gonna subclass else. Super.layoutSubviews. And
607
00:32:11,163 --> 00:32:14,298
all I'm gonna do here in layoutSubviews is to
608
00:32:14,300 --> 00:32:18,302
create a round path, so let path equal UIBezierPath.
609
00:32:18,304 --> 00:32:22,739
And there happens to be a BezierPath ovalInRect
610
00:32:22,741 --> 00:32:24,975
thing to create them. And so
611
00:32:24,977 --> 00:32:28,712
let's have this rect be a CGRect whose center,
612
00:32:28,714 --> 00:32:32,582
this is one of my little UI kit extensions is a,
613
00:32:32,584 --> 00:32:35,552
is a CGRect has a center initializer
614
00:32:35,554 --> 00:32:39,022
will be bounds.mid. That's my mid point of my bounds,
615
00:32:39,024 --> 00:32:42,159
that's also a little extension that I did there. And
616
00:32:42,161 --> 00:32:45,929
we'll have the size of it be the dropSize. Okay so
617
00:32:45,931 --> 00:32:47,164
we're gonna put a little round circle,
618
00:32:47,166 --> 00:32:50,367
the same size as a drop right in the middle. So we have that
619
00:32:50,369 --> 00:32:53,737
path. Let's just call on our drop behavior. We'll add
620
00:32:53,739 --> 00:32:57,841
this barrier with this path. We need to give it a name.
621
00:32:57,843 --> 00:33:00,043
So let's be good programmers here and
622
00:33:00,045 --> 00:33:03,046
do private struct pathNames, we'll call it.
623
00:33:03,048 --> 00:33:06,883
Static let this one we'll call our MiddleBarrier and
624
00:33:06,885 --> 00:33:11,121
we can use any string we want here, just has to be unique so
625
00:33:11,123 --> 00:33:15,325
we'll call it MiddleBarrier. Okay, so
626
00:33:15,327 --> 00:33:19,930
this is PathNames.MiddleBarrier. Okay,
627
00:33:19,932 --> 00:33:25,202
so I'm just adding this named barrier here, to my
628
00:33:25,204 --> 00:33:30,507
to my collision behavior in my drop behavior. All right,
629
00:33:30,509 --> 00:33:36,980
so let's go try it, see what this looks like. All right,
630
00:33:36,982 --> 00:33:40,751
we're dropping them here. Whoop! See that? If we drop
631
00:33:40,753 --> 00:33:45,022
one in the middle, right? Aah, give me a middle one.
632
00:33:45,024 --> 00:33:47,224
There one, see? They're hitting off the middle.
633
00:33:47,226 --> 00:33:49,593
Now this is a little hard to see. See,
634
00:33:49,595 --> 00:33:51,228
you see that it's hitting the barrier, but
635
00:33:51,230 --> 00:33:53,096
you can't actually see the barrier. So
636
00:33:53,098 --> 00:33:56,099
how could we draw that barrier there? Well,
637
00:33:56,101 --> 00:34:00,070
we could implement DrawRect, okay? Our own DrawRect and
638
00:34:00,072 --> 00:34:03,573
draw it, okay, just using just stroke, stroking the thing.
639
00:34:03,575 --> 00:34:05,675
But actually, I'm gonna do something kinda fun. And
640
00:34:05,677 --> 00:34:09,012
this is, really I'm doing it this way to show you, always
641
00:34:09,014 --> 00:34:12,215
think object-oriented when you're building an iOS, okay?
642
00:34:12,217 --> 00:34:14,985
What I'm gonna do instead to draw all that little circle,
643
00:34:14,987 --> 00:34:17,854
is I'm gonna create a new UIView okay,
644
00:34:17,856 --> 00:34:22,025
a new UIView subclass. And all this UIView subclass I'm gonna
645
00:34:22,027 --> 00:34:26,663
create does is it knows how to draw a Bezier Path in itself.
646
00:34:26,665 --> 00:34:29,800
It's just a generic Bezier path-drawing view. And
647
00:34:29,802 --> 00:34:33,970
I'm gonna call it what did I call it? NamedBezierPathsView,
648
00:34:33,972 --> 00:34:37,507
okay, cuz that's what it does. It's gonna have
649
00:34:37,509 --> 00:34:40,210
a dictionary of Bezier paths and it just draws them.
650
00:34:40,212 --> 00:34:42,979
It's really generic. It has nothing to do with DropIt.
651
00:34:42,981 --> 00:34:45,849
I could well end up using this in some other app. Okay, and
652
00:34:45,851 --> 00:34:49,686
this is what this Named BezierPathsView is gonna have.
653
00:34:49,688 --> 00:34:54,357
It's gonna have public var called bezierPaths, okay,
654
00:34:54,359 --> 00:34:56,660
which is going to be a dictionary,
655
00:34:56,662 --> 00:35:00,931
which is String as the key, which is an arbitrary name,
656
00:35:00,933 --> 00:35:06,369
and a UIBezierPath as the value, okay? And that's public
657
00:35:06,371 --> 00:35:09,272
and anybody who wants to use this can just add something to
658
00:35:09,274 --> 00:35:13,610
that, and when they do I'm gonna watch didSet and
659
00:35:13,612 --> 00:35:17,114
do setNeedsDisplay, okay?
660
00:35:17,116 --> 00:35:19,249
So if anyone adds a Bezier path to this,
661
00:35:19,251 --> 00:35:22,252
then I'm going to NeedsDisplay and in my drawRect I'm gonna
662
00:35:22,254 --> 00:35:25,689
draw all these Bezier paths that are in this dictionary.
663
00:35:25,691 --> 00:35:29,159
Which is super simple to do, okay? I'm just gonna say for
664
00:35:29,161 --> 00:35:32,863
_, path) because I don't really care what the names of
665
00:35:32,865 --> 00:35:35,298
them are where I'm drawing them. That's
666
00:35:35,300 --> 00:35:38,034
only w, if someone who's using it wants to replace one that's
667
00:35:38,036 --> 00:35:41,938
already there, they can use the same name. In bezierPaths,
668
00:35:41,940 --> 00:35:44,608
so I'm just enumerating my bezierPath keys and
669
00:35:44,610 --> 00:35:47,377
values, and I don't care about the keys. And I'm just gonna
670
00:35:47,379 --> 00:35:51,281
say path.stroke. So here I've created the world's simplest
671
00:35:51,283 --> 00:35:54,084
little class but it's very powerful drawing class, okay,
672
00:35:54,086 --> 00:35:57,287
cuz it, it, it draws arbitrary Bezier paths. Now if I really
673
00:35:57,289 --> 00:35:59,589
were creating this, I'd probably maybe let you
674
00:35:59,591 --> 00:36:02,926
set the line width and color and other kinds of things, but
675
00:36:02,928 --> 00:36:05,061
here we're just gonna do the, the bar,
676
00:36:05,063 --> 00:36:09,065
bare bones of it here. Okay, now how can I use this
677
00:36:09,067 --> 00:36:13,270
in my DropIt? Anyone have an idea how I could really easily
678
00:36:13,272 --> 00:36:17,374
use this UIView to draw that circle in my DropIt?
679
00:36:20,512 --> 00:36:26,449
How's about if we make DropItView inherit from it?
680
00:36:27,853 --> 00:36:31,521
Now all of a sudden, DropItView has a new instance
681
00:36:31,523 --> 00:36:35,225
variable called BezierPaths which it can set to any
682
00:36:35,227 --> 00:36:39,896
BezierPaths it wants and it'll be drawn in itself, okay?
683
00:36:39,898 --> 00:36:42,999
Everyone got that? So let's do that, okay?
684
00:36:43,001 --> 00:36:46,503
When we set this barrier, let's go ahead and
685
00:36:46,505 --> 00:36:49,439
set our own Bezier paths okay? This Bezier path is,
686
00:36:49,441 --> 00:36:53,143
this is the dictionary that we inherited from that class we
687
00:36:53,145 --> 00:36:56,980
just created. And let's call it middle barrier again.
688
00:36:56,982 --> 00:37:01,117
PathNames.MiddleBarrier, okay, we can give any name we want.
689
00:37:01,119 --> 00:37:06,456
And it's equal to that path we just created. Let's go
690
00:37:06,458 --> 00:37:14,064
take a look at this. And sure enough, there it is. Okay,
691
00:37:14,066 --> 00:37:17,767
now it's stroked with line with one black color fine, but
692
00:37:17,769 --> 00:37:19,569
there it is. And we drop things now,
693
00:37:19,571 --> 00:37:23,139
it's easier to see the things banging off of it.
694
00:37:24,009 --> 00:37:27,143
Okay, and this barrier, of course, if we pile up enough
695
00:37:27,145 --> 00:37:30,780
stuff, the things will start piling up around this barrier,
696
00:37:30,782 --> 00:37:33,817
because everything bounces off of this barrier.
697
00:37:33,819 --> 00:37:35,885
It's causes collision. By the way,
698
00:37:35,887 --> 00:37:37,721
if we put something inside that barrier, okay,
699
00:37:37,723 --> 00:37:42,993
it would bounce around in the inside of the barrier. Okay,
700
00:37:42,995 --> 00:37:46,363
you got that? No remove completed?
701
00:37:46,365 --> 00:37:49,766
Ruh, okay, there we go. There we go.
702
00:37:49,768 --> 00:37:52,535
All right, so that's showing you a little bit there.
703
00:37:52,537 --> 00:37:56,640
Okay the next one I'm gonna do is an attachment behavior. So
704
00:37:56,642 --> 00:38:00,677
I'm gonna allow myself, when a thing drops here,
705
00:38:00,679 --> 00:38:03,747
to grab onto a drop, okay, with my mouse,
706
00:38:03,749 --> 00:38:07,317
or with my finger, if it was on a device and I'd make
707
00:38:07,319 --> 00:38:09,886
an attachment to it. And remember an attachment is like
708
00:38:09,888 --> 00:38:13,990
an iron bar that hooks up from either a point to an item or
709
00:38:13,992 --> 00:38:16,092
between two items. In this case it's gonna be a point,
710
00:38:16,094 --> 00:38:19,796
to point my finger is. And what also cool is, as my, I'm
711
00:38:19,798 --> 00:38:22,632
gonna do a pan gesture and, as my finger moves around,
712
00:38:22,634 --> 00:38:24,934
I'm gonna keep moving that attachment point. And
713
00:38:24,936 --> 00:38:28,638
that bar is gonna be dragged around with it and
714
00:38:28,640 --> 00:38:30,507
the item too, okay?
715
00:38:30,509 --> 00:38:33,343
So that's what we're gonna do. Let's do that attachment
716
00:38:33,345 --> 00:38:36,179
behavior. So, to do the attachment behavior,
717
00:38:36,181 --> 00:38:38,748
I certainly need an attachment behavior.
718
00:38:38,750 --> 00:38:43,453
So I'm gonna create a private var, which is attachment,
719
00:38:43,455 --> 00:38:47,624
UIAttachmentBehavior, okay? Actually we'll have it be
720
00:38:47,626 --> 00:38:50,827
an optional, because we might not have an attachment yet. We
721
00:38:50,829 --> 00:38:54,698
don't have an attachment until we put our finger down on it,
722
00:38:54,700 --> 00:39:00,103
to grab onto it. And when this attachment behavior
723
00:39:00,105 --> 00:39:04,674
gets set to something we need to obviously add
724
00:39:04,676 --> 00:39:08,678
the behavior to our animator, otherwise this attachment
725
00:39:08,680 --> 00:39:12,382
won't do anything, okay? So what do we need to do that?
726
00:39:12,384 --> 00:39:16,319
I'm just going to say if this attachment is not nil, okay,
727
00:39:16,321 --> 00:39:19,222
if someone has set the attachment to be something
728
00:39:19,224 --> 00:39:24,728
other than nil, then animator, addBehavior this attachment.
729
00:39:25,464 --> 00:39:27,230
Okay, everyone understand that? Now, one thing,
730
00:39:27,232 --> 00:39:30,500
by the way, I also am gonna do something we haven't done in
731
00:39:30,502 --> 00:39:34,437
a demo, which is willSet. And willSet happens before the new
732
00:39:34,439 --> 00:39:36,973
value gets set. And what do I wanna do here?
733
00:39:36,975 --> 00:39:40,143
Here I wanna say if my current attachment,
734
00:39:40,145 --> 00:39:46,116
before I've set it, is not nil, then I want to remove it.
735
00:39:48,787 --> 00:39:50,186
Okay, you see why I'm doing this?
736
00:39:50,188 --> 00:39:52,789
Right, if someone sets the attachment to something new,
737
00:39:52,791 --> 00:39:55,625
if there was an old attachment I wanna remove it, okay? So,
738
00:39:55,627 --> 00:39:58,828
here I'm just making sure that every time I set attachment,
739
00:39:58,830 --> 00:40:00,997
the old one gets removed, a new one gets added.
740
00:40:00,999 --> 00:40:04,701
All right with that. Okay, so we've got this attachment,how
741
00:40:04,703 --> 00:40:05,969
are we gonna set this attachment?
742
00:40:05,971 --> 00:40:09,706
We gonna set it to something. I am only gonna allow you to
743
00:40:09,708 --> 00:40:12,442
grab onto the last drop that was dropped.
744
00:40:12,444 --> 00:40:15,278
This cause that you can drop a lot of drops,only the last one
745
00:40:15,280 --> 00:40:18,681
you dropped can you drag grab onto with this pan gesture.
746
00:40:18,683 --> 00:40:21,151
So I'm gonna need a var for the last drop.
747
00:40:21,153 --> 00:40:23,853
So let's go ahead and put a var, I don't know,
748
00:40:23,855 --> 00:40:29,692
we'll put it down here. private var lastDrop, okay,
749
00:40:30,061 --> 00:40:33,930
which is a UIView, okay, and it also could be null,
750
00:40:33,932 --> 00:40:35,465
it might not have dropped anything. And
751
00:40:35,467 --> 00:40:39,869
every time we add a drop, I'm just gonna set the lastDrop
752
00:40:39,871 --> 00:40:44,207
equal to the drop. [INAUDIBLE] understand there, that's just
753
00:40:44,209 --> 00:40:47,143
grabbing the last drop. So, now, I have the last drop,
754
00:40:47,145 --> 00:40:51,848
okay? And I get my finger down, I need to grab onto it,
755
00:40:51,850 --> 00:40:52,949
okay? And create this attachment behavior.
756
00:40:52,951 --> 00:40:56,252
So, how am I gonna do that? All right, let's go here and
757
00:40:56,254 --> 00:41:00,557
create a function called grabDrop which is going to be
758
00:41:00,559 --> 00:41:05,795
a hand gesture Handler, okay. Cuz we're gonna have a pan
759
00:41:05,797 --> 00:41:10,967
gesture be how we grab onto this following drop. Okay,
760
00:41:10,969 --> 00:41:13,937
so first I'm just gonna get the point of the gesture, or
761
00:41:13,939 --> 00:41:19,042
gesturePoint, that's the recognizer locationInView, or
762
00:41:19,044 --> 00:41:22,679
self. Okay, so now we know where that hand gesture is,
763
00:41:22,681 --> 00:41:25,415
either at the start or as we're moving around we know
764
00:41:25,417 --> 00:41:28,418
where it is. So now let's handle these pan
765
00:41:28,420 --> 00:41:32,388
gesture states, okay, recognizer.state.
766
00:41:32,390 --> 00:41:36,593
If it's Began, right, if this thing just began,
767
00:41:36,595 --> 00:41:41,197
then we need to, you know, create the attachment. Okay,
768
00:41:41,199 --> 00:41:44,767
if it's C=changed, okay, person moved the attachment
769
00:41:44,769 --> 00:41:49,205
around, now we need to change the attachments Anchor point.
770
00:41:49,207 --> 00:41:51,708
Okay, cuz remember I said the attachment is gonna attach
771
00:41:51,710 --> 00:41:55,645
a point to that drop and so as we pan around. We're gonna
772
00:41:55,647 --> 00:41:58,214
keep moving the point that it's attached to and that's
773
00:41:58,216 --> 00:42:02,051
gonna drag the trop around with it by the iron bar, okay,
774
00:42:02,053 --> 00:42:06,489
by the stiff bar. Otherwise, I'm just gonna set
775
00:42:06,491 --> 00:42:11,327
the attachment to nil. Okay, because if it's not begin or
776
00:42:11,329 --> 00:42:13,263
change, then it's either ended or some failure,
777
00:42:13,265 --> 00:42:16,232
something like that, I want this attachment to go away.
778
00:42:16,234 --> 00:42:16,933
Okay. If there is an attachment,
779
00:42:16,935 --> 00:42:18,801
I want it to go away so that's the end of the line.
780
00:42:18,803 --> 00:42:24,440
All right. This one's easy, okay. Create the attachment's
781
00:42:24,442 --> 00:42:28,211
anchor point. That's super easy. That's just attachments.
782
00:42:28,213 --> 00:42:32,849
If we have one. AnchorPoint = gesturePoint, okay?
783
00:42:32,851 --> 00:42:36,452
That's it. Okay? Remember, the attachment is just a behavior,
784
00:42:36,454 --> 00:42:38,388
it's an object. It's got a var on it,
785
00:42:38,390 --> 00:42:41,524
which is the anchor point. If it's a point to,
786
00:42:41,526 --> 00:42:43,993
a point connection to a item, it's got an anchor point.
787
00:42:43,995 --> 00:42:45,795
And then we're just gonna change, as you drag around
788
00:42:45,797 --> 00:42:47,730
with the pan, we're just gonna keep changing that. And
789
00:42:47,732 --> 00:42:49,933
the animation system's automatically gonna react to
790
00:42:49,935 --> 00:42:50,833
that. We don't have to do anything.
791
00:42:50,835 --> 00:42:53,636
Just automatically it's going to notice that change and
792
00:42:53,638 --> 00:42:57,507
deal with it. How about creating the attachment? Well,
793
00:42:57,509 --> 00:43:01,044
here we better make sure we have a drop to attach to.
794
00:43:01,046 --> 00:43:05,915
So I'm gonna say if the drop to drop To
795
00:43:05,917 --> 00:43:09,886
attachTo equals the lastDrop. Okay, so in other words,
796
00:43:09,888 --> 00:43:12,822
if we have a lastDrop, cuz we might've just started and
797
00:43:12,824 --> 00:43:14,290
obviously if we start panning when
798
00:43:14,292 --> 00:43:16,492
there's nothing dropping, we can't do it. And
799
00:43:16,494 --> 00:43:22,999
also wanna make sure that that dropToAttachTo is in the view
800
00:43:23,001 --> 00:43:29,205
hierarchy. Okay? So I want to make sure it hasn't
801
00:43:29,207 --> 00:43:33,242
been removed from super view by, remove completed row or
802
00:43:33,244 --> 00:43:37,013
any other way. So if I got a draft drop, I'm just gonna
803
00:43:37,015 --> 00:43:42,285
create an attachment by saying it equals a UI attachment.
804
00:43:44,155 --> 00:43:46,856
Attachment behavior Oops,
805
00:43:46,858 --> 00:43:51,594
we forget attach. Attachment,
806
00:43:51,596 --> 00:43:53,896
UIAttachmentBehavior. And you can see,
807
00:43:53,898 --> 00:43:56,332
here's the different kinds of attachments you can create,
808
00:43:56,334 --> 00:43:59,736
attach to an anchor, an item attached to another item.
809
00:43:59,738 --> 00:44:01,671
You can even attach items to anchors or
810
00:44:01,673 --> 00:44:03,606
other items offsetFromCenter. In other words,
811
00:44:03,608 --> 00:44:05,875
the attachment doesn't have to be in the center of the thing,
812
00:44:05,877 --> 00:44:08,645
it could be on a corner. If you do that Then as you pull
813
00:44:08,647 --> 00:44:10,880
it around that thing's gonna be spinning around because,
814
00:44:10,882 --> 00:44:12,849
you know if you have the corner of a rectangle and you
815
00:44:12,851 --> 00:44:16,185
pull on it its gonna rotate as long as you allow rotation.
816
00:44:16,187 --> 00:44:18,755
Okay? But here I want attached to anchor obviously. I'm
817
00:44:18,757 --> 00:44:21,724
gonna attach it to the anchor which is the pan. The item
818
00:44:21,726 --> 00:44:27,730
is the droptoattachto and the anchor is the gesturePoint.
819
00:44:27,732 --> 00:44:30,667
Okay where the pan gesture went on.
820
00:44:30,669 --> 00:44:33,836
By the way I'm gonna make my game so that once you drab,
821
00:44:33,838 --> 00:44:36,706
grab onto this thing, you can't grab onto it again.
822
00:44:36,708 --> 00:44:38,074
If you grab on and you let it go,
823
00:44:38,076 --> 00:44:41,644
you can't grab it again. So last drop to nil in the case
824
00:44:41,646 --> 00:44:46,482
where I create an attachment. Okay? We have to obviously
825
00:44:46,484 --> 00:44:50,286
add the GestureRecognizer that call this handler, so
826
00:44:50,288 --> 00:44:53,256
we were gonna do that obviously in our controller.
827
00:44:53,258 --> 00:44:55,892
Okay, just like we added this TapGesture, let's go
828
00:44:55,894 --> 00:45:01,731
gameView.addGestureRecognizer, UIPanGesture this time.
829
00:45:01,733 --> 00:45:04,634
The target this time is the gameView.
830
00:45:04,636 --> 00:45:11,607
Okay, and the action is a selector which is
831
00:45:11,609 --> 00:45:18,881
the DropItView.grabDrop with an argument.
832
00:45:18,883 --> 00:45:23,986
Okay, everybody got that? All right, we'll go back here so
833
00:45:23,988 --> 00:45:26,422
you can see this code on the screen at the same time.
834
00:45:26,424 --> 00:45:32,695
To run that. All right, so here we drop it.
835
00:45:32,697 --> 00:45:34,997
I didn't grab it. Okay. We're gonna drop another one.
836
00:45:34,999 --> 00:45:38,768
I grabbed it. Okay, do you see what's happening? It's kind of
837
00:45:38,770 --> 00:45:42,004
hard to see. Watch this. Boing! Okay? So this thing is
838
00:45:42,006 --> 00:45:46,776
bouncing around. I'm dragging it around, ok, with my finger.
839
00:45:46,778 --> 00:45:49,579
Look. I can even it bounce it on the iron bar.
840
00:45:49,581 --> 00:45:51,881
Wooo. Now, it's kind of hard to see this.
841
00:45:51,883 --> 00:45:55,852
Wouldn't it be cool if we could draw a line there?
842
00:45:55,854 --> 00:45:58,755
Okay? So that we could see this iron bar,
843
00:45:58,757 --> 00:46:01,724
that this attachment behavior is creating. And, of course,
844
00:46:01,726 --> 00:46:04,660
we can do that because we inherit from a class that
845
00:46:04,662 --> 00:46:07,196
knows how to draw arbitrary path. So
846
00:46:07,198 --> 00:46:09,766
just like we drew this little one, let's draw that bar.
847
00:46:09,768 --> 00:46:13,269
But it's a little tricky cuz that bar is always changing as
848
00:46:13,271 --> 00:46:16,205
I drag it around with the pen, right? That think is always
849
00:46:16,207 --> 00:46:19,542
changing. So how are we going to have a hook
850
00:46:19,544 --> 00:46:22,578
to know when to draw it? This is where that really cool
851
00:46:22,580 --> 00:46:28,050
UI dynamic behavior mechanism I was talking about is action.
852
00:46:28,052 --> 00:46:30,553
All right. Action is a closure that you can set.
853
00:46:30,555 --> 00:46:34,323
It could get called every time that behavior acts. Perfect.
854
00:46:34,325 --> 00:46:37,960
So I'm gonna put that behavior on that attachment, so that
855
00:46:37,962 --> 00:46:41,664
every time that attachment has any affect on something,
856
00:46:41,666 --> 00:46:44,066
I'm gonna re-draw my line.
857
00:46:44,068 --> 00:46:47,136
Okay. Perfect opportunity to do that. All right. So
858
00:46:47,138 --> 00:46:52,108
I'm gonna do that. Where is a good place to put that?
859
00:46:52,110 --> 00:46:59,248
Let's put it down in where we create the attachment I guess.
860
00:46:59,250 --> 00:47:00,183
Or is that here it is.
861
00:47:00,185 --> 00:47:02,285
Here is where we create the attachment.
862
00:47:02,287 --> 00:47:04,387
This is where we are adding the behavior.
863
00:47:04,389 --> 00:47:07,323
So I'm als, in addition to adding the behavior here, I'm
864
00:47:07,325 --> 00:47:12,328
going to say the attachments. Attachment, I have trouble
865
00:47:12,330 --> 00:47:16,833
typing that. The attachment to action equals some closure.
866
00:47:16,835 --> 00:47:21,270
So in here this is where we wanna draw that line, okay. So
867
00:47:21,272 --> 00:47:23,506
how are we gonna draw this line in here?
868
00:47:23,508 --> 00:47:26,409
How about let's get the attach drop. Okay?
869
00:47:26,411 --> 00:47:29,779
Cuz I wanna draw a line between the point, okay,
870
00:47:29,781 --> 00:47:33,482
that the gestures out and where the attached drop is.
871
00:47:33,484 --> 00:47:35,518
So I need to find that attached drop. So
872
00:47:35,520 --> 00:47:38,354
how can I find the attached drop from the attachment.
873
00:47:38,356 --> 00:47:42,191
Well, here' how we do that. We can get the attachments. Oops,
874
00:47:42,193 --> 00:47:47,697
equals. Get the attach, attachments items.
875
00:47:47,699 --> 00:47:49,999
Okay, that's all the items that it's attaching.
876
00:47:50,001 --> 00:47:52,835
Now it's only gonna be having this one drop attached,
877
00:47:52,837 --> 00:47:55,905
okay it only attaches one drop. But, so this is an array
878
00:47:55,907 --> 00:47:59,108
of items, so I'm gonna get the first one out of there cuz
879
00:47:59,110 --> 00:48:02,378
I know there's only gonna be one. So I've got this first
880
00:48:02,380 --> 00:48:05,281
thing, and I'm gonna make sure it's a UIView, okay?
881
00:48:05,283 --> 00:48:08,651
I just wanna be 100% sure it's a UIView, and I want this var
882
00:48:08,653 --> 00:48:11,754
right here to be of type UIView. Here it's complaining
883
00:48:11,756 --> 00:48:14,390
because this is a closure, and so we have to have self,
884
00:48:14,392 --> 00:48:16,092
explicit self, cuz we're capturing self,
885
00:48:16,094 --> 00:48:18,828
which is actually gonna be a huge problem here, by the way,
886
00:48:18,830 --> 00:48:21,330
to capture self, we'll talk about that in a second,
887
00:48:21,332 --> 00:48:24,267
all right? So now I've got the attached drop,
888
00:48:24,269 --> 00:48:30,907
I just need to create a Bézier path, okay, UIBezierPath.
889
00:48:32,377 --> 00:48:36,812
And I've got a nice lineFrom. Okay,
890
00:48:36,814 --> 00:48:42,184
a UIBezierPath.lineFrom the attachment's anchor point.
891
00:48:42,186 --> 00:48:45,121
Oops, not replacement,
892
00:48:45,123 --> 00:48:51,761
anchorPoint to the attachedDrop.center.
893
00:48:52,797 --> 00:48:56,299
Okay? See what I'm doing there? Okay, we also need some
894
00:48:56,301 --> 00:49:00,236
self dot going on in here. We can do that,
895
00:49:00,238 --> 00:49:05,174
cuz we know we're good on the attachment. What else do
896
00:49:05,176 --> 00:49:09,478
we got here okay, and so we need to give this a name,
897
00:49:09,480 --> 00:49:11,247
right, cuz bezierPath is a dictionary, so
898
00:49:11,249 --> 00:49:13,215
we need give it a name. So let's go back down here and
899
00:49:13,217 --> 00:49:16,252
create another named path, this one we'll call
900
00:49:16,254 --> 00:49:21,324
Attachment. All right, so this is gonna be our
901
00:49:21,326 --> 00:49:25,795
bezierPath[PathNames.Attachme- nt]. Maybe for
902
00:49:25,797 --> 00:49:28,631
readability here, I'll put this on the next line.
903
00:49:28,633 --> 00:49:31,467
Okay? So there, we've created the Bezier path
904
00:49:31,469 --> 00:49:35,438
to show this attachment. Everybody cool with that? Now,
905
00:49:35,440 --> 00:49:37,306
the other thing is, when we remove it,
906
00:49:37,308 --> 00:49:39,275
we better remove that thing. So self dot,
907
00:49:39,277 --> 00:49:42,345
we don't even need self here. We're not inside a closure.
908
00:49:42,347 --> 00:49:47,850
BezierPaths[PathNames.Attachm- ent] = nil.
909
00:49:47,852 --> 00:49:51,153
That's a way to remove something from a dictionary,
910
00:49:51,155 --> 00:49:52,989
by the way, just set it to nil. Okay,
911
00:49:52,991 --> 00:49:57,593
pretty cool with that? Now, as I mentioned we have a serious
912
00:49:57,595 --> 00:50:02,732
problem here with self being captured. Okay, because this
913
00:50:02,734 --> 00:50:06,435
cell, okay, this closure keeps self in the heap. And
914
00:50:06,437 --> 00:50:08,204
self keeps the closure in the heap,
915
00:50:08,206 --> 00:50:12,108
because self.attachment.action equals this closure. So
916
00:50:12,110 --> 00:50:13,676
they each have strong pointers to each other,
917
00:50:13,678 --> 00:50:17,313
they can never leave the heap. Now, this one we can fix with
918
00:50:17,315 --> 00:50:21,951
unowned self. Because we know that this behavior
919
00:50:21,953 --> 00:50:26,022
is gonna leave the heap as soon as self leaves the heap,
920
00:50:26,024 --> 00:50:29,759
so it's perfectly safe here to say, unowned self to
921
00:50:29,761 --> 00:50:33,696
break that loop. Okay, self will never be around when this
922
00:50:33,698 --> 00:50:39,068
attachment behavior's around, still around. Okay, so
923
00:50:39,070 --> 00:50:44,340
let's see if that works. All right,
924
00:50:44,342 --> 00:50:47,376
let's drop a few down. Now I'm gonna grab the next one.
925
00:50:47,378 --> 00:50:48,677
There it is. Okay, so
926
00:50:48,679 --> 00:50:52,281
now we can see a lot better what we're doing here. So
927
00:50:52,283 --> 00:50:58,554
we balance it. Okay? Got it. Make sense?
928
00:50:59,390 --> 00:51:03,659
All right, so that is all I wanted to show you for
929
00:51:03,661 --> 00:51:06,529
behaviors. Any questions about that stuff? I showed you
930
00:51:06,531 --> 00:51:08,564
a lot of behaviors there, attachments, colliders,
931
00:51:08,566 --> 00:51:12,601
item behaviors, gravity, okay, so that's plenty of stuff to
932
00:51:12,603 --> 00:51:17,106
help you get started on your homework there. All right,
933
00:51:17,108 --> 00:51:20,676
let's go back and look at some slides here. So
934
00:51:20,678 --> 00:51:24,480
now I'm gonna talk about Core Motion, okay? Core Motion
935
00:51:24,482 --> 00:51:27,917
is an object-oriented API to get at the motion
936
00:51:27,919 --> 00:51:29,285
sensing hardware on your device.
937
00:51:29,287 --> 00:51:32,221
So that's accelerometer, the gyro, magnetometer,
938
00:51:32,223 --> 00:51:36,025
those kinds of things, all right? Not all devices have
939
00:51:36,027 --> 00:51:40,262
all of this hardware, although in recent years they all do.
940
00:51:40,264 --> 00:51:43,499
But if your app wants to run back on iPhone 4 or
941
00:51:43,501 --> 00:51:46,202
something like that, it doesn't have a gyro, for
942
00:51:46,204 --> 00:51:49,839
example, we kind of, for good programming technique,
943
00:51:49,841 --> 00:51:52,741
wanna check to make sure that we have the hardware, and
944
00:51:52,743 --> 00:51:56,946
you'll see that in a second. You get at all this stuff
945
00:51:56,948 --> 00:51:59,682
via an instance of CMMotionManager.
946
00:51:59,684 --> 00:52:03,719
Now, you really only wanna have one CMMotionManager in
947
00:52:03,721 --> 00:52:04,253
your entire app,
948
00:52:04,255 --> 00:52:06,789
because there's only one gyro in your entire
949
00:52:06,791 --> 00:52:09,792
device, right? So it really makes no sense to have two
950
00:52:09,794 --> 00:52:12,228
motion managers trying to, you know, cross over each
951
00:52:12,230 --> 00:52:14,930
other. You need to coordinate it, so if you really had two
952
00:52:14,932 --> 00:52:18,300
different places in your app where you were accessing
953
00:52:18,302 --> 00:52:21,871
this hardware, then you would need to have a shared motion
954
00:52:21,873 --> 00:52:24,440
manager either as a static on some class
955
00:52:24,442 --> 00:52:26,942
somewhere or maybe part of your app delegate or something
956
00:52:26,944 --> 00:52:29,712
like that, that is, you know, dealing with the fact that two
957
00:52:29,714 --> 00:52:33,315
different people wanna look at the the hardware the same
958
00:52:33,317 --> 00:52:34,416
time, okay?
959
00:52:34,418 --> 00:52:36,285
So how do you use this motion manager to get this
960
00:52:36,287 --> 00:52:40,556
information? Well, first it was two ways really, okay. One
961
00:52:40,558 --> 00:52:44,326
way is by polling, and one way is by getting constant updates
962
00:52:44,328 --> 00:52:46,829
about what's going on, okay. But in either case, first
963
00:52:46,831 --> 00:52:49,165
you're gonna check to see if the hardware's available, and
964
00:52:49,167 --> 00:52:51,600
I'll talk about how you do that. In the polling case,
965
00:52:51,602 --> 00:52:55,404
you're then going to tell the motion manager, okay, I want
966
00:52:55,406 --> 00:52:59,275
accelerometer updates. Okay, and that'll turn that on. And
967
00:52:59,277 --> 00:53:02,178
then you just ask it, what's the current accelerometer,
968
00:53:02,180 --> 00:53:03,746
what's the current accelerometer?
969
00:53:03,748 --> 00:53:04,680
Any time, you can ask it, you want,
970
00:53:04,682 --> 00:53:08,117
you're polling it, basically, asking it. So that's one way,
971
00:53:08,119 --> 00:53:11,453
rare that you would do that way. The second way, again,
972
00:53:11,455 --> 00:53:13,088
you check to see that hardware's available,
973
00:53:13,090 --> 00:53:15,758
then you're gonna set the rate at which you want
974
00:53:15,760 --> 00:53:18,194
the system to tell you about accelerometer or
975
00:53:18,196 --> 00:53:23,265
gyro or magnetometer updates, okay. So five times a second,
976
00:53:23,267 --> 00:53:26,001
30 times a second, probably not much more than that.
977
00:53:26,003 --> 00:53:28,771
I mean, you can only draw it maybe 60 frames a second,
978
00:53:28,773 --> 00:53:31,974
so you're probably not gonna need information much
979
00:53:31,976 --> 00:53:32,141
But you're gonna tell it how often you want it, okay?
980
00:53:32,143 --> 00:53:34,910
more than that.
981
00:53:34,912 --> 00:53:36,612
Then you're gonna give it a closure, okay,
982
00:53:36,614 --> 00:53:39,381
a block of code. And it's going to execute that closure
983
00:53:39,383 --> 00:53:42,017
that many times. Five times a second, 30 times a second. And
984
00:53:42,019 --> 00:53:44,887
that closure, the arguments to it are the current state of
985
00:53:44,889 --> 00:53:48,257
the accelerometer or the current state of the gyro.
986
00:53:48,259 --> 00:53:51,794
Okay, so those are the two ways to do it. So, let's fix,
987
00:53:51,796 --> 00:53:54,797
see how all this works. Checking the availability. You
988
00:53:54,799 --> 00:53:58,367
just ask the CMMotionManager accelerometer Available.
989
00:53:58,369 --> 00:54:00,736
That's a Bool, okay, and it'll tell you yes or no,
990
00:54:00,738 --> 00:54:03,839
or magnetometer available, and that's how you find out. Okay,
991
00:54:03,841 --> 00:54:06,475
so it's as simple as that. So you always wanna check that,
992
00:54:06,477 --> 00:54:08,611
though, because if it's not available and
993
00:54:08,613 --> 00:54:11,080
you start asking for things, you're gonna get errors,
994
00:54:11,082 --> 00:54:14,750
all right? Now, how do you, if you're doing the polling case,
995
00:54:14,752 --> 00:54:16,685
how do you start this thing up? You just say,
996
00:54:16,687 --> 00:54:20,422
start Accelerometer Updates, start Gyro Updates.
997
00:54:20,424 --> 00:54:22,424
And it'll start looking at that hardware. Now,
998
00:54:22,426 --> 00:54:26,762
this is not free. Okay, having this thing start listening to
999
00:54:26,764 --> 00:54:31,367
these pieces of hardware costs battery especially, okay, and
1000
00:54:31,369 --> 00:54:33,068
some CPU too, but it costs battery.
1001
00:54:33,070 --> 00:54:36,005
So don't turn this on unless you're really ready to start
1002
00:54:36,007 --> 00:54:39,041
getting the information, okay? You can ask
1003
00:54:39,043 --> 00:54:42,745
the CMMotionManager whether it is currently, has that
1004
00:54:42,747 --> 00:54:46,015
hardware fired up, basically, by saying, you know,
1005
00:54:46,017 --> 00:54:49,184
Accelerometer active. And that's bool, yes or no. By
1006
00:54:49,186 --> 00:54:51,420
the way, you're noticing all here, you see accelerometer,
1007
00:54:51,422 --> 00:54:54,623
gyro, magnetometer. You also see this thing, deviceMotion.
1008
00:54:54,625 --> 00:54:58,761
Okay, deviceMotion is kind of a special device.
1009
00:54:58,763 --> 00:55:01,730
It's really a combination of the other three. Okay,
1010
00:55:01,732 --> 00:55:04,533
an intelligent combination of the other three, and
1011
00:55:04,535 --> 00:55:08,437
we'll talk about that in a moment here. And
1012
00:55:08,439 --> 00:55:09,338
then to stop the hardware,
1013
00:55:09,340 --> 00:55:12,174
to turn off the accelerometer, turn off the gyro, you just
1014
00:55:12,176 --> 00:55:16,845
say stop whatever updates, okay, and that'll stop it. And
1015
00:55:16,847 --> 00:55:20,215
you wanna do this as soon as you realize, I don't need it,
1016
00:55:20,217 --> 00:55:22,451
even if you only don't need it for a little while and
1017
00:55:22,453 --> 00:55:24,887
you're gonna go back and turn it back on, fine. But
1018
00:55:24,889 --> 00:55:26,755
any time you don't need it, turn it off, okay,
1019
00:55:26,757 --> 00:55:29,258
don't waste the user's battery by having this thing
1020
00:55:29,260 --> 00:55:32,861
working when you're not actually using the data. Okay,
1021
00:55:32,863 --> 00:55:36,065
so that's how you kind of control the hardware.
1022
00:55:36,067 --> 00:55:39,134
Now, again, we're talking about polling here,
1023
00:55:39,136 --> 00:55:40,669
how do I then say, okay, well,
1024
00:55:40,671 --> 00:55:42,371
you got the accelerometer turned on,
1025
00:55:42,373 --> 00:55:45,474
what is the current value of the accelerometer? And you
1026
00:55:45,476 --> 00:55:49,311
just use this CMMotionManager var accelerometer data.
1027
00:55:49,313 --> 00:55:53,982
And it gives you this struct, CMAccelerometerData struct,
1028
00:55:53,984 --> 00:55:57,219
and it's got the acceleration data in x, y, and z, okay?
1029
00:55:57,221 --> 00:56:01,824
So what is this x, y, and z? So here I have a device, okay,
1030
00:56:01,826 --> 00:56:05,561
so, y Okay, y is this axis right here,
1031
00:56:05,563 --> 00:56:10,099
pointing down through the home button, okay?
1032
00:56:10,101 --> 00:56:14,236
So this is the y, okay? It's this vertical axis, okay?
1033
00:56:14,238 --> 00:56:18,440
X is perpendicular to that, cross the device this way.
1034
00:56:18,442 --> 00:56:22,878
So this is y, with the home button being down. This is x.
1035
00:56:22,880 --> 00:56:28,951
Z is out the back. Okay, so if I put this on a table,
1036
00:56:28,953 --> 00:56:33,689
z, what do, what do you think z value would be?
1037
00:56:33,691 --> 00:56:37,693
1.0. Okay, why 1.0? Because x, y, and
1038
00:56:37,695 --> 00:56:41,029
z here are in g. Everyone know what g is, right? 9.8 meters
1039
00:56:41,031 --> 00:56:43,966
per second squared. That's what g is. So z would be 1
1040
00:56:43,968 --> 00:56:47,603
g if I sit it right there, okay? Because the acceleration
1041
00:56:47,605 --> 00:56:51,407
due to gravity is operating on it. And it does tell you
1042
00:56:51,409 --> 00:56:52,741
the acceleration is due to gravity,
1043
00:56:52,743 --> 00:56:54,610
in addition to the acceleration due to the user
1044
00:56:54,612 --> 00:56:56,845
moving the thing around, okay. Acceleration going all
1045
00:56:56,847 --> 00:56:59,882
over the place, okay? But the acceleration due to gravity
1046
00:56:59,884 --> 00:57:04,853
also counts, so that would be x and y 0, z 1.0.
1047
00:57:04,855 --> 00:57:08,157
Similarly if I do this, this would be y 1.0, x and
1048
00:57:08,159 --> 00:57:13,529
z zero. Okay, so that's the acceleration there.
1049
00:57:13,898 --> 00:57:16,298
You get similar information for gyro, okay.
1050
00:57:16,300 --> 00:57:21,870
The gyro is the rotation of the device, okay. What, what,
1051
00:57:21,872 --> 00:57:26,141
how it's rotating, okay. And this is gonna tell you this in
1052
00:57:26,143 --> 00:57:29,378
radians per second, same axes, okay, radians per second,
1053
00:57:29,380 --> 00:57:32,414
how this thing is rotating. Now there's a bias in here.
1054
00:57:32,416 --> 00:57:35,083
I don't know if people know what a gyro bias is, but
1055
00:57:35,085 --> 00:57:36,385
one simple way to think about it is,
1056
00:57:36,387 --> 00:57:40,088
if I'm moving like this, what if I'm also moving like this?
1057
00:57:40,090 --> 00:57:42,658
Okay, you can see how that might make the gyro think this
1058
00:57:42,660 --> 00:57:45,394
is moving faster than it is, because it's moving this way
1059
00:57:45,396 --> 00:57:48,197
in addition to this. So there's a bias in there. And
1060
00:57:48,199 --> 00:57:53,769
also just the the gyro equipment has a bias built
1061
00:57:53,771 --> 00:57:55,637
into it. Now I'm gonna show you in a way, in a second,
1062
00:57:55,639 --> 00:57:59,107
a way to get this rotation as pure rotation without
1063
00:57:59,109 --> 00:58:02,277
that bias, okay? And also a way to get the accelerometer
1064
00:58:02,279 --> 00:58:07,115
data without gravity. The magnetometer is measuring
1065
00:58:07,117 --> 00:58:10,285
magnetic field, right? This is a thing that can find, you can
1066
00:58:10,287 --> 00:58:13,689
find true north. You can basically find out which way
1067
00:58:13,691 --> 00:58:17,526
each of those axes is pointing relative to true north,
1068
00:58:17,528 --> 00:58:20,863
by measuring the magnetic field around your device,
1069
00:58:20,865 --> 00:58:25,834
okay. Now, I talked about the bias and acceleration due to
1070
00:58:25,836 --> 00:58:28,370
gravity. Here's how you get that out of there.
1071
00:58:28,372 --> 00:58:31,974
Instead of using CMAccelerometer, okay, or
1072
00:58:31,976 --> 00:58:36,645
CMGyro, you use this device CMDeviceMotion.
1073
00:58:36,647 --> 00:58:38,313
And that's the one that's the combination of the other
1074
00:58:38,315 --> 00:58:41,316
three. And by combining them and using the data from all of
1075
00:58:41,318 --> 00:58:44,486
them, it can tell you the part of the acceleration that is
1076
00:58:44,488 --> 00:58:48,123
gravity, and the part of it is the user moving it. Okay,
1077
00:58:48,125 --> 00:58:52,928
because it has the gyro, you see? So it knows from the gyro
1078
00:58:52,930 --> 00:58:57,132
what's going on there. Similar, because it has
1079
00:58:57,134 --> 00:59:01,670
the accelerometer, it can unbias the gyro and
1080
00:59:01,672 --> 00:59:06,275
it reports the gyro to you like an airplane. Any of you
1081
00:59:06,277 --> 00:59:08,810
pilots or anybody flown, piloting, know about it,
1082
00:59:08,812 --> 00:59:12,614
okay. So imagine my iPad here is an airplane,
1083
00:59:12,616 --> 00:59:16,618
okay. The way an airplane describes its motion is it
1084
00:59:16,620 --> 00:59:21,123
has, roll. Roll is this, okay, this axis in the middle,
1085
00:59:21,125 --> 00:59:23,225
how you're rolling. Okay, this is roll. So
1086
00:59:23,227 --> 00:59:25,994
when airplanes turn, they roll and that causes them to turn,
1087
00:59:25,996 --> 00:59:29,765
okay. Then there's pitch, that's up and down, right,
1088
00:59:29,767 --> 00:59:31,700
airplane climbing and descending,
1089
00:59:31,702 --> 00:59:35,037
okay. And then there's yaw. Yaw is when an airplane,
1090
00:59:35,039 --> 00:59:37,472
let's say there's a fierce wind coming this way,
1091
00:59:37,474 --> 00:59:38,640
the airplane wants to go that way but
1092
00:59:38,642 --> 00:59:42,644
it actually has to turn this way to fight this wind. Okay,
1093
00:59:42,646 --> 00:59:45,647
the amount it's turned along this axis is
1094
00:59:45,649 --> 00:59:50,552
the yaw, okay. So you've got roll, pitch and yaw. Okay,
1095
00:59:50,554 --> 00:59:54,256
so that's what we reported to you with CM device motion and
1096
00:59:54,258 --> 00:59:56,091
that is independent of any motion,
1097
00:59:56,093 --> 00:59:58,460
whether the airplane is going fast or sitting still,
1098
00:59:58,462 --> 01:00:01,129
whatever, you're going to get that roll, pitch and yaw,
1099
01:00:01,131 --> 01:00:05,467
okay. So that's device motion, really powerful mechanism.
1100
01:00:05,469 --> 01:00:07,302
And if you're doing anything with a gyro you're probably
1101
01:00:07,304 --> 01:00:11,306
gonna be using Device Motion instead of CM Gyro. Okay,
1102
01:00:11,308 --> 01:00:14,142
magnetic field, with the CM Device Motion,
1103
01:00:14,144 --> 01:00:20,048
it can tell you the accuracy of the magnetic field, okay.
1104
01:00:20,684 --> 01:00:23,485
All right, so, that, we talked about how to poll.
1105
01:00:23,487 --> 01:00:25,988
You just ask for the information you want.
1106
01:00:25,990 --> 01:00:28,890
How about if you wanna be notified 5 times a second,
1107
01:00:28,892 --> 01:00:32,594
or 30 times a second of what's going on, very easy.
1108
01:00:32,596 --> 01:00:35,230
You just call startAccelerometer updates or
1109
01:00:35,232 --> 01:00:38,667
startGyro updates or startdevicemotion updates to
1110
01:00:38,669 --> 01:00:39,901
queue. And you give it a queue,
1111
01:00:39,903 --> 01:00:43,505
this is not a dispatc_queueT, this is an nsoperation queue,
1112
01:00:43,507 --> 01:00:46,408
the object oriented version I talked about. But
1113
01:00:46,410 --> 01:00:48,910
probably you're gonna be passing NSoperationmainqueue,
1114
01:00:48,912 --> 01:00:51,546
or maybe you'll use NSoperationcurrentqueue if
1115
01:00:51,548 --> 01:00:54,383
you're on a different queue. You don't really wanna do this
1116
01:00:54,385 --> 01:00:56,618
on the main queue if you're gonna be doing it at a very
1117
01:00:56,620 --> 01:00:59,554
high rate, like 30 frames a sec, 30 calls a second unless
1118
01:00:59,556 --> 01:01:03,258
you're doing something really simple. Just think about it,
1119
01:01:03,260 --> 01:01:05,227
you don't want the main queue busy doing that.
1120
01:01:05,229 --> 01:01:08,296
But if you're doing 4 or 5 frames a second, absolutely,
1121
01:01:08,298 --> 01:01:10,866
doing something simple, you can do that.
1122
01:01:10,868 --> 01:01:13,268
Then you give it a handler. This is just a block,
1123
01:01:13,270 --> 01:01:16,271
a closure, and you can see that closure as two arguments.
1124
01:01:16,273 --> 01:01:18,840
One is that same CMAccelerometerData we
1125
01:01:18,842 --> 01:01:22,811
saw from before, the x, y and z, and g, that thing,
1126
01:01:22,813 --> 01:01:25,480
okay, and also an error possibly.
1127
01:01:25,482 --> 01:01:26,448
Okay, these are optionals,
1128
01:01:26,450 --> 01:01:29,551
probably if one's nil the other one's not, vice-versa.
1129
01:01:29,553 --> 01:01:31,453
Okay, so this is just closure and inside that closure,
1130
01:01:31,455 --> 01:01:34,489
you can do anything you want with the AccelerometerData.
1131
01:01:34,491 --> 01:01:38,427
All right, similar with the gyro, startGyroUpdatesToQueue,
1132
01:01:38,429 --> 01:01:40,495
you're gonna get the gyro data.
1133
01:01:40,497 --> 01:01:43,498
StartMagnetometerUpdatesToQu- eue, you're gonna get
1134
01:01:43,500 --> 01:01:45,600
the magnetometer data. And of course,
1135
01:01:45,602 --> 01:01:49,471
startDeviceMotionUpdatesToQu- eue, okay. You get the CM,
1136
01:01:49,473 --> 01:01:53,108
CM button DeviceMotion, that's that thing that has vars like
1137
01:01:53,110 --> 01:01:57,713
gravity and attitude for the roll, pitch and yaw, that kind
1138
01:01:57,715 --> 01:02:01,316
of stuff. Just to talk a little bit about the errors,
1139
01:02:01,318 --> 01:02:03,251
you're, of course, gonna want to look in the documentation,
1140
01:02:03,253 --> 01:02:06,788
but some of the errors you can get are like you can't find
1141
01:02:06,790 --> 01:02:09,991
true north maybe, too much interference.
1142
01:02:09,993 --> 01:02:12,394
Maybe this activity's not authorized. Okay,
1143
01:02:12,396 --> 01:02:15,997
maybe user, you're not authorized to actually measure
1144
01:02:15,999 --> 01:02:17,999
the device's motion. Okay, that could be too.
1145
01:02:18,001 --> 01:02:20,068
So you do wanna be able to check for these errors to make
1146
01:02:20,070 --> 01:02:24,940
sure you're actually getting good data. All right, so
1147
01:02:24,942 --> 01:02:28,443
how often you get these closures, called, is set with
1148
01:02:28,445 --> 01:02:31,513
these vars in motion manager, okay. Update interval,
1149
01:02:31,515 --> 01:02:34,149
this is just the number of seconds between calls.
1150
01:02:34,151 --> 01:02:38,854
So like 0.25 would be four times a second, okay? It is
1151
01:02:38,856 --> 01:02:42,724
okay to, to have multiple, closures registered. You could
1152
01:02:42,726 --> 01:02:46,328
say startAccelerometer updates with handler and then call
1153
01:02:46,330 --> 01:02:48,029
it again with another handler. But understand,
1154
01:02:48,031 --> 01:02:50,766
they're all gonna be called at this same interval cuz there's
1155
01:02:50,768 --> 01:02:53,869
only this one motion manager that you're gonna create for
1156
01:02:53,871 --> 01:02:54,503
your whole app, okay.
1157
01:02:54,505 --> 01:02:56,505
All right, so let's take a look at this.
1158
01:02:56,507 --> 01:02:59,641
Let's take DropIt, and let's make the gravity that's in
1159
01:02:59,643 --> 01:03:03,979
DropIt be real gravity, okay. Right now the gravity just
1160
01:03:03,981 --> 01:03:06,281
goes down towards the home button, but let's make it so
1161
01:03:06,283 --> 01:03:08,683
that it's actual gravity. So as I move my thing around,
1162
01:03:08,685 --> 01:03:12,921
those blocks are gonna start swinging towards real gravity.
1163
01:03:13,357 --> 01:03:18,760
All right, so how are we gonna do that? I'm going to
1164
01:03:18,762 --> 01:03:25,767
go to my DropIt view right here, I'm gonna add a new var,
1165
01:03:25,769 --> 01:03:31,273
public var called realGravity, and it's gonna be a Bool,
1166
01:03:31,275 --> 01:03:35,410
okay? I'll start it out being false. And if this is on,
1167
01:03:35,412 --> 01:03:38,313
then I'm gonna use real gravity in my DropIt.
1168
01:03:38,315 --> 01:03:40,515
If it's off, I'm gonna use fake gravity,
1169
01:03:40,517 --> 01:03:44,452
the gravity I already have basically, all right? And so
1170
01:03:44,454 --> 01:03:47,455
let's go back to our controller and
1171
01:03:47,457 --> 01:03:48,824
set this to be true, so
1172
01:03:48,826 --> 01:03:52,394
I'm just gonna do it here in the didSet here,
1173
01:03:52,396 --> 01:03:57,599
gameView.realgravity. Gravity and real,
1174
01:03:57,601 --> 01:04:01,870
realGravity equals true. Okay, so
1175
01:04:01,872 --> 01:04:05,006
we've got realGravity turned on. And how am I gonna make
1176
01:04:05,008 --> 01:04:08,743
this real gravity work? Well anytime someone changes this,
1177
01:04:08,745 --> 01:04:11,446
I'm gonna call some method update real gravity or
1178
01:04:11,448 --> 01:04:14,883
something, and this is the method that's gonna have to
1179
01:04:14,885 --> 01:04:17,853
turn the accelerometer on to start using it. Okay?
1180
01:04:17,855 --> 01:04:22,557
So that's a private funk update real gravity.
1181
01:04:22,559 --> 01:04:23,625
Okay, so this is going to be the guts.
1182
01:04:23,627 --> 01:04:26,494
This is going to be the thing that's actually doing
1183
01:04:26,496 --> 01:04:27,729
the work here. All right, so
1184
01:04:27,731 --> 01:04:30,465
what do update real gravity going to do? Well,
1185
01:04:30,467 --> 01:04:32,434
it's going to do something with the accelerometer. And
1186
01:04:32,436 --> 01:04:34,669
we to use the accelerometer to get the gravity.
1187
01:04:34,671 --> 01:04:35,804
I'm not going to use device motion,
1188
01:04:35,806 --> 01:04:40,075
okay, just to make it simple, we'll use the accelerometer.
1189
01:04:40,077 --> 01:04:43,211
We want the real gravity anyway so we'll use that. So I
1190
01:04:43,213 --> 01:04:46,281
need a private var here which is that motion manager. As I
1191
01:04:46,283 --> 01:04:48,984
told you, you need a motion manager to do any of this. So,
1192
01:04:48,986 --> 01:04:52,087
we'll just say that equals CMMotionManager.
1193
01:04:52,089 --> 01:04:54,122
I'm not using any of this in
1194
01:04:54,124 --> 01:04:56,391
any other part of my app. So I'm putting it here.
1195
01:04:56,393 --> 01:04:59,227
Again, if I was using it in two different places,
1196
01:04:59,229 --> 01:05:02,964
I'd have to make this be some sort of shared resource.
1197
01:05:02,966 --> 01:05:04,699
Notice it is complaining right here, okay?
1198
01:05:04,701 --> 01:05:05,934
Does never heard of a CMMotionManager and
1199
01:05:05,936 --> 01:05:08,003
it doesn't know what the heck we're talking about.
1200
01:05:08,005 --> 01:05:11,039
This is because this is in a different framework,
1201
01:05:11,041 --> 01:05:15,677
import,CcoreMotion, okay? All this CoreMotion stuff you have
1202
01:05:15,679 --> 01:05:19,848
to import CoreMotion. All right, so we have our
1203
01:05:19,850 --> 01:05:22,450
motion manager right here. How are we going to use our
1204
01:05:22,452 --> 01:05:25,987
motion manager to grab that accelerometer, information?
1205
01:05:25,989 --> 01:05:31,226
So if we want real gravity, then we're going to do that.
1206
01:05:31,228 --> 01:05:32,928
By the way, if we don't want real gravity,
1207
01:05:32,930 --> 01:05:35,997
then I'm going to tell the motion manager to stop
1208
01:05:35,999 --> 01:05:40,001
accelerometer updates. Again, I always want to stop that
1209
01:05:40,003 --> 01:05:43,939
updating happening any time I don't want the updates, okay.
1210
01:05:43,941 --> 01:05:46,741
So we try to keep that off as much as possible. But if we do
1211
01:05:46,743 --> 01:05:51,579
want real gravity, then first I'm gonna check to see, if
1212
01:05:51,581 --> 01:05:57,886
the motionManager accelerometer is available.
1213
01:05:57,888 --> 01:06:02,257
Okay, as promised, this is first thing we need to do.
1214
01:06:02,259 --> 01:06:03,758
Actually I'm also going to see here if
1215
01:06:03,760 --> 01:06:06,628
the accelerometer is already active. Cuz maybe I already
1216
01:06:06,630 --> 01:06:09,364
called updateRealGravity and it's already running. So
1217
01:06:09,366 --> 01:06:14,903
I'm gonna actually say and the motionManager.accelerometerAc-
1218
01:06:14,905 --> 01:06:19,607
tive is false. Okay? So if the accelerometer is available and
1219
01:06:19,609 --> 01:06:20,075
it's not already running,
1220
01:06:20,077 --> 01:06:24,079
then I need to fire this thing up, okay? So let's fire it up.
1221
01:06:24,081 --> 01:06:27,949
Let's start with our update interval. So I'm going to have
1222
01:06:27,951 --> 01:06:30,919
the accelerometer update interval be, I,
1223
01:06:30,921 --> 01:06:35,090
maybe 0.25, 0.2 is probably enough. You know, gravity
1224
01:06:35,092 --> 01:06:38,193
doesn't really need to change that much more often than that
1225
01:06:38,195 --> 01:06:40,161
as I am moving my device around. This is a number
1226
01:06:40,163 --> 01:06:43,031
I could play with. Again, this should probably, not probably,
1227
01:06:43,033 --> 01:06:45,600
definitely be a constant but we're time constrained here so
1228
01:06:45,602 --> 01:06:50,872
I'm not gonna do that. So now I'm just gonna have the motion
1229
01:06:50,874 --> 01:06:54,309
manager start giving me updates to queue,
1230
01:06:54,311 --> 01:06:57,612
okay? So this is how I started updating. Now, what queue am I
1231
01:06:57,614 --> 01:06:59,581
gonna use? I'm gonna use the main cue here cuz
1232
01:06:59,583 --> 01:07:01,916
I'm only doing it four times a second and I'm only just
1233
01:07:01,918 --> 01:07:04,052
gonna set that gravity thing, that's all I'm gonna do, so
1234
01:07:04,054 --> 01:07:07,222
that's really lightweight so I can just use the main queue.
1235
01:07:07,224 --> 01:07:11,059
So, enter operation queue main queue. Okay,
1236
01:07:11,061 --> 01:07:14,362
here's the handler, the CM handler. I always recommend
1237
01:07:14,364 --> 01:07:17,098
double clicking on these things because it fills
1238
01:07:17,100 --> 01:07:21,603
out really nicely. Your arguments and
1239
01:07:21,605 --> 01:07:25,106
stuff like that. I'm also going to use the closing or
1240
01:07:25,108 --> 01:07:29,911
the trailing closure syntax here to do this, all right.
1241
01:07:29,913 --> 01:07:32,747
So I'm going to start this accelerometer,
1242
01:07:32,749 --> 01:07:34,616
here is my closure that I have to do,
1243
01:07:34,618 --> 01:07:39,621
okay? So, this accelerometer data, I'll call it data. This
1244
01:07:39,623 --> 01:07:44,459
error I'll call error. Okay, so, how am I gonna do this?
1245
01:07:44,461 --> 01:07:48,630
Well, this data might be nil, it's an optional. So, I'm
1246
01:07:48,632 --> 01:07:51,132
gonna see if I can get the accelerometer data first.
1247
01:07:51,134 --> 01:07:54,702
So, let's say if I can let, we'll call the x,
1248
01:07:54,704 --> 01:07:57,539
I'm only caring about x and y. Don't care about z, okay,
1249
01:07:57,541 --> 01:08:01,242
because I'm gonna have the my little drop it is two
1250
01:08:01,244 --> 01:08:03,244
dimensional so it doesn't care about z. So
1251
01:08:03,246 --> 01:08:07,582
if I can let dx equal the data acceleration in x, and
1252
01:08:07,584 --> 01:08:12,020
also we can let dy equal the data's acceleration in y so
1253
01:08:12,022 --> 01:08:16,124
then I'm just getting simultaneously getting and
1254
01:08:16,126 --> 01:08:19,794
checking to be sure the data is not nil, so
1255
01:08:19,796 --> 01:08:21,062
I've got the dx a new y.
1256
01:08:21,064 --> 01:08:28,803
Let's first try to just set the drop behaviors gravity's,
1257
01:08:28,805 --> 01:08:33,808
gravity direction, to just be a CG vector
1258
01:08:33,810 --> 01:08:37,812
which is dx and dy, okay? So we're gonna try this first.
1259
01:08:37,814 --> 01:08:41,483
Now, this is not gonna work because gravity is private. So
1260
01:08:41,485 --> 01:08:44,285
let's just go over to our behavior, falling object
1261
01:08:44,287 --> 01:08:47,655
behaviour here. I'm gonna make this not be private. Okay,
1262
01:08:47,657 --> 01:08:50,191
just for expediency. Maybe we wanna make some other public
1263
01:08:50,193 --> 01:08:53,128
API like we did with Glider here. I'm just gonna make this
1264
01:08:53,130 --> 01:08:57,932
public. Make it a little, go a little faster. All right, so
1265
01:08:57,934 --> 01:09:01,669
here we are, we're saving this, gravity here.
1266
01:09:02,305 --> 01:09:05,206
Yeah good one, that's what, okay got it.
1267
01:09:05,208 --> 01:09:09,511
Very good catch, bonus points, all right, so right.
1268
01:09:09,513 --> 01:09:13,848
So there's that, one other thing I wanna be careful of
1269
01:09:13,850 --> 01:09:17,986
here is memory cycle. Okay I've got this accelerometer,
1270
01:09:17,988 --> 01:09:20,655
cuz motion manager has accelerometer that that
1271
01:09:20,657 --> 01:09:23,925
thing has a hold of this closure, I have a hold of it.
1272
01:09:23,927 --> 01:09:25,426
Okay I have a hold the of motion manger. And
1273
01:09:25,428 --> 01:09:29,597
Motion manager has a hold of me, okay? So that's not good.
1274
01:09:29,599 --> 01:09:33,268
Again, we can do unowned to solve this one. Oops don't put
1275
01:09:33,270 --> 01:09:37,505
it there. Unowned goes over here. We can do unowned
1276
01:09:37,507 --> 01:09:42,177
self here because as soon as self leaves the heap,
1277
01:09:42,179 --> 01:09:44,512
this motionManager is gonna leave the heap with it,
1278
01:09:44,514 --> 01:09:46,915
which means this closure is gonna go out too.
1279
01:09:46,917 --> 01:09:49,417
So this closure will never get executed with self not in
1280
01:09:49,419 --> 01:09:52,787
the heap. Once self leaves the heap, this guy goes with it.
1281
01:09:52,789 --> 01:09:56,224
So we can do unowned here, to do that. The only other
1282
01:09:56,226 --> 01:10:00,562
thing I want to do here is if, I, I want to try and turn this
1283
01:10:00,564 --> 01:10:04,299
accelerator updates off as much as possible. So another
1284
01:10:04,301 --> 01:10:08,436
time I want to turn it off is if I'm not animating, right?
1285
01:10:08,438 --> 01:10:11,940
If I have my dropBehavior, okay, and it's not currently
1286
01:10:11,942 --> 01:10:15,243
animating, I do not want to be continuing to do this.
1287
01:10:15,245 --> 01:10:19,814
So, I'm gonna say only do this if my dropBehavior,
1288
01:10:19,816 --> 01:10:24,185
it's dynamic animator is not nil. That's how you can tell
1289
01:10:24,187 --> 01:10:25,887
whether a behaviour is currently being animated.
1290
01:10:25,889 --> 01:10:29,490
If it has a dynamic animator, it's being animated. If not,
1291
01:10:29,492 --> 01:10:34,028
it's not. So, if it's not nil, then we'll do this. Otherwise,
1292
01:10:34,030 --> 01:10:38,099
I'm going to stop this motion manager, okay?
1293
01:10:38,101 --> 01:10:41,703
Stop accelerometer updates in this motion manager. You see
1294
01:10:41,705 --> 01:10:45,039
how I'm trying to stop this thing as much as possible?
1295
01:10:45,041 --> 01:10:46,407
The only thing about this, though,
1296
01:10:46,409 --> 01:10:49,310
is if I do stop it because I stop animating, then I better
1297
01:10:49,312 --> 01:10:51,646
be sure that when I start animating up again.
1298
01:10:51,648 --> 01:10:53,181
Remember this is that animating bar,
1299
01:10:53,183 --> 01:10:54,782
you set it to true to start animating.
1300
01:10:54,784 --> 01:10:58,152
I better update my real gravity here to make sure that
1301
01:10:58,154 --> 01:11:01,556
I start the accelerometer back up again, okay? So
1302
01:11:01,558 --> 01:11:03,124
that allows me to start and stop animating.
1303
01:11:03,126 --> 01:11:06,394
It stops the accelerometers, every time I restart it,
1304
01:11:06,396 --> 01:11:09,264
it starts them again, okay? So let's try this and
1305
01:11:09,266 --> 01:11:11,299
see if it works. Now, this is the first time in this
1306
01:11:11,301 --> 01:11:12,200
class where I'm having to actually,
1307
01:11:12,202 --> 01:11:15,903
I can't do it in a simulator because the simulator has no,
1308
01:11:15,905 --> 01:11:17,505
accelerometer or anything like that, so
1309
01:11:17,507 --> 01:11:21,376
we're gonna be doing it here on this other device.
1310
01:11:21,378 --> 01:11:26,147
Let me run over here and show you this. All right, so
1311
01:11:26,149 --> 01:11:30,551
let's run this on our device here, here we go,
1312
01:11:30,553 --> 01:11:33,187
it's coming up here, there it is, we see our little circle,
1313
01:11:33,189 --> 01:11:38,793
if I tap, it's not working. What's going on here?
1314
01:11:38,795 --> 01:11:41,396
Why wouldn't that be working? I got the gravity going on
1315
01:11:41,398 --> 01:11:46,501
there, hm. Maybe if I turn my thing upside down, woah!
1316
01:11:46,503 --> 01:11:48,703
That worked, I turned my iPad upside down, and
1317
01:11:48,705 --> 01:11:52,874
all of a sudden gravity seems to be backwards here, okay?
1318
01:11:52,876 --> 01:11:57,111
Now why would gravity be backwards? The answer is,
1319
01:11:57,113 --> 01:12:01,282
because our drawing coordinate system has 0,0 in the upper
1320
01:12:01,284 --> 01:12:04,752
left. So this is working fine, it's falling down toward zero,
1321
01:12:04,754 --> 01:12:08,022
it's just that's in the upper left. So we need to add some
1322
01:12:08,024 --> 01:12:12,126
code here that knows what the orientation of our device is,
1323
01:12:12,128 --> 01:12:16,297
and adjusts, okay, for the fact that it's upside down,
1324
01:12:16,299 --> 01:12:19,567
basically. Also, when we turn sideways, and
1325
01:12:19,569 --> 01:12:21,369
we're looking at landscape mode,
1326
01:12:21,371 --> 01:12:24,172
we wanna make sure that's doing the right thing as well.
1327
01:12:24,174 --> 01:12:28,843
So let's put that in here. Okay, that goes right, so
1328
01:12:28,845 --> 01:12:32,613
let's put that right in here. Okay, we've got our dx and dy.
1329
01:12:32,615 --> 01:12:36,751
All I'm gonna do here is I'm gonna switch on my device's,
1330
01:12:36,753 --> 01:12:40,621
this is how you get the current device's orientation,
1331
01:12:40,623 --> 01:12:43,124
okay. This will return the orientation of the current
1332
01:12:43,126 --> 01:12:48,196
device. And so for example, in the case of portrait, okay,
1333
01:12:48,198 --> 01:12:51,666
we know that portrait is upside down, so
1334
01:12:51,668 --> 01:12:54,535
I'm gonna say dy = -dy in portrait.
1335
01:12:54,537 --> 01:12:58,639
In the case of portrait upside down, everything's fine.
1336
01:12:58,641 --> 01:13:00,875
We know that that works, okay. Portrait upside down,
1337
01:13:00,877 --> 01:13:03,611
we've got the right thing. How about LandscapeLeft,
1338
01:13:03,613 --> 01:13:07,715
or LandscapeRight, let's say. In LandscapeRight, x and
1339
01:13:07,717 --> 01:13:11,819
y are swapped, right, because I've turned it sideways, so
1340
01:13:11,821 --> 01:13:14,789
now it's the x of my device that's down. So
1341
01:13:14,791 --> 01:13:17,759
I need to swap them. There's actually a nice method here or
1342
01:13:17,761 --> 01:13:21,496
function called swap which will swap two variables, okay.
1343
01:13:21,498 --> 01:13:29,370
And then in LandscapeLeft, not only are they swapped,
1344
01:13:29,372 --> 01:13:34,675
but also dy has to be -dy, okay?
1345
01:13:34,677 --> 01:13:38,279
In other cases, there are other cases like FaceUp and
1346
01:13:38,281 --> 01:13:41,816
FaceDown. Those are orientations. In those cases,
1347
01:13:41,818 --> 01:13:45,119
there should be no gravity going on. So I'm gonna say dx
1348
01:13:45,121 --> 01:13:49,323
= 0, dy = 0. Okay, because my thing is flat or whatever.
1349
01:13:49,325 --> 01:13:52,827
Now, notice I'm modifying dy and dx in here, but I said
1350
01:13:52,829 --> 01:13:57,298
if let, just to remind you all you can change this to var,
1351
01:13:57,300 --> 01:14:01,502
if var. Exactly the same as if let, it's just that
1352
01:14:01,504 --> 01:14:05,640
these will now be vars that inside here you can modify.
1353
01:14:05,642 --> 01:14:10,545
Okay, everybody got that? Okay, so
1354
01:14:10,547 --> 01:14:18,119
let's try that. All right,
1355
01:14:18,121 --> 01:14:21,222
here we go. Drop it, it's working! Look at this.
1356
01:14:21,224 --> 01:14:23,691
Dropping some more things, I have my thing face up.
1357
01:14:23,693 --> 01:14:26,694
Okay, now I'm going to turn my thing upside down.
1358
01:14:26,696 --> 01:14:30,531
Let's see if they all fall down. And they do! Excellent.
1359
01:14:30,533 --> 01:14:36,003
Okay, now I'm gonna turn it sideways. Bam. Okay,
1360
01:14:36,005 --> 01:14:41,042
sideways around the other way. Okay. Now,
1361
01:14:41,044 --> 01:14:44,011
one thing that's interesting about this is I actually have
1362
01:14:44,013 --> 01:14:47,648
my rotation locked. If I unlock my rotation,
1363
01:14:47,650 --> 01:14:49,784
then my view is gonna change. Watch this.
1364
01:14:49,786 --> 01:14:54,555
See? So since it switched, all the views moved to the bottom.
1365
01:14:54,557 --> 01:14:57,291
So it didn't actually do much. Now, I can try and
1366
01:14:57,293 --> 01:15:00,795
move it around without changing my orientation, but
1367
01:15:00,797 --> 01:15:02,997
as soon as my orientation changes,
1368
01:15:02,999 --> 01:15:07,368
it's gonna jump around. Got that?
1369
01:15:07,370 --> 01:15:11,606
Okay? All right, that's it for
1370
01:15:11,608 --> 01:15:14,342
today. You have everything you need to know, I hope,
1371
01:15:14,344 --> 01:15:18,246
to do your assignment six. If you have any questions,
1372
01:15:18,248 --> 01:15:20,781
I'll be here as usual. >> For
1373
01:15:20,783 --> 01:15:20,814
more, please visit us at Stanford.edu.
================================================
FILE: subtitles/15. Application Lifecycle, Alerts, CloudKit.srt
================================================
1
00:00:00,001 --> 00:00:08,173
[SOUND] Stanford University. >> All right,
2
00:00:08,175 --> 00:00:13,145
well welcome to lecture 15 CS193P Spring of 2016.
3
00:00:13,147 --> 00:00:16,915
So today we have three major topics.
4
00:00:16,917 --> 00:00:18,350
One is the Application Life Cycle.
5
00:00:18,352 --> 00:00:20,786
Remember we talked about the View Controller Life Cycle.
6
00:00:20,788 --> 00:00:23,956
How View Controller comes into existence and goes on and
7
00:00:23,958 --> 00:00:25,090
off screen and all that stuff. Well,
8
00:00:25,092 --> 00:00:27,726
an Application goes through a similar sort of life cycle.
9
00:00:27,728 --> 00:00:30,596
And we're going to talk about that. And along the way I'm
10
00:00:30,598 --> 00:00:32,998
also going to talk about NS Notification, which
11
00:00:33,000 --> 00:00:35,834
is that radio station from the NBC that we talked about
12
00:00:35,836 --> 00:00:37,369
at the very beginning of the Quarter.
13
00:00:37,371 --> 00:00:40,239
And we're going to talk about alerts. And I'm going to
14
00:00:40,241 --> 00:00:41,974
switch gears a little from what I had said and
15
00:00:41,976 --> 00:00:44,977
talk today about Cloud Kit. Because quite a few of you
16
00:00:44,979 --> 00:00:46,645
have come up to me in your final projects and said.
17
00:00:46,647 --> 00:00:50,582
Well, I want to do a little light weight backend over
18
00:00:50,584 --> 00:00:50,716
maybe something simple like, some sort of messaging or
19
00:00:50,718 --> 00:00:53,552
the network,
20
00:00:53,554 --> 00:00:57,656
something like that. and cloud kit is a pretty cool way and
21
00:00:57,658 --> 00:01:00,526
pretty simple way, actually, to do that, especially for
22
00:01:00,528 --> 00:01:01,493
something like your final project,
23
00:01:01,495 --> 00:01:07,199
which is a proof of concept vehicle where you
24
00:01:07,201 --> 00:01:09,868
just need something simple where you can make it work and
25
00:01:09,870 --> 00:01:11,103
you can always replace it with something much
26
00:01:11,105 --> 00:01:13,605
more powerful networking-wise later. All right.
27
00:01:13,607 --> 00:01:15,140
So I'm gonna talk about those three things today. And
28
00:01:15,142 --> 00:01:20,579
then on Wednesday I will demo all these things. All right?
29
00:01:20,581 --> 00:01:23,082
All right. So let's talk about this in this notification
30
00:01:23,084 --> 00:01:25,851
thing. If you remember back to the very first lecture or
31
00:01:25,853 --> 00:01:28,520
the second lecture when I talked about MVC,
32
00:01:28,522 --> 00:01:31,924
there was this little guy here on the model
33
00:01:31,926 --> 00:01:34,526
that looked a little round thing right here.
34
00:01:34,528 --> 00:01:36,528
Which I called a radio station model for
35
00:01:36,530 --> 00:01:40,499
communication from the model to the controller usually.
36
00:01:40,501 --> 00:01:43,068
Guess where it goes and so we're gonna talk about
37
00:01:43,070 --> 00:01:46,238
actually how this little radio station works in code,
38
00:01:46,240 --> 00:01:47,973
how we do this thing in code. Okay,
39
00:01:47,975 --> 00:01:51,376
it's called NSNotification, all right? It's a radio
40
00:01:51,378 --> 00:01:54,113
station, the radio station by the way is not only good for
41
00:01:54,115 --> 00:01:55,547
the model communicating with the controller,
42
00:01:55,549 --> 00:01:59,084
it's also good for the system IOS to communicate with your
43
00:01:59,086 --> 00:02:03,188
app. So you'll see a lot of communication from IOS to your
44
00:02:03,190 --> 00:02:07,292
app as a whole using these notifications as well. So
45
00:02:07,294 --> 00:02:09,995
let's first of all talk about how you tune into a radio
46
00:02:09,997 --> 00:02:12,264
station. In other words if you want to listen to what's being
47
00:02:12,266 --> 00:02:14,733
broadcast on a radio station, how do you do that? It's
48
00:02:14,735 --> 00:02:17,970
actually quite simple, you're gonna get this in instance of
49
00:02:17,972 --> 00:02:21,273
this object right here the NS notification center by saying
50
00:02:21,275 --> 00:02:23,876
NSNotificationCenter.defaultC- enter(), okay.
51
00:02:23,878 --> 00:02:27,379
And this is gonna get to this shared NS notification center,
52
00:02:27,381 --> 00:02:28,680
and then you're gonna send that
53
00:02:28,682 --> 00:02:33,485
message right here addObserverForName, okay. So
54
00:02:33,487 --> 00:02:38,757
addObserverForName Takes a string which is the name
55
00:02:38,759 --> 00:02:43,195
of the radio station. And it takes a block down here,
56
00:02:43,197 --> 00:02:46,031
the send this notification block which is the block
57
00:02:46,033 --> 00:02:49,067
that's going to be executed when there are broadcasts on
58
00:02:49,069 --> 00:02:52,004
the radio station. So it really couldn't be any
59
00:02:52,006 --> 00:02:56,642
simpler. It returns something that's an NS object protocol.
60
00:02:56,644 --> 00:02:58,944
Optional, okay, but basically it's just a cookie.
61
00:02:58,946 --> 00:03:01,914
You can almost think of it as any object. You'll kind of
62
00:03:01,916 --> 00:03:06,418
need the return value of this, so you can stop observing this
63
00:03:06,420 --> 00:03:08,820
radio station because you're listening when you,
64
00:03:08,822 --> 00:03:11,156
when you send this, to this default center there.
65
00:03:11,158 --> 00:03:12,824
You start listening to the radio station, well,
66
00:03:12,826 --> 00:03:14,059
you have to be able to stop listening and
67
00:03:14,061 --> 00:03:17,229
to do that, you need this little return value of this
68
00:03:17,231 --> 00:03:18,163
add observer. For
69
00:03:18,165 --> 00:03:21,767
name, guy right there. Okay, what are the other two
70
00:03:21,769 --> 00:03:23,869
arguments here besides the name of the radio station and
71
00:03:23,871 --> 00:03:28,040
the block of code to execute? One is the senders,
72
00:03:28,042 --> 00:03:30,509
okay, in other words, the broadcasters that you're
73
00:03:30,511 --> 00:03:32,911
You're interested in. Now you can just specify nil here,
74
00:03:32,913 --> 00:03:35,714
then you'll listen to any radio station with that name.
75
00:03:35,716 --> 00:03:38,784
Anybody broadcasting on that radio station you're going to
76
00:03:38,786 --> 00:03:40,519
listen to. But if you put an object here,
77
00:03:40,521 --> 00:03:43,188
it would only be if that particular object is
78
00:03:43,190 --> 00:03:45,924
broadcasting on that radio station.
79
00:03:45,926 --> 00:03:48,560
Okay? So, it depends on the kind
80
00:03:48,562 --> 00:03:51,363
of environment that you're listening here as to whether
81
00:03:51,365 --> 00:03:52,664
it makes sense to put something there.
82
00:03:52,666 --> 00:03:53,799
And I'll show you some examples.
83
00:03:53,801 --> 00:03:57,135
It'll make more sense. Then there's this queue right here.
84
00:03:57,137 --> 00:03:59,137
This queue is gonna be the queue,
85
00:03:59,139 --> 00:04:03,976
that this closure is going to be executed on. Okay? So
86
00:04:03,978 --> 00:04:07,079
often times if you're doing UI stuff in here you'll obviously
87
00:04:07,081 --> 00:04:09,982
want to say NSOperationQueue.MainQueue.
88
00:04:09,984 --> 00:04:13,785
Okay? You can also pass nil here. You can see the question
89
00:04:13,787 --> 00:04:16,355
mark there, so this could be nil. If you pass mail here,
90
00:04:16,357 --> 00:04:20,058
then it will execute this closure on the same queue
91
00:04:20,060 --> 00:04:23,061
as the radio station broadcaster,
92
00:04:23,063 --> 00:04:24,062
which is a little bit odd, okay,
93
00:04:24,064 --> 00:04:26,231
because sometimes the radio station's broadcasting and
94
00:04:26,233 --> 00:04:28,767
you're not really sure what queue they're on, but
95
00:04:28,769 --> 00:04:31,336
that's what meant, so usually we're going to have NS
96
00:04:31,338 --> 00:04:34,806
operation main queue in here when you're listening. Now,
97
00:04:34,808 --> 00:04:37,843
inside this closure, you can see there is one argument.
98
00:04:37,845 --> 00:04:39,544
The argument is an NSNotification. So,
99
00:04:39,546 --> 00:04:43,148
an NSNotification encapsulates the data that's coming across
100
00:04:43,150 --> 00:04:46,418
in a radio station broadcast. And, there's really only one
101
00:04:46,420 --> 00:04:48,620
interesting thing inside, there's a few in here you
102
00:04:48,622 --> 00:04:51,356
can look in the documentation, but the main interesting thing
103
00:04:51,358 --> 00:04:54,960
in this notification, is this thing called it's userInfo,
104
00:04:54,962 --> 00:04:58,330
notification.userInfo. That's a dictionary, okay,
105
00:04:58,332 --> 00:05:02,668
keys are NSObjects the values are AnyObject, okay. And
106
00:05:02,670 --> 00:05:05,137
in that dictionary is any information that the radio
107
00:05:05,139 --> 00:05:07,806
station broadcaster wants you to have, okay. So they're
108
00:05:07,808 --> 00:05:10,609
gonna have to let you know what the keys of this are so
109
00:05:10,611 --> 00:05:13,645
you can look in there and then you can get certain values.
110
00:05:13,647 --> 00:05:16,181
So everybody who broadcasts. On a radio station,
111
00:05:16,183 --> 00:05:17,983
it's going to tell you the name of the radio station.
112
00:05:17,985 --> 00:05:20,819
This string right here, addObserverForName(String.
113
00:05:20,821 --> 00:05:23,789
And then they're going to tell you the keys that you can use
114
00:05:23,791 --> 00:05:27,225
to look inside this notification.userinfo. Okay,
115
00:05:27,227 --> 00:05:29,394
those are all things that the radio station broadcaster has
116
00:05:29,396 --> 00:05:32,564
to tell you if you want to listen to that broadcast and
117
00:05:32,566 --> 00:05:36,068
make any sense of it, okay. All right, so
118
00:05:36,070 --> 00:05:40,272
here's an example of listening to a radio station. So
119
00:05:40,274 --> 00:05:43,775
there happens to be a radio station that broadcasts
120
00:05:43,777 --> 00:05:46,712
whenever the user goes into their setting and
121
00:05:46,714 --> 00:05:50,482
sets their font size. This is, did you see on the right,
122
00:05:50,484 --> 00:05:52,484
there's a screen in the general settings
123
00:05:52,486 --> 00:05:54,086
app that's under accessibility I think,
124
00:05:54,088 --> 00:05:56,655
font sizes or something like that. And you can see there's
125
00:05:56,657 --> 00:05:58,824
this slider down here. You can make your fonts bigger or
126
00:05:58,826 --> 00:06:02,260
smaller. Okay, well when you change this a radio station
127
00:06:02,262 --> 00:06:05,197
broadcast goes out to all the apps saying the,
128
00:06:05,199 --> 00:06:08,734
what's called, content size category did change.
129
00:06:08,736 --> 00:06:11,036
So this is the name of the radio station,
130
00:06:11,038 --> 00:06:14,573
UIContentSizeCategoryDidChang- eNotification. Okay,
131
00:06:14,575 --> 00:06:18,944
that's a constant somewhere in IOS. And it is
132
00:06:18,946 --> 00:06:21,880
a string that just finds the name of this radio station.
133
00:06:21,882 --> 00:06:24,983
So if you wanted to find out when the user changes
134
00:06:24,985 --> 00:06:29,354
their font size you would just do defaultcenter here,
135
00:06:29,356 --> 00:06:31,990
addObserverForName listen to that radio station.
136
00:06:31,992 --> 00:06:35,260
The sender is going to be the UI application when the system
137
00:06:35,262 --> 00:06:37,929
talks to you it usually talks to you from UI application,
138
00:06:37,931 --> 00:06:41,266
but you might put nill right here because if anybody wants
139
00:06:41,268 --> 00:06:43,969
to tell you that the content size category did change,
140
00:06:43,971 --> 00:06:45,837
you're probably interested so you might say nill for
141
00:06:45,839 --> 00:06:48,807
the object there. And then main queue you're almost
142
00:06:48,809 --> 00:06:49,941
certainly gonna be listening for
143
00:06:49,943 --> 00:06:52,778
broadcast of this radio station, on your main queue,
144
00:06:52,780 --> 00:06:55,680
because when the font size changes we are gonna do
145
00:06:55,682 --> 00:06:57,416
some UI thing. You're gonna react to it and
146
00:06:57,418 --> 00:07:01,586
of course we know you can only do UI on the main queue. Okay?
147
00:07:01,588 --> 00:07:04,656
So here's my closure, and right here I see I'm getting
148
00:07:04,658 --> 00:07:08,093
the notification user info, inside there is a key, again,
149
00:07:08,095 --> 00:07:12,297
this broadcast that's, you know, in some IOS place there.
150
00:07:12,299 --> 00:07:15,767
UI content size category new value key, and it'll tell you
151
00:07:15,769 --> 00:07:19,738
the new value of this size category of the font,
152
00:07:19,740 --> 00:07:24,943
okay? It could be for example, UIContentSizeCategorySmall.
153
00:07:24,945 --> 00:07:27,012
That's one of the size categories. And
154
00:07:27,014 --> 00:07:29,548
once you get that new category, then you can react
155
00:07:29,550 --> 00:07:33,952
to it, okay. Redraw something, whatever you wanna do.
156
00:07:33,954 --> 00:07:35,854
By the way, if you're using preferred font,
157
00:07:35,856 --> 00:07:39,391
remember we switched to using system font in our early apps?
158
00:07:39,393 --> 00:07:42,294
We started using preferred fonts like the body font and
159
00:07:42,296 --> 00:07:42,360
You remember all that? Those fonts are really cool because
160
00:07:42,362 --> 00:07:45,096
the headline font.
161
00:07:45,098 --> 00:07:48,900
they automatically are going to adjust to this size change.
162
00:07:48,902 --> 00:07:50,802
So if you have a UI label somewhere, and
163
00:07:50,804 --> 00:07:54,406
you used a prefered body font, and the person cranks up their
164
00:07:54,408 --> 00:07:57,709
size really big, cuz maybe their eyes are getting old,
165
00:07:57,711 --> 00:08:00,612
age is getting up with, catching up with them Then
166
00:08:00,614 --> 00:08:03,949
it's automatically going to change those for you.
167
00:08:03,951 --> 00:08:06,518
So that's kind of cool feature. Okay, but
168
00:08:06,520 --> 00:08:08,820
sometimes you might have something else that some other
169
00:08:08,822 --> 00:08:11,122
part of the UI that depends on the size of font that you have
170
00:08:11,124 --> 00:08:13,825
to adjust as well. And maybe I'll show that in a little bit
171
00:08:13,827 --> 00:08:18,864
in my demo on Wednesday. Okay, now, what about broadcasting.
172
00:08:18,866 --> 00:08:21,500
What if you want to be a radio station broadcaster?
173
00:08:21,502 --> 00:08:24,002
How do youd do that? That's very easy too.
174
00:08:24,004 --> 00:08:26,238
You just create one of these NSNotifications,
175
00:08:26,240 --> 00:08:29,307
the same thing that is given to your closure when you're
176
00:08:29,309 --> 00:08:31,843
a listener. You just create one of these things and
177
00:08:31,845 --> 00:08:34,513
then you're gonna ask the NSNotification default center
178
00:08:34,515 --> 00:08:38,116
to post that notification. Okay, and then it's going to
179
00:08:38,118 --> 00:08:40,585
broadcast on that radio station. And the notification
180
00:08:40,587 --> 00:08:43,455
of course has to have the name of the radio station. And
181
00:08:43,457 --> 00:08:44,823
also, the object that is sending it.
182
00:08:44,825 --> 00:08:47,659
That's usually gonna be self, right. Your post notification,
183
00:08:47,661 --> 00:08:49,427
you're gonna usually have the self be the center.
184
00:08:49,429 --> 00:08:51,630
And then here's that user info you get to provide,
185
00:08:51,632 --> 00:08:55,500
okay. This is the information that's gonna go to people
186
00:08:55,502 --> 00:08:57,536
who listen to your broadcast. Okay,
187
00:08:57,538 --> 00:08:58,904
you just post this notification and
188
00:08:58,906 --> 00:09:02,007
all the listeners will have their closures executed
189
00:09:02,009 --> 00:09:06,211
with this notification object as the argument. Okay,
190
00:09:06,213 --> 00:09:09,180
pretty simple, all right. Okay, so
191
00:09:09,182 --> 00:09:12,350
that's it for notifications, now I'm gonna move on to, and
192
00:09:12,352 --> 00:09:14,152
I'm gonna talk about notification more like in when
193
00:09:14,154 --> 00:09:15,554
we talk about Cloud Kit a little later,
194
00:09:15,556 --> 00:09:17,589
there's notifications involved there, so we'll talk about
195
00:09:17,591 --> 00:09:20,859
this more that and then in the day we'll talk about this too
196
00:09:20,861 --> 00:09:22,460
So now we're gonna do a little different topic here which
197
00:09:22,462 --> 00:09:26,698
is the application lifecycle, okay. So this is what happens
198
00:09:26,700 --> 00:09:29,734
to your application as it goes through its life, okay.
199
00:09:29,736 --> 00:09:32,737
And it's really encapsulated in these boxes right here.
200
00:09:32,739 --> 00:09:37,642
This green box right here is the kind of your application
201
00:09:37,644 --> 00:09:41,212
when it's the user's focus of attention when they're running
202
00:09:41,214 --> 00:09:44,783
your app. The blue box is your app is still around and
203
00:09:44,785 --> 00:09:47,352
running but it's not the one the user's interacting with
204
00:09:47,354 --> 00:09:51,690
right now. And then these other spaces are not that,
205
00:09:51,692 --> 00:09:54,859
okay. It's not either of those cases. So let's look at this
206
00:09:54,861 --> 00:09:58,229
green box this foreground box. Inside that green box there's
207
00:09:58,231 --> 00:10:00,932
actually a little state here called inactive.
208
00:10:00,934 --> 00:10:05,036
Okay that's when your app is running, it's the foreground,
209
00:10:05,038 --> 00:10:06,972
but it's not receiving any UI events, okay.
210
00:10:06,974 --> 00:10:10,408
So this is kind of like when it's just getting started up,
211
00:10:10,410 --> 00:10:13,411
okay. And then it moves from there into this state
212
00:10:13,413 --> 00:10:17,215
active where it's running and receiving events. Okay so
213
00:10:17,217 --> 00:10:19,751
there are just two distinct states there, and you'll see
214
00:10:19,753 --> 00:10:23,855
why we have those in a moment. And then down here, okay,
215
00:10:23,857 --> 00:10:26,891
when you're in this background area, at the bottom,
216
00:10:26,893 --> 00:10:30,428
your code might be running, for a short amount of time.
217
00:10:30,430 --> 00:10:33,565
You don't usually run in the background for very long. Now,
218
00:10:33,567 --> 00:10:37,135
there's a misconception that iOS, at least until recently,
219
00:10:37,137 --> 00:10:38,570
is not a multitasking system. Because,
220
00:10:38,572 --> 00:10:41,306
it seems like you only have one app running at a time.
221
00:10:41,308 --> 00:10:41,539
in iOS nine, they introduced the ability to have two apps
222
00:10:41,541 --> 00:10:44,409
Well of course,
223
00:10:44,411 --> 00:10:44,809
running at the same time,
224
00:10:44,811 --> 00:10:47,979
on the same screen at the same time. In on iPad,
225
00:10:47,981 --> 00:10:52,884
side by side. But even before that, apps could run
226
00:10:52,886 --> 00:10:55,654
at the same time. It just one would be or two or three would
227
00:10:55,656 --> 00:10:57,956
be in the background and one would be in the foreground.
228
00:10:57,958 --> 00:11:00,759
Of course, we know that iOS is just UNIX underneath and
229
00:11:00,761 --> 00:11:03,461
UNIX is a complete multitasking operating
230
00:11:03,463 --> 00:11:03,528
system so
231
00:11:03,530 --> 00:11:08,466
The single tasking nature that it would appear that IOS had,
232
00:11:08,468 --> 00:11:12,504
was more a UI decision for the user. The user could
233
00:11:12,506 --> 00:11:14,539
concentrate on the one thing they're doing at the time and
234
00:11:14,541 --> 00:11:17,442
I have a cluttered, trying to figure out what's going on,
235
00:11:17,444 --> 00:11:18,643
which apps are running all the time.
236
00:11:18,645 --> 00:11:21,312
So it's kind of a simplification of the UI.
237
00:11:21,314 --> 00:11:24,549
Okay, but even then, apps were running in the background.
238
00:11:24,551 --> 00:11:27,152
And we'll talk about why apps run in the back,
239
00:11:27,154 --> 00:11:30,388
background and when and all that in a moment here,
240
00:11:30,390 --> 00:11:32,624
okay. When you go down to the state
241
00:11:32,626 --> 00:11:36,127
at the bottom, okay, your code, you are not running.
242
00:11:36,129 --> 00:11:39,664
None of your, no code is being executed in your app at all.
243
00:11:39,666 --> 00:11:43,001
You're in the suspended state. So your process still exists,
244
00:11:43,003 --> 00:11:44,369
but it's not getting any cycles from the CPU.
245
00:11:44,371 --> 00:11:47,806
It's just sitting there. So spend it. At any time in here,
246
00:11:47,808 --> 00:11:50,742
you could be killed, okay, and you could be killed
247
00:11:50,744 --> 00:11:53,578
without ever having a chance to run again. So
248
00:11:53,580 --> 00:11:54,279
when you get into this state,
249
00:11:54,281 --> 00:11:57,949
you wanna make sure you're ready to be killed, okay?
250
00:11:57,951 --> 00:12:00,585
So that you don't have any data that's half written or
251
00:12:00,587 --> 00:12:03,555
any kind of weird state, you have to be ready to be killed
252
00:12:03,557 --> 00:12:08,493
down in this suspended state. Okay? And then of course,
253
00:12:08,495 --> 00:12:11,229
y- you have the not running state at the top, and
254
00:12:11,231 --> 00:12:12,964
how do we get out of this not running state?
255
00:12:12,966 --> 00:12:13,598
So, the not running state, there's,
256
00:12:13,600 --> 00:12:16,534
there's no process that's run in your app, and how do we get
257
00:12:16,536 --> 00:12:18,803
out of that? Well, we getting out of it by launching.
258
00:12:18,805 --> 00:12:21,506
And the launching process takes you from not running
259
00:12:21,508 --> 00:12:25,376
into the foreground Thorough this inactive state and
260
00:12:25,378 --> 00:12:26,377
then into active state.
261
00:12:26,379 --> 00:12:29,214
Now it's also possible for you to be launched directly into
262
00:12:29,216 --> 00:12:32,517
the background. Not really sure I am here but that's
263
00:12:32,519 --> 00:12:37,388
possible as well. When you switch to another application.
264
00:12:37,390 --> 00:12:38,623
So you are the primary application.
265
00:12:38,625 --> 00:12:40,592
The user goes and switches to some other application.
266
00:12:40,594 --> 00:12:42,994
Maybe by double clicking the home button or something like
267
00:12:42,996 --> 00:12:45,263
that. Going back and picking something else.
268
00:12:45,265 --> 00:12:47,098
What happens here is you go from the active state to
269
00:12:47,100 --> 00:12:50,568
the inactive state and around to the background state. And
270
00:12:50,570 --> 00:12:53,905
then after a little while, you moved to suspended, okay,
271
00:12:53,907 --> 00:12:56,374
while this other app is running. Okay, so
272
00:12:56,376 --> 00:12:59,043
if they go back to you, then you're gonna go back, okay,
273
00:12:59,045 --> 00:13:04,482
the reverse here. Yes. I have mentioned this already.
274
00:13:04,484 --> 00:13:04,682
When you're killed,
275
00:13:04,684 --> 00:13:06,217
when you go through Suspended to Not running,
276
00:13:06,219 --> 00:13:09,387
no code gets run in your app. So any preparation you have to
277
00:13:09,389 --> 00:13:11,990
do for being killed has to happen before that. Now,
278
00:13:11,992 --> 00:13:14,692
at each of these transitions happen, all these arrows,
279
00:13:14,694 --> 00:13:18,363
okay, code gets executed in your application. Most notably
280
00:13:18,365 --> 00:13:21,366
in your AppDelegate, okay? We know that AppDelegate file,
281
00:13:21,368 --> 00:13:24,469
a file I always Move out of the way into supporting files
282
00:13:24,471 --> 00:13:28,339
place that record data, and manage up the contexting was.
283
00:13:28,341 --> 00:13:30,875
Okay, well, there's a bunch of methods in there since it is
284
00:13:30,877 --> 00:13:34,279
the delegates UIApplication object not much methods
285
00:13:34,281 --> 00:13:36,481
in there they're called as we transition through all
286
00:13:36,483 --> 00:13:38,783
these states. So, we'll talk about those. So,
287
00:13:38,785 --> 00:13:42,453
when you're going from not running to inactive, okay, or
288
00:13:42,455 --> 00:13:46,291
this all time from not running into the background.
289
00:13:46,293 --> 00:13:49,060
Okay, you could do this little path here
290
00:13:49,062 --> 00:13:53,198
not running Inactive into the background. You get this
291
00:13:53,200 --> 00:13:55,066
method right called in your AppDelegate called
292
00:13:55,068 --> 00:13:58,803
ApplicationDidFinishLaunching- WithOption, okay, and
293
00:13:58,805 --> 00:14:01,840
it's exactly what its sound. It's saying okay you have
294
00:14:01,842 --> 00:14:04,576
launch, you're in the Inactive state, okay.
295
00:14:04,578 --> 00:14:08,079
And here are some options and these options is a dictionary.
296
00:14:08,081 --> 00:14:09,380
Okay and we'll talk about what's in the dictionary in
297
00:14:09,382 --> 00:14:12,951
a second. You also are gonna get an NSNotification for
298
00:14:12,953 --> 00:14:15,820
radio station broadcast on this radio station the
299
00:14:15,822 --> 00:14:19,057
UIApplicationDidFinishLaunchi- ngNotification radio station.
300
00:14:19,059 --> 00:14:22,927
And so if you observe on that you'll get your closer called
301
00:14:22,929 --> 00:14:26,397
when the app reaches this inactive state right here.
302
00:14:26,399 --> 00:14:29,334
Okay. Now, what's this dictionary all about right
303
00:14:29,336 --> 00:14:32,337
here? Okay. So this dictionary is passed to you and
304
00:14:32,339 --> 00:14:34,939
it tells you basically why you were launched.
305
00:14:34,941 --> 00:14:37,308
Now, you always think of I get launched because someone
306
00:14:37,310 --> 00:14:40,311
clicked on my icon. That's true. That's one way to get
307
00:14:40,313 --> 00:14:42,447
launched but there's a lot other way to get launched. For
308
00:14:42,449 --> 00:14:45,950
example, someone might want you to open URL. You might be
309
00:14:45,952 --> 00:14:50,421
an app Like you're the Keynote app, okay, on iOS. And
310
00:14:50,423 --> 00:14:53,258
you know how to open Keynote files. So when someone says,
311
00:14:53,260 --> 00:14:55,927
I wanna open this Keynote file, the app is gonna launch
312
00:14:55,929 --> 00:14:59,264
Keynote, okay. And so this is going to tell them
313
00:14:59,266 --> 00:15:03,601
that that's why Keynote got launched. You might
314
00:15:03,603 --> 00:15:06,638
do this because you enter a certain place in the world.
315
00:15:06,640 --> 00:15:06,871
There's a way,
316
00:15:06,873 --> 00:15:09,607
with core location which we haven't talked about,
317
00:15:09,609 --> 00:15:09,741
to say hey,
318
00:15:09,743 --> 00:15:13,044
if this person walks in this little area of the world,
319
00:15:13,046 --> 00:15:17,348
these GPS coordinates, then wake my app up. Launch me,
320
00:15:17,350 --> 00:15:20,518
because I want to do something when they walk into that area.
321
00:15:20,520 --> 00:15:23,154
Okay? So you might get launched because of that. You
322
00:15:23,156 --> 00:15:25,957
might be continuing activities started on another device.
323
00:15:25,959 --> 00:15:28,026
How many people have used Da app
324
00:15:28,028 --> 00:15:30,828
continuation where you start using an app like Safari or
325
00:15:30,830 --> 00:15:32,730
something on your iPhone or something and
326
00:15:32,732 --> 00:15:34,632
then you go over to your iPad and you just continue,
327
00:15:34,634 --> 00:15:36,401
there's a little thing in the corner, okay,
328
00:15:36,403 --> 00:15:38,269
only a couple of you are nodding your heads but
329
00:15:38,271 --> 00:15:40,438
it's kind of a cool feature but if you go and
330
00:15:40,440 --> 00:15:43,074
press that little continuation icon on your IOS and
331
00:15:43,076 --> 00:15:45,343
of course it's going to launch you to continue what you were
332
00:15:45,345 --> 00:15:49,414
doing on the device. Also a notification might have
333
00:15:49,416 --> 00:15:51,382
arrived for you. Like push notification.
334
00:15:51,384 --> 00:15:54,652
Who doesn't know what a push notification is?
335
00:15:54,654 --> 00:15:55,853
Okay, so, everybody knows what that is.
336
00:15:55,855 --> 00:15:58,556
It's little things that make the badges on your icon,
337
00:15:58,558 --> 00:16:01,059
just basically when communication's coming from
338
00:16:01,061 --> 00:16:03,695
servers out there in the world, things like that.
339
00:16:03,697 --> 00:16:07,065
Well of course that might launch you. Maybe you have
340
00:16:07,067 --> 00:16:10,368
some Bluetooth devi, Bluetooth device that is attached to
341
00:16:10,370 --> 00:16:13,771
your computer and it wants to do something with your app,
342
00:16:13,773 --> 00:16:14,772
it might launch your app to do that.
343
00:16:14,774 --> 00:16:17,408
So you can see there's tons of examples and these,
344
00:16:17,410 --> 00:16:19,777
this dictionary is gonna contain the information that
345
00:16:19,779 --> 00:16:23,915
tells you For example, the URL it wants to open or that,
346
00:16:23,917 --> 00:16:26,718
you know, that, you entered a certain place in the world or
347
00:16:26,720 --> 00:16:28,286
things like that. So it's gonna give you information
348
00:16:28,288 --> 00:16:32,724
about what happened that caused you to launch, okay.
349
00:16:33,893 --> 00:16:35,560
One thing about this application to finish
350
00:16:35,562 --> 00:16:37,395
launching with options. It used to be
351
00:16:37,397 --> 00:16:41,332
that you actually built you UI here. Okay your primary UI.
352
00:16:41,334 --> 00:16:44,268
Like a split view controller with a navigation controller
353
00:16:44,270 --> 00:16:46,471
in the master with this view controller in it. And you
354
00:16:46,473 --> 00:16:50,942
would actually build that in code inside of this. A method,
355
00:16:50,944 --> 00:16:54,145
we no longer do that anymore, we use storyboards. But,
356
00:16:54,147 --> 00:16:55,913
when you are doing your final project you are going to be
357
00:16:55,915 --> 00:16:59,417
out searching on the internet for things and you are going
358
00:16:59,419 --> 00:17:01,152
to find something and someone is going to say, Yeah,
359
00:17:01,154 --> 00:17:03,454
you do this by putting code in your application to finish
360
00:17:03,456 --> 00:17:05,289
launching with options. You create your split view there
361
00:17:05,291 --> 00:17:08,826
and you set the delegates. Whoa don't do that That's old.
362
00:17:08,828 --> 00:17:12,063
That's old IOS 7 kind of behavior but
363
00:17:12,065 --> 00:17:13,598
you know sometimes on the internet when you're searching
364
00:17:13,600 --> 00:17:16,000
you can't tell what's new and what's old. The good thing for
365
00:17:16,002 --> 00:17:19,370
you guys is, if you search for Swift stuff, you're likely
366
00:17:19,372 --> 00:17:21,973
gonna get new stuff. Swift's only been around since IOS 8 I
367
00:17:21,975 --> 00:17:27,045
guess, maybe it was 9 I guess. I was eight,
368
00:17:27,047 --> 00:17:30,014
I don't remember, whichever. So, so, it's new.
369
00:17:30,016 --> 00:17:32,717
So you're not likely to see this stuff when you're looking
370
00:17:32,719 --> 00:17:37,255
for Swift-based code. So that's good. Okay. All right,
371
00:17:37,257 --> 00:17:40,491
so what about this transition right here? Okay. So you
372
00:17:40,493 --> 00:17:45,897
are going out of the inactive of this, Active state into
373
00:17:45,899 --> 00:17:49,700
inactive. So you were active, user was interacting with you.
374
00:17:49,702 --> 00:17:52,703
And now you have been moved into the inactive state.
375
00:17:52,705 --> 00:17:55,239
Now why is it important to know this state.
376
00:17:55,241 --> 00:18:01,245
This I like to talk, about as the 'app is paused' state.
377
00:18:01,247 --> 00:18:04,082
Okay? So for example, let's imagine a match in breakout.
378
00:18:04,084 --> 00:18:07,919
Okay? This would be the you want to
379
00:18:07,921 --> 00:18:09,120
pause the motion of the ball.
380
00:18:09,122 --> 00:18:12,757
Remember it's Linear Velocity and Stop it. Okay,
381
00:18:12,759 --> 00:18:16,861
in here. Now, you haven't gone to any of this other state,
382
00:18:16,863 --> 00:18:19,730
you just move from Active to Inactive. Now,
383
00:18:19,732 --> 00:18:22,800
why might you move from Active to Inactive? Well, you might
384
00:18:22,802 --> 00:18:25,670
be in your way to being thrown in to the background and
385
00:18:25,672 --> 00:18:28,639
then suspend it but maybe just a phone call came in.,
386
00:18:28,641 --> 00:18:31,242
Okay,if a phone call comes in you move to
387
00:18:31,244 --> 00:18:34,846
the inactive foreground state. The phone call gets possessed.
388
00:18:34,848 --> 00:18:37,682
When the person hangs up you go back to active. So,
389
00:18:37,684 --> 00:18:39,383
if you had the bouncing ball in Breakout and
390
00:18:39,385 --> 00:18:41,719
a phone call comes in, you wanna stop the ball.
391
00:18:41,721 --> 00:18:44,021
When the call's over you wanna continue the ball right?
392
00:18:44,023 --> 00:18:46,757
So this is the pause. Think about it as pause.
393
00:18:46,759 --> 00:18:50,027
So, that's what you're gonna do in will resign active. And
394
00:18:50,029 --> 00:18:53,464
again, there's a, radio station for it as well, okay,
395
00:18:53,466 --> 00:18:56,033
as you can find out. The radio station is nice because
396
00:18:56,035 --> 00:18:59,137
you can find out anywhere in your application if you just
397
00:18:59,139 --> 00:19:00,471
went to this resign, okay?
398
00:19:00,473 --> 00:19:03,241
You don't have to implement some method in app delegate,
399
00:19:03,243 --> 00:19:04,976
you can find out anywhere in any view controller,
400
00:19:04,978 --> 00:19:10,915
just sign up to listen to this radio station, okay. Now,
401
00:19:10,917 --> 00:19:13,050
when you come back active again, okay,
402
00:19:13,052 --> 00:19:16,120
maybe the phone call was over or maybe you're launching and
403
00:19:16,122 --> 00:19:19,457
got to here. Okay or maybe you came out of the background.
404
00:19:19,459 --> 00:19:21,225
For whatever reason when you come back here,
405
00:19:21,227 --> 00:19:21,926
that's kind of the unpause.
406
00:19:21,928 --> 00:19:24,695
Okay, that's where you're gonna put the ball back and
407
00:19:24,697 --> 00:19:29,000
resume it's linear velocity to what it was. Okay? So this is
408
00:19:29,002 --> 00:19:32,069
the pause and unpause capping between these two states right
409
00:19:32,071 --> 00:19:35,473
here. And the great thing is, if you paused the ball and
410
00:19:35,475 --> 00:19:38,876
you went to background, that's fine, it would be paused. And
411
00:19:38,878 --> 00:19:38,910
when you come back,
412
00:19:38,912 --> 00:19:41,279
whether it's come back from background or whatever reason,
413
00:19:41,281 --> 00:19:45,149
you unpause and move the ball again. That's fine too.
414
00:19:45,151 --> 00:19:48,986
All right? Now what about this state right here. Okay,
415
00:19:48,988 --> 00:19:51,189
so here you were in the inactive state and
416
00:19:51,191 --> 00:19:53,090
now you're moved into the background. Okay,
417
00:19:53,092 --> 00:19:55,826
this is an important one right here, because when you move
418
00:19:55,828 --> 00:19:58,362
into the background, you're only gonna get to run for
419
00:19:58,364 --> 00:20:00,398
a short amount of time, about 30 seconds or so,
420
00:20:00,400 --> 00:20:02,767
it's not actually guaranteed how long you get to run,
421
00:20:02,769 --> 00:20:06,304
but practical matter, it's around 30 seconds. and
422
00:20:06,306 --> 00:20:08,606
you'd better, in 30 seconds, you'd better hurry up,
423
00:20:08,608 --> 00:20:11,075
and clean up. Because, after this,
424
00:20:11,077 --> 00:20:13,144
you're probably gonna move down to the state suspended,
425
00:20:13,146 --> 00:20:16,714
and then you can be killed at any time, okay? So, background
426
00:20:16,716 --> 00:20:19,550
is really where you wanna batten down the hatches, okay?
427
00:20:19,552 --> 00:20:24,956
Close files, you know, get rid of any stuff you're not using.
428
00:20:24,958 --> 00:20:28,926
Things like that. Get your network state into the state
429
00:20:28,928 --> 00:20:31,162
you want things like that because you might be,
430
00:20:31,164 --> 00:20:33,197
this might be a prelude to being killed.
431
00:20:33,199 --> 00:20:34,098
Maybe not but it might be so
432
00:20:34,100 --> 00:20:37,868
you really wanna ask that here. Now it's possible to ask
433
00:20:37,870 --> 00:20:41,339
the system for more time more if 30 seconds is not quite
434
00:20:41,341 --> 00:20:43,174
enough, you can ask for a little bit more time but
435
00:20:43,176 --> 00:20:45,109
eventually the system will get tired of you asking for
436
00:20:45,111 --> 00:20:48,613
more time and they'll stop giving you more time. Okay.
437
00:20:48,615 --> 00:20:53,918
Now, other applications, run in the background as well.
438
00:20:54,120 --> 00:20:56,988
If not coming into this situation from here.
439
00:20:56,990 --> 00:20:59,890
Whereas, user went to a different app or whatever. But
440
00:20:59,892 --> 00:21:02,026
you can actually do things in the background.
441
00:21:02,028 --> 00:21:05,997
And we'll talk about that in a second too. Okay.
442
00:21:05,999 --> 00:21:09,033
Now when you come back okay, so you were in the background
443
00:21:09,035 --> 00:21:10,868
okay or even suspended and then you went back
444
00:21:10,870 --> 00:21:13,070
to the background and then the user made you active again.
445
00:21:13,072 --> 00:21:15,873
Of course you come back up around here and you find out
446
00:21:15,875 --> 00:21:18,476
here that application will enter foreground. You see
447
00:21:18,478 --> 00:21:20,945
how you're coming out of the background into the green box
448
00:21:20,947 --> 00:21:24,382
foreground there. Will tell you that you're back alive.
449
00:21:24,384 --> 00:21:26,150
And now you can un-batten down the hatchets.
450
00:21:26,152 --> 00:21:29,453
Undo what you did when you got into the background here.
451
00:21:29,455 --> 00:21:31,722
You can open things back up.
452
00:21:32,258 --> 00:21:36,627
All right, now, what other things can you do in
453
00:21:36,629 --> 00:21:40,064
the AppDelegate besides these lifecycle methods? Tons of
454
00:21:40,066 --> 00:21:43,734
things, okay. So not only can you handle push notifications
455
00:21:43,736 --> 00:21:46,771
here, okay. You also have a local notification, which
456
00:21:46,773 --> 00:21:50,541
are notification that you set to go up at a certain time.
457
00:21:50,543 --> 00:21:54,445
Like reminders, okay, it's at 10:00pm every day,
458
00:21:54,447 --> 00:21:57,581
have this local notification go up and wake my app up, and
459
00:21:57,583 --> 00:22:03,354
go do something. Well, all that handled through the Also,
460
00:22:03,356 --> 00:22:05,589
data protection, okay.
461
00:22:05,591 --> 00:22:09,593
When your, when your app or your device is locked,
462
00:22:09,595 --> 00:22:12,663
then a lot of the data, pretty much by default,
463
00:22:12,665 --> 00:22:15,166
all of the data on your phone is encrypted. Okay.
464
00:22:15,168 --> 00:22:18,302
That's why a locked phone, no one can get in there to your
465
00:22:18,304 --> 00:22:20,671
data cuz it's encrypted, okay? And so
466
00:22:20,673 --> 00:22:24,208
all the locking and unlocking and giving you access of files
467
00:22:24,210 --> 00:22:26,677
where they're not encrypted when you're unlocked and
468
00:22:26,679 --> 00:22:27,611
encrypted when they're locked,
469
00:22:27,613 --> 00:22:31,982
that's all the AppDelegate. The AppDelegate also would get
470
00:22:31,984 --> 00:22:33,351
called if you were already running and
471
00:22:33,353 --> 00:22:35,386
someone said open this URL, like your keynote,
472
00:22:35,388 --> 00:22:38,589
and someone said open this keynote file. You can also do
473
00:22:38,591 --> 00:22:41,359
what's called background fetching which is kinda cool.
474
00:22:41,361 --> 00:22:41,592
Let's say your some
475
00:22:41,594 --> 00:22:43,260
social media app or something like that.
476
00:22:43,262 --> 00:22:47,832
A news app and you want to kinda through out the day,
477
00:22:47,834 --> 00:22:49,433
go often fetch the latest news.
478
00:22:49,435 --> 00:22:51,902
So when the app runs you got the latest news right there.
479
00:22:51,904 --> 00:22:54,972
You don't have to go fetch on the network at that time. And
480
00:22:54,974 --> 00:22:56,207
you are actually allowed to do that,
481
00:22:56,209 --> 00:22:59,677
so this background fetching Let your app launch and
482
00:22:59,679 --> 00:23:02,713
run in the background for the little while again not too
483
00:23:02,715 --> 00:23:05,783
long okay this is another one too many apps stealing cycles
484
00:23:05,785 --> 00:23:08,753
from the foreground app but it will run in the background and
485
00:23:08,755 --> 00:23:10,020
you can go do some network activity.
486
00:23:10,022 --> 00:23:10,855
Pull down your data or whatever and
487
00:23:10,857 --> 00:23:14,525
then go back to sleep and the system manages that and
488
00:23:14,527 --> 00:23:15,659
you can go look at the documentation for
489
00:23:15,661 --> 00:23:19,597
UI application to find out how you set this up. You have to
490
00:23:19,599 --> 00:23:22,533
enable something in the capabilities of your app.
491
00:23:22,535 --> 00:23:22,900
You set this up and
492
00:23:22,902 --> 00:23:25,369
you can say kinda how often you wanna be woken up and
493
00:23:25,371 --> 00:23:28,873
all those kinda things. If you abuse this, of course this so
494
00:23:28,875 --> 00:23:32,243
we'll stop waking you up in the background. Other Other
495
00:23:32,245 --> 00:23:34,245
reasons that you might wake up in the background.
496
00:23:34,247 --> 00:23:38,416
You might be a VOIP app, right. A voice over IP app and
497
00:23:38,418 --> 00:23:41,218
so you're using some other application in the foreground,
498
00:23:41,220 --> 00:23:43,954
but you're talking on over VOIP to someone else.
499
00:23:43,956 --> 00:23:45,956
Or you're a music playing app, okay.
500
00:23:45,958 --> 00:23:47,324
And the person's got their headphones on, they're
501
00:23:47,326 --> 00:23:49,693
listening to your music while they're using some other app.
502
00:23:49,695 --> 00:23:51,529
Of course you're of course running in the background in
503
00:23:51,531 --> 00:23:53,864
that case. So there are a lot of cases where you can run in
504
00:23:53,866 --> 00:23:56,100
the background and you can look it all this up in your UI
505
00:23:56,102 --> 00:23:59,503
application. Unfortunately, again from time constraints,
506
00:23:59,505 --> 00:24:00,538
I don't have time to go thru and
507
00:24:00,540 --> 00:24:03,674
show you all the Cool thing to know in the background but
508
00:24:03,676 --> 00:24:04,475
these are some of them, and
509
00:24:04,477 --> 00:24:08,012
almost all of this stuff happens with your AppDelegate.
510
00:24:08,014 --> 00:24:10,448
Okay the methods in the AppDelegate.
511
00:24:11,117 --> 00:24:14,518
All right, let's talk about UIApplication. The actual
512
00:24:14,520 --> 00:24:16,754
object UIApplication is only one instance,
513
00:24:16,756 --> 00:24:19,957
obviously in your app. You get it by calling this method
514
00:24:19,959 --> 00:24:24,328
UIApplication.sharedapplica- tion Right there. Okay.
515
00:24:24,330 --> 00:24:28,666
And it manages all the global behavior in your app. Mostly
516
00:24:28,668 --> 00:24:31,302
it delegates things to its delegate, the app delegate,
517
00:24:31,304 --> 00:24:34,071
so that's good, that's why we see so much behavior there.
518
00:24:34,073 --> 00:24:36,841
But it does have some useful functionality of it's own.
519
00:24:36,843 --> 00:24:39,376
For example, it has a method called openURL.
520
00:24:39,378 --> 00:24:41,645
You saw this in your smashtag. Right,
521
00:24:41,647 --> 00:24:44,181
this is how you could click on a URL and open it up.
522
00:24:44,183 --> 00:24:47,618
You call this application method openURL okay.
523
00:24:47,620 --> 00:24:51,121
You can register here for scheduling these
524
00:24:51,123 --> 00:24:53,591
notifications. These local notifications, right, those
525
00:24:53,593 --> 00:24:56,427
things I was telling you wake up every day at 10 o'clock and
526
00:24:56,429 --> 00:24:59,296
run my app kind of things. You actually register and
527
00:24:59,298 --> 00:25:02,800
schedule them here in UIApplication If
528
00:25:02,802 --> 00:25:05,202
you are doing the background fetching thing I talked about.
529
00:25:05,204 --> 00:25:08,672
You can set the minimum fetch interval. Okay,
530
00:25:08,674 --> 00:25:09,607
so how often your background,
531
00:25:09,609 --> 00:25:12,243
things gonna open up in the background in run? Here is
532
00:25:12,245 --> 00:25:15,713
how you ask for more time if you're in the background.
533
00:25:15,715 --> 00:25:20,251
Background task with expiration handler. Okay,
534
00:25:20,253 --> 00:25:23,153
there's a little spinning wheel on the upper left
535
00:25:23,155 --> 00:25:26,023
corner in the status bar of your iOS device.
536
00:25:26,025 --> 00:25:26,624
I'm sure you've seen it, right,
537
00:25:26,626 --> 00:25:29,159
that's supposed to represent network activity. You can turn
538
00:25:29,161 --> 00:25:32,563
that on and off with this bool right here in UIApplication.
539
00:25:32,565 --> 00:25:34,798
networkActivityIndicatorVis- ible equals true and
540
00:25:34,800 --> 00:25:38,702
it'll on start spinning. Say false, it'll turn off.
541
00:25:38,704 --> 00:25:41,539
Okay, and you generally should do this anytime you're
542
00:25:41,541 --> 00:25:45,009
doing any network activity so that the user knows, app is
543
00:25:45,011 --> 00:25:49,046
accessing the network right now. You can also find out
544
00:25:49,048 --> 00:25:51,582
a lot of things, like remember that content sides category,
545
00:25:51,584 --> 00:25:54,051
the fonts, big fonts on, you can find that out,
546
00:25:54,053 --> 00:25:55,786
you can poll it, of course, you could also listen
547
00:25:55,788 --> 00:25:57,821
to the radio station that tells you when it changes, but
548
00:25:57,823 --> 00:26:00,224
you can poll it. You can also find out how much time
549
00:26:00,226 --> 00:26:03,160
you've got remaining until you're gonna be suspended
550
00:26:03,162 --> 00:26:06,497
from the background state. And you can even find out
551
00:26:06,499 --> 00:26:07,998
what state you're in. Am I in the foreground?
552
00:26:08,000 --> 00:26:11,835
Am I in the background etc. With this guy right there.
553
00:26:12,305 --> 00:26:15,639
Okay? Now let's talk about Info.plist.
554
00:26:15,641 --> 00:26:16,941
We've actually seen Info.plist.
555
00:26:16,943 --> 00:26:21,345
Remember, that's where we went when we tried to access URLs,
556
00:26:21,347 --> 00:26:23,948
and they were HTTP not HTTPS, right? And
557
00:26:23,950 --> 00:26:27,384
we had to go in here and add something to our Info.plist
558
00:26:27,386 --> 00:26:32,957
that allowed us to kind of unsecurely access URLs and
559
00:26:32,959 --> 00:26:35,593
in general Info.plist is for things like that.
560
00:26:35,595 --> 00:26:38,262
Little settings that you're gonna set to do things like
561
00:26:38,264 --> 00:26:41,599
that. You can see that there's an, kind of a property list
562
00:26:41,601 --> 00:26:45,536
editor right here. Lets you go through and edit values and
563
00:26:45,538 --> 00:26:48,973
even you know, if the values are arrays okay, you can
564
00:26:48,975 --> 00:26:52,409
edit that in there. You can also right-click on it and
565
00:26:52,411 --> 00:26:56,246
change it to be an XML view, okay? I don't really recommend
566
00:26:56,248 --> 00:26:58,849
this, cuz it's easy to make a syntax error in here, and
567
00:26:58,851 --> 00:27:00,050
now your Info.plist can't be read.
568
00:27:00,052 --> 00:27:02,720
So I'd stick with the other view. But actually, most of
569
00:27:02,722 --> 00:27:06,924
the things in Info.plist you set In the general settings of
570
00:27:06,926 --> 00:27:09,994
your project. Right? All these places where we said
571
00:27:09,996 --> 00:27:13,931
things like, portrait upside down or landscape left.
572
00:27:13,933 --> 00:27:14,431
Which one of those are allowed?
573
00:27:14,433 --> 00:27:17,301
Those are info.p list things but we edit them here.
574
00:27:17,303 --> 00:27:21,805
It's just nicer. And most of the common ones we can find in
575
00:27:22,241 --> 00:27:27,678
here. Okay? Now, in addition to all, all the settings there
576
00:27:27,680 --> 00:27:31,982
there's also some capabilities in your app that you have to
577
00:27:31,984 --> 00:27:35,719
explicitly enable if you wanna use them, okay? And you do
578
00:27:35,721 --> 00:27:38,622
this in the Capabilities tab in your Project Setting,
579
00:27:38,624 --> 00:27:39,690
it looks like this. Okay.
580
00:27:39,692 --> 00:27:41,425
This list is always always changing. But
581
00:27:41,427 --> 00:27:43,961
you can see a lot of cool features in here like iCloud,
582
00:27:43,963 --> 00:27:47,031
wich I'm gonna talk about a little later in this lecture,
583
00:27:47,033 --> 00:27:47,598
and the Game Centre,
584
00:27:47,600 --> 00:27:51,201
Apple Pay, here the background fetching thing,
585
00:27:51,203 --> 00:27:55,873
right here, Data Protection down here, Home Kit and
586
00:27:55,875 --> 00:27:58,909
Health Kit down at the bottom. If you wanna use any of these,
587
00:27:58,911 --> 00:28:01,812
you have to turn it on by just going to this capabilities
588
00:28:01,814 --> 00:28:04,181
tab in your project settings. And just clicking this
589
00:28:04,183 --> 00:28:07,184
from off to on. And when you click it to on, there's going
590
00:28:07,186 --> 00:28:08,986
to be various settings for that thing. Okay and
591
00:28:08,988 --> 00:28:12,222
we'll see this all in detail when we do Cloud Kit. Okay,
592
00:28:12,224 --> 00:28:15,092
you can also see from this how much stuff I just can't cover
593
00:28:15,094 --> 00:28:17,094
in this class. In fact, of this whole list.
594
00:28:17,096 --> 00:28:19,863
Only thing I'm gonna cover is the first one, I cloud.
595
00:28:19,865 --> 00:28:22,499
Okay? The rest of the stuff, I just don't have time to get to
596
00:28:22,501 --> 00:28:25,803
it all. When you go start looking through the doc and
597
00:28:25,805 --> 00:28:27,371
you start looking at some of the things you might do,
598
00:28:27,373 --> 00:28:29,940
you can know that you have to go here to turn things on.
599
00:28:29,942 --> 00:28:33,877
Okay? All right, so
600
00:28:33,879 --> 00:28:36,580
that's pretty much it for your application lifestyle,
601
00:28:36,582 --> 00:28:40,050
life cycle, and kind of overall settings in your app.
602
00:28:40,052 --> 00:28:44,722
The next topic I'm gonna do is alerts and action sheets okay.
603
00:28:44,724 --> 00:28:46,056
So these are the pop up and
604
00:28:46,058 --> 00:28:49,793
ask the user something UI okay. And they're very,
605
00:28:49,795 --> 00:28:54,965
very similar the API for them is almost identical okay but
606
00:28:54,967 --> 00:28:56,066
their UI is slightly different so
607
00:28:56,068 --> 00:28:58,702
let's take a look at these alerts and action sheets. So
608
00:28:58,704 --> 00:29:01,138
alerts, always pop up in the middle of the screen.
609
00:29:01,140 --> 00:29:03,474
They grey out everything else behind it. Okay.
610
00:29:03,476 --> 00:29:05,476
On this little screen. Both an iPad and iPhone.
611
00:29:05,478 --> 00:29:10,748
*swallow* And, it usually asks a question that has
612
00:29:10,750 --> 00:29:11,582
usually one or two answers.
613
00:29:11,584 --> 00:29:15,219
Either you just click 'okay' or maybe 'okay, cancel' or
614
00:29:15,221 --> 00:29:19,156
'yes, no'. Possibly a third answer. But usually it's only
615
00:29:19,158 --> 00:29:23,494
two. Answers. This thing is very modal,
616
00:29:23,496 --> 00:29:24,995
it kind of takes over your whole UI and
617
00:29:24,997 --> 00:29:26,430
just puts up this little box, so you know,
618
00:29:26,432 --> 00:29:29,066
you don't wanna use it unless you really can't proceed
619
00:29:29,068 --> 00:29:33,704
without the user acknowledging this information or whatever.
620
00:29:33,706 --> 00:29:36,206
Usually it's used when there are asynchronous problems,
621
00:29:36,208 --> 00:29:40,878
some network connection fails. There's no place in the UI to
622
00:29:40,880 --> 00:29:43,347
tell them that, so you kinda have to Take over for
623
00:29:43,349 --> 00:29:47,017
a moment and say the network connection failed click OK or
624
00:29:47,019 --> 00:29:51,288
click here to retry or whatever. Okay, this alerts
625
00:29:51,290 --> 00:29:56,293
can have text fields in it as well. Okay, and the condition
626
00:29:56,295 --> 00:29:57,895
just OK or cancel or whatever you cannot type full.
627
00:29:57,897 --> 00:30:01,899
So you could use it for you know, access tonight. Enter
628
00:30:01,901 --> 00:30:05,903
password Okay, you can use it for things like that. Okay?
629
00:30:05,905 --> 00:30:10,674
I wouldn't use it as a general text of obtaining UI, however.
630
00:30:10,676 --> 00:30:13,677
Okay? Again, it's only for exceptional circumstances.
631
00:30:13,679 --> 00:30:16,747
Now an action sheet is very similar to an alert, but
632
00:30:16,749 --> 00:30:19,183
it has more than two choices, usually three or
633
00:30:19,185 --> 00:30:22,886
four choices. Okay? Instead of appearing in the middle,
634
00:30:22,888 --> 00:30:25,689
it slides up from the bottom, on an iPhone, or
635
00:30:25,691 --> 00:30:30,994
it appears in a popover on an iPad, okay? And you kind of
636
00:30:30,996 --> 00:30:34,798
would think of, action sheet as a branching decision UI.
637
00:30:34,800 --> 00:30:37,968
Okay, the user has reached a place in there Point in their
638
00:30:37,970 --> 00:30:41,004
use of the UI where they have to branch and go do a certain
639
00:30:41,006 --> 00:30:42,673
task, and go a different direction, they have two,
640
00:30:42,675 --> 00:30:46,877
or three, or four different ways that they can go. Okay?
641
00:30:46,879 --> 00:30:50,047
Now you've seen all these UI, these things in your app,
642
00:30:50,049 --> 00:30:51,515
so I don't want to go too much into it,
643
00:30:51,517 --> 00:30:54,418
but let's look a little bit about how we
644
00:30:54,420 --> 00:30:57,621
do the code behind it. So Here is what they look like.
645
00:30:57,623 --> 00:30:59,923
An action sheet comes up from the bottom. right.
646
00:30:59,925 --> 00:31:03,560
It's got a tile right here and then also a little commentary
647
00:31:03,562 --> 00:31:06,997
in there some explanation. And then it's got the buttons.
648
00:31:06,999 --> 00:31:11,602
And alert is, comes up in the middle of the screen.
649
00:31:11,604 --> 00:31:13,670
It's, you got one or two buttons usually and
650
00:31:13,672 --> 00:31:16,974
possibly. Text, so here for example I've got
651
00:31:16,976 --> 00:31:21,178
a Login Required to control my Cassini I guess here and
652
00:31:21,180 --> 00:31:23,881
you got to type your guided system password to
653
00:31:23,883 --> 00:31:25,883
be able to get control of Cassini.
654
00:31:25,885 --> 00:31:28,485
While over in the Action Sheet I'm kind of
655
00:31:28,487 --> 00:31:31,288
Cassini wants to go somewhere and we're picking a branch in
656
00:31:31,290 --> 00:31:33,190
decision whether we're gonna Orbit Saturn or
657
00:31:33,192 --> 00:31:36,293
E=xplore Titan, get a Closeup of the Sun. Or just cancel
658
00:31:36,295 --> 00:31:40,264
that branching decision. Okay? So, how's the code for
659
00:31:40,266 --> 00:31:45,535
this look? Well these things, like action sheet, are just
660
00:31:45,537 --> 00:31:48,572
UI view controllers. Okay? They're presented motilly,
661
00:31:48,574 --> 00:31:50,974
they just instead of taking over the whole screen,
662
00:31:50,976 --> 00:31:52,342
they just get drawn like this, or
663
00:31:52,344 --> 00:31:53,777
as a little square in the middle.
664
00:31:53,779 --> 00:31:55,379
But they're basically just UI view controllers
665
00:31:55,381 --> 00:31:57,681
presented motilly. I haven't really talked about motile
666
00:31:57,683 --> 00:32:00,484
presentation. I hope to get that on Wednesday, but
667
00:32:00,486 --> 00:32:03,520
modal just means you can't do anything else in that app
668
00:32:03,522 --> 00:32:07,391
until you deal with this view controller. K, so these
669
00:32:07,393 --> 00:32:09,293
are modal view controllers so you create them but
670
00:32:09,295 --> 00:32:11,828
they are initially initialized to UI alert controller,
671
00:32:11,830 --> 00:32:14,131
gonna create you one of these UI view controllers.
672
00:32:14,133 --> 00:32:16,600
It's a sub class of UI view controller. All you need to
673
00:32:16,602 --> 00:32:19,770
specify is this title right here like Redeploy Cassini.
674
00:32:19,772 --> 00:32:21,972
And the message that goes in here
675
00:32:21,974 --> 00:32:24,107
Issue commands to Cassini's guidance system.
676
00:32:24,109 --> 00:32:26,176
Right here. And then, the style you want,
677
00:32:26,178 --> 00:32:29,646
either action sheet or alert. Okay, so that's it,
678
00:32:29,648 --> 00:32:32,182
that's how you create one of these view controllers.
679
00:32:32,184 --> 00:32:34,318
The next step is we're gonna configure it and
680
00:32:34,320 --> 00:32:35,919
then we're going to present it modally.
681
00:32:35,921 --> 00:32:38,355
So let's do the configuration part.
682
00:32:38,357 --> 00:32:42,726
Obviously configuring it, we need to add actions to it,
683
00:32:42,728 --> 00:32:45,295
okay, these various buttons and we do that by sending
684
00:32:45,297 --> 00:32:50,033
the method addAction, okay, to the alert and the addAction
685
00:32:50,035 --> 00:32:54,805
takes one argument which is a UIAlertAction, okay.
686
00:32:54,807 --> 00:32:57,341
They UIAlertAction has the following
687
00:32:57,343 --> 00:33:00,110
initializer arguments The title that's gonna be
688
00:33:00,112 --> 00:33:02,713
the title on the button okay like Orbit Saturn or
689
00:33:02,715 --> 00:33:05,449
Explore Titan whatever. The style we'll talk about what
690
00:33:05,451 --> 00:33:08,118
that style is in a second and then a handler and
691
00:33:08,120 --> 00:33:11,054
this is just a closure that's gonna get executed when
692
00:33:11,056 --> 00:33:15,392
this button gets pressed. Got it? So could be simpler. Okay
693
00:33:15,394 --> 00:33:18,662
that's all there is. So let's look at some examples of this.
694
00:33:18,664 --> 00:33:19,997
Here's orbit Saturn right here.
695
00:33:19,999 --> 00:33:23,433
The title is Orbit Saturn, the style is the default style,
696
00:33:23,435 --> 00:33:26,370
orbit Saturn explore Titan or default style buttons. We'll
697
00:33:26,372 --> 00:33:29,373
talk about the other styles in a second. And in the closure,
698
00:33:29,375 --> 00:33:32,209
obviously we're going to go into orbit around Saturn.
699
00:33:32,211 --> 00:33:33,010
That's what we're going to do and
700
00:33:33,012 --> 00:33:36,446
we'll do whatever code is necessary to do that there.
701
00:33:36,448 --> 00:33:38,048
Okay, so here is Explore TItan,
702
00:33:38,050 --> 00:33:42,686
almost exactly the same. Okay. Just that the, closure here is
703
00:33:42,688 --> 00:33:45,789
a little different. Notice this closure says, okay,
704
00:33:45,791 --> 00:33:48,759
if I'm going to Explorer TItan that's not part of Cassini's
705
00:33:48,761 --> 00:33:50,427
mission, then, so I'm going to log,
706
00:33:50,429 --> 00:33:53,230
make the person log in. Okay you can send him to Saturn,
707
00:33:53,232 --> 00:33:56,299
send Cassini to Saturn evidently without a password.
708
00:33:56,301 --> 00:33:58,668
But you need to enter a password for Titan.
709
00:33:58,670 --> 00:34:01,705
So just has a little different action there, that's all.
710
00:34:01,707 --> 00:34:06,243
Okay and these are both normal default style buttons.
711
00:34:06,245 --> 00:34:09,713
Now let's take a look at closing, closer, go closeup to
712
00:34:09,715 --> 00:34:12,482
the sun here, okay. Closeup to the sun's a little different.
713
00:34:12,484 --> 00:34:17,821
Its style is not default, it's Destructive. So if you have a,
714
00:34:17,823 --> 00:34:20,924
action sheet button that is of style of destructive,
715
00:34:20,926 --> 00:34:24,227
it's gonna appear red, okay. Now you should always
716
00:34:24,229 --> 00:34:27,064
pick destructive if the thing the users gonna do with this
717
00:34:27,066 --> 00:34:30,167
button is going to do something permanent like
718
00:34:30,169 --> 00:34:30,500
delete something,
719
00:34:30,502 --> 00:34:33,303
delete an entry from a database that can't be undone,
720
00:34:33,305 --> 00:34:36,606
or something like that. Okay so that is a destructive here.
721
00:34:36,608 --> 00:34:39,743
A close up of the sun is probably destroy Cassini so
722
00:34:39,745 --> 00:34:41,978
we made that destructive, alright but
723
00:34:41,980 --> 00:34:46,583
otherwise it is still going to perform this action. Okay,
724
00:34:46,585 --> 00:34:50,020
and what about cancel so cancel you notice it looks
725
00:34:50,022 --> 00:34:52,756
a little different it is kind of separated. You see that?
726
00:34:52,758 --> 00:34:57,360
Otherwise it looks the same. Maybe the font might be bold?
727
00:34:57,362 --> 00:35:01,331
Hard to tell there, I think so. But otherwise, it's just
728
00:35:01,333 --> 00:35:03,934
a normal button, okay? But it's of style cancel.
729
00:35:03,936 --> 00:35:06,369
When we get to iPad, you're gonna see why it's very
730
00:35:06,371 --> 00:35:09,706
important that we make our Cancel button be style. Cancel
731
00:35:09,708 --> 00:35:14,478
be a different style than the other thing. Okay. So you add
732
00:35:14,480 --> 00:35:18,215
all your actions there to do all the things you want to do.
733
00:35:18,217 --> 00:35:21,351
And then you simply call this view controller method,
734
00:35:21,353 --> 00:35:23,854
present view controller. Present view controller is
735
00:35:23,856 --> 00:35:27,757
the Normal modal presentation method in view controller.
736
00:35:27,759 --> 00:35:30,594
When I get talk, give you the lecture on mobile view
737
00:35:30,596 --> 00:35:32,529
controllers, this is the method I'm gonna talk about.
738
00:35:32,531 --> 00:35:35,198
You call this method, it takes a view controller, that's what
739
00:35:35,200 --> 00:35:38,335
an alert is, right? Alert is alert controller which is
740
00:35:38,337 --> 00:35:39,202
subclass UIController.
741
00:35:39,204 --> 00:35:42,639
Takes one of these and it presents is modally. Okay,
742
00:35:42,641 --> 00:35:46,276
this is whether it's going to animate the presentation. And
743
00:35:46,278 --> 00:35:48,044
this is just a completion handler, they call it,
744
00:35:48,046 --> 00:35:51,381
it gets called when it has finished presenting it.
745
00:35:51,383 --> 00:35:54,284
Now when it goes away but when it finishes presenting so it's
746
00:35:54,286 --> 00:35:57,154
on screen. Okay, we almost never use that, by the way.
747
00:35:57,156 --> 00:36:00,223
All right, so the view controller, you do that, okay.
748
00:36:00,225 --> 00:36:03,426
It puts the thing up, now when these buttons get pressed,
749
00:36:03,428 --> 00:36:05,262
their closures get called. Okay,
750
00:36:05,264 --> 00:36:09,466
when cancel gets pressed, it goes away, it dismisses.
751
00:36:10,102 --> 00:36:13,637
That's it, that's how you use the Action sheet. Alright,
752
00:36:13,639 --> 00:36:18,608
now, let's talk a little bit about iPad, okay. Because,
753
00:36:18,610 --> 00:36:22,679
an iPad, this thing would look ridiculous if it came up from
754
00:36:22,681 --> 00:36:25,115
the bottom. Either it would just be a tiny thing here, or
755
00:36:25,117 --> 00:36:28,151
if it was as wide as the whole screen It will be gigantic,
756
00:36:28,153 --> 00:36:32,556
okay? So we don't have these things come up from the bottom
757
00:36:32,558 --> 00:36:37,093
on an iPad, instead we have them come up as pop-overs,
758
00:36:37,095 --> 00:36:40,197
okay. See how this is the same thing, but it's over pop-over.
759
00:36:40,199 --> 00:36:43,733
Notice, no cancel button, okay? Why do we
760
00:36:43,735 --> 00:36:45,302
have no cancel button on iPad?
761
00:36:45,304 --> 00:36:48,572
Because when a pop-over comes up, you click anywhere else,
762
00:36:48,574 --> 00:36:53,176
it dismisses it. That's just like hitting cancel, okay.
763
00:36:53,178 --> 00:36:58,181
In fact your cancel closure will get there, okay. So what
764
00:36:58,183 --> 00:37:03,353
do we need to do to make this appear in a pop over, on iPad,
765
00:37:03,355 --> 00:37:05,522
okay. Well we do need to do a little bit different code.
766
00:37:05,524 --> 00:37:08,024
So here's our code from the iPhone. Okay.
767
00:37:08,026 --> 00:37:11,628
First we're gonna add a little thing in here, which is,
768
00:37:11,630 --> 00:37:12,162
we're gonna set the,
769
00:37:12,164 --> 00:37:16,700
a View Controller's modal Presentation Style to Popover.
770
00:37:16,702 --> 00:37:18,368
Okay, that says present this modally,
771
00:37:18,370 --> 00:37:22,639
but in a Popover style, instead of up from the bottom
772
00:37:22,641 --> 00:37:27,177
there. Okay? Then we just need to get the popover
773
00:37:27,179 --> 00:37:30,447
Presentation Controller, which is just It's a class.
774
00:37:30,449 --> 00:37:31,681
We're not gonna talk too much about it,
775
00:37:31,683 --> 00:37:34,718
but it's the thing that controls presentation.
776
00:37:34,720 --> 00:37:37,554
Okay? All view controllers, when they're presented,
777
00:37:37,556 --> 00:37:39,856
have a presentation controller that presents them.
778
00:37:39,858 --> 00:37:42,559
Well, pop-over ones have one called pop-over presentation
779
00:37:42,561 --> 00:37:45,295
controller. You're just gonna get this thing if you look for
780
00:37:45,297 --> 00:37:47,364
the UI for a pop-over presentation controller,
781
00:37:47,366 --> 00:37:50,767
you'll see that it has. An argument bar button item,
782
00:37:50,769 --> 00:37:52,902
it also has another argument wrecked.
783
00:37:52,904 --> 00:37:56,706
Okay, and if you set the bar button item this just tells
784
00:37:56,708 --> 00:38:02,112
the system where this pop over pops up from. So
785
00:38:02,114 --> 00:38:05,115
here this redeploy bar button item is this little bar button
786
00:38:05,117 --> 00:38:07,651
item in the upper right. And so we're just telling
787
00:38:07,653 --> 00:38:09,786
the presentation controller that's gonna pop over,
788
00:38:09,788 --> 00:38:12,455
pop this thing over, that this is the button that it
789
00:38:12,457 --> 00:38:15,759
pops over from. And that's how it knows to put it up near it,
790
00:38:15,761 --> 00:38:17,460
and to put that little triangle up there,
791
00:38:17,462 --> 00:38:20,196
you see the triangle pointing to, to redeploy. We could of
792
00:38:20,198 --> 00:38:24,868
said PPC question mark dot wrecked equals whatever.
793
00:38:24,870 --> 00:38:25,802
And view equals whatever and
794
00:38:25,804 --> 00:38:28,204
then it could pop over from any arbitrary rectangle,
795
00:38:28,206 --> 00:38:31,875
anywhere in a view, okay. So you can do that as well.
796
00:38:31,877 --> 00:38:33,143
So that's all we're doing here.
797
00:38:33,145 --> 00:38:36,880
All we've added to this whole thing for pop over's just
798
00:38:36,882 --> 00:38:38,548
made our presentation saw a pop over and
799
00:38:38,550 --> 00:38:42,252
told the control, the pop over presentation controller, where
800
00:38:42,254 --> 00:38:45,322
we want it to pop over from. That's all we've done. Okay,
801
00:38:45,324 --> 00:38:49,959
now cool thing about this is this will work on iPhone 2.
802
00:38:49,961 --> 00:38:53,830
So you can put this code in here. It will do this but
803
00:38:53,832 --> 00:38:55,799
iPhone will continue to do this. And
804
00:38:55,801 --> 00:38:59,636
why is that? That because of this adaptive presentation
805
00:38:59,638 --> 00:39:01,571
style mechanism we talk about early in the core.
806
00:39:01,573 --> 00:39:04,341
You might have forgot it by now. But it's the mechanism
807
00:39:04,343 --> 00:39:06,943
that says if I try to put something up as a pop-over
808
00:39:06,945 --> 00:39:10,280
on an iPhone, it says op, can't, we don't do pop-overs
809
00:39:10,282 --> 00:39:12,916
on an iPhone so we're gonna present this thing modally.
810
00:39:12,918 --> 00:39:15,652
So it presents it modally and this is what an action sheet
811
00:39:15,654 --> 00:39:18,288
looks like, one presented modally, okay, versus
812
00:39:18,290 --> 00:39:22,792
pop-over style So it's back to presenting the same way. Now,
813
00:39:22,794 --> 00:39:24,494
of course, it doesn't even look at
814
00:39:24,496 --> 00:39:26,429
this pop over presentation controller because
815
00:39:26,431 --> 00:39:29,432
it didn't end up presenting it with the pop over style.
816
00:39:29,434 --> 00:39:31,901
It adapted and presented it with a normal modal style.
817
00:39:31,903 --> 00:39:35,038
So this is great. You can write this one code and
818
00:39:35,040 --> 00:39:37,841
it'll work on both devices. Okay.
819
00:39:37,843 --> 00:39:42,412
All right, what about alerts? Okay,
820
00:39:42,414 --> 00:39:45,181
so alerts are almost exactly the same as Action Cheat. You
821
00:39:45,183 --> 00:39:49,152
just say the style is Alert here instead of Action Cheat.
822
00:39:49,154 --> 00:39:53,590
You still do all the same thing with Add Action, okay,
823
00:39:53,592 --> 00:39:56,659
your Cancel Button and Add Action for
824
00:39:56,661 --> 00:39:57,861
the login button. Okay?
825
00:39:57,863 --> 00:40:00,530
So just like same thing we did with the cl, closures and
826
00:40:00,532 --> 00:40:03,166
all that stuff right here. One thing that's different about
827
00:40:03,168 --> 00:40:05,802
the alert, of course, you've got this text field. So how do
828
00:40:05,804 --> 00:40:08,371
you deal with the fact there's text fields in an alert but
829
00:40:08,373 --> 00:40:11,174
not an action sheet? And the answer is
830
00:40:11,176 --> 00:40:15,011
AlertController has a method addTextFieldWithConfiguration-
831
00:40:15,013 --> 00:40:18,047
Handler. This only does something on alerts,
832
00:40:18,049 --> 00:40:19,916
it doesn't do anything if it's an action sheet. And
833
00:40:19,918 --> 00:40:23,787
this closure will be called with the text field that
834
00:40:23,789 --> 00:40:27,457
you're adding, and allow you to configure it in here.
835
00:40:27,459 --> 00:40:28,825
So that's all this little closure does,
836
00:40:28,827 --> 00:40:31,027
is just configuring the text field. So for example,
837
00:40:31,029 --> 00:40:34,097
I want guidance system password as my placeholder
838
00:40:34,099 --> 00:40:36,766
text in this text field, so I've just set textfield's
839
00:40:36,768 --> 00:40:40,804
placeholder to be Guidance System Password there, okay.
840
00:40:40,806 --> 00:40:43,940
So this adds a text field to the alert, and
841
00:40:43,942 --> 00:40:46,075
this closure just lets you configure it.
842
00:40:46,077 --> 00:40:49,345
Now, what happens when someone types in here? How do you get
843
00:40:49,347 --> 00:40:53,283
the text out of there? Well in the action, like for
844
00:40:53,285 --> 00:40:56,352
cancel, we went away, we, we don't even look at the text.
845
00:40:56,354 --> 00:40:58,955
But for log in, I wanna look at this password.
846
00:40:58,957 --> 00:41:00,523
So in the action closure here for
847
00:41:00,525 --> 00:41:03,092
login, you see that right there, the action closure?
848
00:41:03,094 --> 00:41:09,232
I'm going to say, if I can let tf = self.alert.textFields?,
849
00:41:09,234 --> 00:41:12,435
is option because you don't always have text field .first.
850
00:41:12,437 --> 00:41:15,004
Okay, I only have one text field so I want the first one.
851
00:41:15,006 --> 00:41:17,807
Then, I've got the text field and I could, for example,
852
00:41:17,809 --> 00:41:21,044
log in with the password tf.text. Okay, so
853
00:41:21,046 --> 00:41:24,380
the answer is I use this text field's bar which is an array
854
00:41:24,382 --> 00:41:29,018
of all the text fields that are in this alert. Okay,
855
00:41:29,020 --> 00:41:32,655
so that's it, that's it for action alert.
856
00:41:32,657 --> 00:41:32,922
Pretty easy to use,
857
00:41:32,924 --> 00:41:35,425
you're almost certain to use them in your final project.
858
00:41:35,427 --> 00:41:38,394
That's why I'm covering them early in the final project
859
00:41:38,396 --> 00:41:43,233
period here. So hopefully that gives you all the things,
860
00:41:43,235 --> 00:41:48,104
everything you need to know to do that. All right, and
861
00:41:48,106 --> 00:41:51,975
of course, we present this thing exact same way,
862
00:41:51,977 --> 00:41:53,209
presentViewController, okay.
863
00:41:53,211 --> 00:41:55,778
It's just modally presented, happens to draw in this way,
864
00:41:55,780 --> 00:41:58,648
but it's still just modally presented, so we use the same
865
00:41:58,650 --> 00:42:02,752
presentViewController there, okay. And
866
00:42:02,754 --> 00:42:08,124
it looks the same both on iPad and iPhone. All right, so
867
00:42:08,126 --> 00:42:10,593
Cloud Kit. So I'm covering Cloud Kit,
868
00:42:10,595 --> 00:42:12,128
again I don't usually cover Cloud Kit. But
869
00:42:12,130 --> 00:42:14,330
I'm covering it this quarter because I think it's gonna be
870
00:42:14,332 --> 00:42:17,767
something really cool and powerful for you to use for
871
00:42:17,769 --> 00:42:21,271
your final projects, okay. So what is Cloud Kit?
872
00:42:21,273 --> 00:42:24,340
Cloud Kit is a database in the cloud, okay.
873
00:42:24,342 --> 00:42:28,545
It's a feature of iCloud and it's very simple to use, okay.
874
00:42:28,547 --> 00:42:31,614
And it has very basic database operations,
875
00:42:31,616 --> 00:42:34,751
okay. It's over the network, so everything that you're
876
00:42:34,753 --> 00:42:36,719
doing is happening real time on the network, so
877
00:42:36,721 --> 00:42:40,189
it's all asynchronous, of course. Because the network
878
00:42:40,191 --> 00:42:42,258
could be slow or even not available, okay.
879
00:42:42,260 --> 00:42:44,794
That requires you to do some thoughtful programing,
880
00:42:44,796 --> 00:42:47,363
as you have learned with everything is asynchronous,
881
00:42:47,365 --> 00:42:49,766
you kinda have to like, think about what it means for
882
00:42:49,768 --> 00:42:52,869
these methods to, not call you back until much later, okay.
883
00:42:52,871 --> 00:42:56,272
So, otherwise, it's really nice simple, database
884
00:42:56,274 --> 00:42:59,075
mechanism to use. Here are the important concepts,
885
00:42:59,077 --> 00:43:02,745
okay, that you need to know. One is record type.
886
00:43:02,747 --> 00:43:05,448
Record type is like an entity in core data.
887
00:43:05,450 --> 00:43:07,517
We're gonna do a lot of parallels to core data,
888
00:43:07,519 --> 00:43:11,387
cuz you know core data so well so know that terminology, so
889
00:43:11,389 --> 00:43:14,023
record type is kind of like entity.
890
00:43:14,025 --> 00:43:17,994
Fields are like attributes. Okay, so fields
891
00:43:17,996 --> 00:43:21,497
are the data that's stored in a record, type record.
892
00:43:21,499 --> 00:43:26,603
A record is an instance of a record type. So I've got,
893
00:43:26,605 --> 00:43:31,207
just like, it's like, sort of like NSManageObject in,
894
00:43:31,209 --> 00:43:34,377
core data. But of course it's so much more lightweight,
895
00:43:34,379 --> 00:43:36,179
there isn't any of that infrastructure.
896
00:43:36,181 --> 00:43:36,579
But it's kind of like that, so
897
00:43:36,581 --> 00:43:40,283
it's an instance of a certain record. A reference is kinda
898
00:43:40,285 --> 00:43:45,588
like a relationship to another entity or another record type
899
00:43:45,590 --> 00:43:48,725
there, okay reference. A database is a place where you
900
00:43:48,727 --> 00:43:53,529
store your records. A zone is a sub-area of a database.
901
00:43:53,531 --> 00:43:54,864
I'm not gonna talk about zones, but
902
00:43:54,866 --> 00:43:58,067
if you divide your database up into zones you can do cool
903
00:43:58,069 --> 00:43:59,602
things like, do efficient queries,
904
00:43:59,604 --> 00:44:02,171
where you're only querying over a subset of your data,
905
00:44:02,173 --> 00:44:05,141
like this in-sum zone. Okay, I'm not gonna talk about that,
906
00:44:05,143 --> 00:44:06,309
but that's what a zone is all about,
907
00:44:06,311 --> 00:44:08,277
it's a subarea of a database for searching and
908
00:44:08,279 --> 00:44:12,582
stuff. A container is a collection of databases, we'll
909
00:44:12,584 --> 00:44:17,420
talk about that. A query, is a NSPredicate based search.
910
00:44:17,422 --> 00:44:20,123
Of course, NSPredicates can be quite a bit simpler,
911
00:44:20,125 --> 00:44:21,424
because it's a simpler database,
912
00:44:21,426 --> 00:44:24,627
but, it's still, similar to core data. You're gonna use,
913
00:44:24,629 --> 00:44:27,130
actually use the class and its predicate to make '.
914
00:44:27,132 --> 00:44:31,734
And then a subscription is a standing query, okay.
915
00:44:31,736 --> 00:44:34,837
It's, basically an NSPredicate, you can upload to
916
00:44:34,839 --> 00:44:37,674
the database, and any time it changes it will send you
917
00:44:37,676 --> 00:44:41,844
a push notification, which is fantastic. This is the coolest
918
00:44:41,846 --> 00:44:45,014
feature of all in Cloud Kit, is that it will send you push
919
00:44:45,016 --> 00:44:46,949
notifications when the database changes, okay.
920
00:44:46,951 --> 00:44:49,786
And then all you need to do is just, give it a standing
921
00:44:49,788 --> 00:44:52,855
query, called a subscription, and it'll do that, okay.
922
00:44:52,857 --> 00:44:55,858
All right, so let's talk about how all this works,
923
00:44:55,860 --> 00:44:59,562
how do we make this Cloud Kit thing happen. First thing you
924
00:44:59,564 --> 00:45:02,298
have to understand about Cloud Kit is a very important thing
925
00:45:02,300 --> 00:45:05,902
here called the Cloud Kit Dashboard. This is a website,
926
00:45:05,904 --> 00:45:09,672
okay. You get to it by going to your project, okay,
927
00:45:09,674 --> 00:45:11,541
click on your project. Actually,
928
00:45:11,543 --> 00:45:13,476
I'll show in a second how you can get to this. But
929
00:45:13,478 --> 00:45:14,844
you can click somewhere in your project, and
930
00:45:14,846 --> 00:45:16,379
it's gonna take you to this website.
931
00:45:16,381 --> 00:45:18,848
This is gonna have all your record types,
932
00:45:18,850 --> 00:45:19,882
all your entities, and
933
00:45:19,884 --> 00:45:22,118
all of their fields, all their attributes.
934
00:45:22,120 --> 00:45:25,722
So here's an entity called QandA, it's a question and
935
00:45:25,724 --> 00:45:27,824
answer thing. Here's, it's got answers,
936
00:45:27,826 --> 00:45:30,526
which is a list of strings, an array of strings,
937
00:45:30,528 --> 00:45:33,463
and it's got a question, which is a single string. And
938
00:45:33,465 --> 00:45:36,766
these are all indexed, both for querying and searching and
939
00:45:36,768 --> 00:45:39,769
even sorting, okay. Here's another one,
940
00:45:39,771 --> 00:45:42,538
response, it's got some things. Over here, okay,
941
00:45:42,540 --> 00:45:46,209
you can actually go to default zone right here, you'll
942
00:45:46,211 --> 00:45:50,213
be able to actually see the data stored in these records,
943
00:45:50,215 --> 00:45:53,883
which is kinda cool. And then it's got some other
944
00:45:53,885 --> 00:45:57,153
things here like the subscriptions that you have
945
00:45:57,155 --> 00:45:58,921
outstanding out there.
946
00:45:58,923 --> 00:46:01,324
Maybe any users that are using the database etc.
947
00:46:01,326 --> 00:46:03,960
So this is kind of your place where you're gonna go to find
948
00:46:03,962 --> 00:46:06,729
out everything that's going on in your database in real time,
949
00:46:06,731 --> 00:46:11,934
'kay. The dashboard also has interesting
950
00:46:11,936 --> 00:46:14,837
metadata indexes, and I'm putting a slide on this,
951
00:46:14,839 --> 00:46:18,274
cuz this is easy to forget. Okay, because, of course,
952
00:46:18,276 --> 00:46:21,210
the QandA thing has answers and question fields, but
953
00:46:21,212 --> 00:46:24,380
it also has these fields like the record ID which is
954
00:46:24,382 --> 00:46:28,785
a unique ID for this QandA record. Also, created by,
955
00:46:28,787 --> 00:46:32,421
that's the user who created it, that's very interesting.
956
00:46:32,423 --> 00:46:33,689
The date it was created.
957
00:46:33,691 --> 00:46:36,392
So these are all kind of fields you always get, and
958
00:46:36,394 --> 00:46:39,529
by default none of them are index however. So if you
959
00:46:39,531 --> 00:46:42,732
wanted to search and say show me all the QandA's that were
960
00:46:42,734 --> 00:46:45,668
created by a certain user, that wouldn't work, unless you
961
00:46:45,670 --> 00:46:50,439
go in here in the dashboard and turn on query index for
962
00:46:50,441 --> 00:46:53,142
created by. Okay, so don't forget about this. This is
963
00:46:53,144 --> 00:46:56,045
something easy to forget to do cuz they're automatic off and
964
00:46:56,047 --> 00:46:59,882
you're like how come that they won't show me all the QandA's
965
00:46:59,884 --> 00:47:00,683
by this user,
966
00:47:00,685 --> 00:47:04,187
it's because it's not indexed, okay. So that,
967
00:47:04,189 --> 00:47:05,721
you need to click on this little guy here,
968
00:47:05,723 --> 00:47:09,425
metadata indexes, to show this. Okay now,
969
00:47:09,427 --> 00:47:14,130
that schema that we're talking about there, the entities and
970
00:47:14,132 --> 00:47:16,566
the attributes okay, the record types and the fields,
971
00:47:16,568 --> 00:47:20,536
you can create it all in the dashboard if you want, but
972
00:47:20,538 --> 00:47:21,204
you don't have to.
973
00:47:21,206 --> 00:47:23,940
You can just start creating them in your code and
974
00:47:23,942 --> 00:47:26,209
it'll automatically create the schema for you. Every time
975
00:47:26,211 --> 00:47:28,644
you will reference an entity that wasn't there before,
976
00:47:28,646 --> 00:47:31,347
it's gonna create a new entity for it. Every time you access
977
00:47:31,349 --> 00:47:35,051
a field on a certain entity, you know, record type,
978
00:47:35,053 --> 00:47:37,720
it's gonna create an attribute automatically. Now,
979
00:47:37,722 --> 00:47:40,122
you're still gonna have to go to dashboard and turn on or
980
00:47:40,124 --> 00:47:43,159
off any indexes and things like that, but
981
00:47:43,161 --> 00:47:45,761
it's just gonna create it all for you on the fly.
982
00:47:45,763 --> 00:47:48,231
That only happens though during development,
983
00:47:48,233 --> 00:47:50,867
once you deploy your app over at the app store,
984
00:47:50,869 --> 00:47:52,535
now that's not true anymore, okay.
985
00:47:52,537 --> 00:47:56,572
So your app has to have its schema built in development
986
00:47:56,574 --> 00:48:00,476
and then you export that schema. When you run in
987
00:48:00,478 --> 00:48:05,214
deployment environment, okay? So this is really cool for
988
00:48:05,216 --> 00:48:07,884
development because you don't have to do a lot of tedious
989
00:48:07,886 --> 00:48:12,154
creating of things in your in your dashboard.
990
00:48:12,156 --> 00:48:14,657
And you can always go in the dashboard and delete things.
991
00:48:14,659 --> 00:48:18,060
And it's really actually quite flexible and cool.
992
00:48:18,062 --> 00:48:22,031
Now none of this will work until you unable Cloud Kit
993
00:48:22,033 --> 00:48:24,800
in your capabilities. Remember I'd show you the capabilities
994
00:48:24,802 --> 00:48:28,337
earlier. You go to the very top one here iCloud and
995
00:48:28,339 --> 00:48:30,072
you're gonna click this from off to on.
996
00:48:30,074 --> 00:48:32,975
Okay if you don't do that none of this works. So
997
00:48:32,977 --> 00:48:36,746
when you click it to on. You get this settings right here.
998
00:48:36,748 --> 00:48:40,316
You're gonna wanna click this switch right here Cloud Kit.
999
00:48:40,318 --> 00:48:41,884
That's all this stuff I'm gonna be talking about here in
1000
00:48:41,886 --> 00:48:45,454
this lecture, okay? Cloud Kit. There's also Key Value Storage
1001
00:48:45,456 --> 00:48:49,125
which is kind of cool, you can store like an NS user defaults
1002
00:48:49,127 --> 00:48:50,893
on the Cloud. That's kind of cool features.
1003
00:48:50,895 --> 00:48:53,262
Well, I'm not gonna talk about that. It's there if you wanna
1004
00:48:53,264 --> 00:48:56,766
do it and of course you can store documents in the Cloud.
1005
00:48:56,768 --> 00:48:59,268
Manage document Remember that from core data?
1006
00:48:59,270 --> 00:49:02,905
It stores all the core data stuff in the cloud, okay?
1007
00:49:02,907 --> 00:49:04,440
So, so you could have that as well.
1008
00:49:04,442 --> 00:49:08,644
But today, we're talking about this Cloud Kit right here,
1009
00:49:08,646 --> 00:49:11,047
all right? This button right here,
1010
00:49:11,049 --> 00:49:12,715
is how you get to that Cloud Kit dashboard.
1011
00:49:12,717 --> 00:49:16,786
This will open up your browser and take you to that thing,
1012
00:49:16,788 --> 00:49:22,725
for you. Kay? This right here is the containers. Remember I
1013
00:49:22,727 --> 00:49:25,895
said a container is a thing that contains databases.
1014
00:49:25,897 --> 00:49:29,198
We're only gonna talk about use default container here.
1015
00:49:29,200 --> 00:49:32,201
But you're gonna actually create your custom containers.
1016
00:49:32,203 --> 00:49:35,771
And what's cool about these containers is these containers
1017
00:49:35,773 --> 00:49:39,809
can be used across different kinds of apps. Okay?
1018
00:49:39,811 --> 00:49:41,544
In other words, you might be a company in
1019
00:49:41,546 --> 00:49:43,779
your shipment four different apps, and
1020
00:49:43,781 --> 00:49:45,314
they wanted to share data.
1021
00:49:45,316 --> 00:49:49,085
They can do that by creating a shared container, okay?
1022
00:49:49,087 --> 00:49:52,521
The default container is just to use by your one app, okay?
1023
00:49:52,523 --> 00:49:56,225
I'm shipping to my app, all my users of that one app can use
1024
00:49:56,227 --> 00:49:59,295
this default container. Okay, and that's what I'm gonna
1025
00:49:59,297 --> 00:50:01,764
talk about today, but you can just know that this custom
1026
00:50:01,766 --> 00:50:04,900
container feature is available for your final project,
1027
00:50:04,902 --> 00:50:07,103
the default container's gonna be plenty, okay?
1028
00:50:07,105 --> 00:50:10,339
All right, so let's talk about these containers and
1029
00:50:10,341 --> 00:50:13,542
what's in them, okay? And because we need to know that,
1030
00:50:13,544 --> 00:50:15,411
so we can create records in the database,
1031
00:50:15,413 --> 00:50:19,115
okay? So we get a database from the container.
1032
00:50:19,117 --> 00:50:21,884
Remember, I said container contains databases.
1033
00:50:21,886 --> 00:50:25,855
And the default container actually has two databases,
1034
00:50:25,857 --> 00:50:28,858
a public database and a private database.
1035
00:50:28,860 --> 00:50:31,260
The difference between these two is that the private
1036
00:50:31,262 --> 00:50:35,564
database can only be looked at by the currently logged in
1037
00:50:35,566 --> 00:50:39,902
iCloud user. Okay, so you know how on your phone, hopefully,
1038
00:50:39,904 --> 00:50:42,338
you're logged into iCloud, right? Okay, so,
1039
00:50:42,340 --> 00:50:46,409
whatever that login is, only the person logged in as that
1040
00:50:46,411 --> 00:50:49,612
iCloud can see the private database for that iCloud view.
1041
00:50:49,614 --> 00:50:50,346
So, it is exactly what it sounds like,
1042
00:50:50,348 --> 00:50:53,449
it is private information. The public database can be seen by
1043
00:50:53,451 --> 00:50:58,988
anybody, okay, anybody who is running your app. Can see
1044
00:50:58,990 --> 00:51:02,324
this public database. Got the difference between public and
1045
00:51:02,326 --> 00:51:05,094
private? So public is where you're gonna have data that's
1046
00:51:05,096 --> 00:51:08,130
shared between your users, private data is where you're
1047
00:51:08,132 --> 00:51:11,200
gonna have data that the user sees on all their devices but
1048
00:51:11,202 --> 00:51:15,404
the other users can't see. All right. So that's how you
1049
00:51:15,406 --> 00:51:17,406
get the database. Once you have the database.
1050
00:51:17,408 --> 00:51:18,441
You can do all kinds of things.
1051
00:51:18,443 --> 00:51:20,609
Create records query all kinds of stuff.
1052
00:51:20,611 --> 00:51:24,180
So let's starts with how to create record on the database.
1053
00:51:24,182 --> 00:51:27,583
Okay. You create a record first of all by distributing
1054
00:51:27,585 --> 00:51:31,320
CKRecord, Cloud Kit record and all you do is specified
1055
00:51:31,322 --> 00:51:34,190
the record type. Okay, that's like the entity name,
1056
00:51:34,192 --> 00:51:37,560
like tweet or Twitter user, whatever, it's just a string,
1057
00:51:37,562 --> 00:51:40,396
okay. If that entity doesn't exist in the database when you
1058
00:51:40,398 --> 00:51:45,301
save this record, it'll create that entity, okay? Make sense?
1059
00:51:45,303 --> 00:51:48,170
All right, so now you have this record, okay?
1060
00:51:48,172 --> 00:51:51,474
How do you put information into the record? Well, you use
1061
00:51:51,476 --> 00:51:55,177
a dictionary-like syntax. So here I've got a record,
1062
00:51:55,179 --> 00:52:00,015
right here. And I'm adding some fields to it by saying,
1063
00:52:00,017 --> 00:52:03,219
records of text equals something, records of created
1064
00:52:03,221 --> 00:52:05,754
equals something, records of items equals something, So,
1065
00:52:05,756 --> 00:52:07,490
you see how ithat looks a dictionary? It looks like
1066
00:52:07,492 --> 00:52:10,526
records is almost a dictionary right there And so
1067
00:52:10,528 --> 00:52:13,462
that's actually use it, now what are these values,
1068
00:52:13,464 --> 00:52:15,564
these things that I have dot, dot, dot right here,
1069
00:52:15,566 --> 00:52:18,267
what are these? Well, these can only be things
1070
00:52:18,269 --> 00:52:22,338
that implement the protocol CKRecordValue, okay? And
1071
00:52:22,340 --> 00:52:24,874
the only things the implement that protocol are these seven
1072
00:52:24,876 --> 00:52:29,178
things in green. Strings, numbers, arrays and dates.
1073
00:52:29,180 --> 00:52:32,148
And of course, we get the bridged Swift types, so
1074
00:52:32,150 --> 00:52:36,352
that means doubles, ints, string, array, okay? And
1075
00:52:36,354 --> 00:52:39,989
then also these three special things, CKReference,
1076
00:52:39,991 --> 00:52:42,758
CKAssett and CLLocation, okay?
1077
00:52:42,760 --> 00:52:47,930
Now array can be array of any of the other types. Okay?
1078
00:52:47,932 --> 00:52:49,865
The array of any of these other green things.
1079
00:52:49,867 --> 00:52:52,268
So you can have an array of numbers, array of strings,
1080
00:52:52,270 --> 00:52:56,305
array of dates,
1081
00:52:56,307 --> 00:53:00,643
CKReference is a reference to another record. Okay, so this
1082
00:53:00,645 --> 00:53:03,712
is how you do relationships, okay? So you have a record.
1083
00:53:03,714 --> 00:53:09,318
So again, if you had tweeter, and the tweets, okay?
1084
00:53:09,320 --> 00:53:12,955
The tweet record type would have a field
1085
00:53:12,957 --> 00:53:18,861
that is a CKReference to a Twitter user, okay?
1086
00:53:18,863 --> 00:53:23,599
CKAsset is for storing big things, like images, sounds,
1087
00:53:23,601 --> 00:53:28,370
video, okay, files basically. Now, This cloud kit does
1088
00:53:28,372 --> 00:53:31,507
a really cool job of when you put something in the cloud it
1089
00:53:31,509 --> 00:53:34,376
doesn't immediately just start cranking on the network piling
1090
00:53:34,378 --> 00:53:37,813
that thing up on the net, it stores it locally and
1091
00:53:37,815 --> 00:53:40,182
when it wants to, when there's good network connection,
1092
00:53:40,184 --> 00:53:44,386
whatever, it's uploading it to the cloud. And vice versa. So
1093
00:53:44,388 --> 00:53:46,021
It's doing all this behind the scenes and
1094
00:53:46,023 --> 00:53:46,388
being very efficient and
1095
00:53:46,390 --> 00:53:50,092
caching it etc. CL location, we're gonna talk about next
1096
00:53:50,094 --> 00:53:52,394
week when we talk about maps and locations.
1097
00:53:52,396 --> 00:53:55,831
It's just a GPS coordinate, okay, someplace on earth,
1098
00:53:55,833 --> 00:54:00,102
so you can store something as a GPS coordinate. Okay, so
1099
00:54:00,104 --> 00:54:02,938
that's it, it's super. Easy to create these records. Again as
1100
00:54:02,940 --> 00:54:05,608
you put these field names in here if that doesn't exist in
1101
00:54:05,610 --> 00:54:08,677
the schema it's just gonna add that field okay as soon as you
1102
00:54:08,679 --> 00:54:11,614
store this to the database. So here we've created the record
1103
00:54:11,616 --> 00:54:13,749
but we haven't stored it yet okay it's not it hasn't been
1104
00:54:13,751 --> 00:54:16,585
uploaded to the network we've only created it locally.
1105
00:54:16,587 --> 00:54:21,590
So how do we get the information. So we
1106
00:54:21,592 --> 00:54:24,927
saw how to put the information record how do we get it out
1107
00:54:24,929 --> 00:54:26,395
You again use dictionary syntax,
1108
00:54:26,397 --> 00:54:29,965
so recor|"text| to get the text out of the record. But
1109
00:54:29,967 --> 00:54:34,737
you have to cast, because all of these things come back as
1110
00:54:34,739 --> 00:54:38,274
CKRecordValues. Okay? Cuz I told you, the only things that
1111
00:54:38,276 --> 00:54:40,943
can go in are things that implement that protocol.
1112
00:54:40,945 --> 00:54:42,511
So when you get it out, it comes out,
1113
00:54:42,513 --> 00:54:44,346
it's a thing that implements that protocol.
1114
00:54:44,348 --> 00:54:45,180
So you have to cast it.
1115
00:54:45,182 --> 00:54:49,251
To the thing it actually is, okay. Make sense? That's why
1116
00:54:49,253 --> 00:54:52,254
even like records of items. That's an array of strings.
1117
00:54:52,256 --> 00:54:56,292
You have to say as array of string, okay, got it.
1118
00:54:56,294 --> 00:54:58,827
And of course, these all will be optional because as
1119
00:54:58,829 --> 00:55:03,432
question mark. So I might wanna say if let on all these.
1120
00:55:03,434 --> 00:55:08,070
Okay. Now, I've got the record creator, how do I put it into
1121
00:55:08,072 --> 00:55:11,707
the database? It's, actually, the real answer is, or
1122
00:55:11,709 --> 00:55:15,010
the full answer is you're gonna create an NSOperation,
1123
00:55:15,012 --> 00:55:17,680
okay? Do you remember NSOperation, NSOperationQueue?
1124
00:55:17,682 --> 00:55:21,450
That's the object-oriented version of the GCD, right?
1125
00:55:21,452 --> 00:55:22,251
Dispatch async and
1126
00:55:22,253 --> 00:55:26,155
all that stuff So the way you actually create
1127
00:55:26,157 --> 00:55:28,757
records in the database is you create a special kind of
1128
00:55:28,759 --> 00:55:33,529
NS operation called CKModifyRecordsOperation and
1129
00:55:33,531 --> 00:55:36,365
you can give it a bunch of records and you create and
1130
00:55:36,367 --> 00:55:40,369
NS or operation cue, okay. You could use the main cue, okay,
1131
00:55:40,371 --> 00:55:43,405
or you can create you're own cue. Those cues are just like
1132
00:55:43,407 --> 00:55:45,774
threads, okay, they're like Not like threads,
1133
00:55:45,776 --> 00:55:48,010
they're cues, just like dispatch under bar cue,
1134
00:55:48,012 --> 00:55:51,847
under bar tee things. And then you just tell that, if you put
1135
00:55:51,849 --> 00:55:55,617
this ModifyRecordsOperation on a cue it'll go and
1136
00:55:55,619 --> 00:55:56,318
execute it and
1137
00:55:56,320 --> 00:55:57,219
update the database. But
1138
00:55:57,221 --> 00:56:00,422
that's a little bit of a pain in the neck to go do all that,
1139
00:56:00,424 --> 00:56:04,159
okay. So there's a nice little convenience method right in
1140
00:56:04,161 --> 00:56:08,397
database called save record. And it just takes a record and
1141
00:56:08,399 --> 00:56:10,666
a completion handler. And it saves the record and
1142
00:56:10,668 --> 00:56:12,935
calls your completion handler when it's done. Okay, and
1143
00:56:12,937 --> 00:56:15,571
remember that can be quite a ways later because this is
1144
00:56:15,573 --> 00:56:17,506
happening over the network. Your network might be slow,
1145
00:56:17,508 --> 00:56:19,942
you might be out of range of network, whatever. So
1146
00:56:19,944 --> 00:56:22,745
it can take a very, very long time for this to come back.
1147
00:56:22,747 --> 00:56:27,116
It could even possibly time out but this is how it works.
1148
00:56:27,118 --> 00:56:28,283
S this is on the database.
1149
00:56:28,285 --> 00:56:29,051
Remember the public database or
1150
00:56:29,053 --> 00:56:32,087
the private database that you got from the container?
1151
00:56:32,089 --> 00:56:34,556
That's where you send this method. Okay?
1152
00:56:34,558 --> 00:56:37,292
Now this thing cant do everything that this whole
1153
00:56:37,294 --> 00:56:42,765
NSoperationQ can write multiple records at once.
1154
00:56:42,767 --> 00:56:45,200
It also knows how to overwrite a newer version.
1155
00:56:45,202 --> 00:56:47,703
Okay, this does optimistic locking right here, the save
1156
00:56:47,705 --> 00:56:50,906
record one. So if it tries to write something and there's
1157
00:56:50,908 --> 00:56:54,076
a newer version of that thing it's just gonna fail.
1158
00:56:54,078 --> 00:56:54,510
Report an error.
1159
00:56:54,512 --> 00:56:58,747
It'll come back with an error here in the handler whereas
1160
00:56:58,749 --> 00:57:01,683
the modify records operation knows how to
1161
00:57:01,685 --> 00:57:05,421
overwrite what's in there. But save record for
1162
00:57:05,423 --> 00:57:07,923
the most part does what you want most of the time. And for
1163
00:57:07,925 --> 00:57:10,559
your final project this is gonna be plenty probably.
1164
00:57:10,561 --> 00:57:13,996
Okay, but you're not gonna need this operation thing.
1165
00:57:14,365 --> 00:57:16,999
All right, this happens all asychronously.
1166
00:57:17,001 --> 00:57:19,768
Be very careful specially be careful with the fact
1167
00:57:19,770 --> 00:57:23,439
that your closure they gets cold when they save record is
1168
00:57:23,441 --> 00:57:27,109
finished Happens on another thread besides the main queue.
1169
00:57:27,111 --> 00:57:28,510
Okay, it's not in the main queue.
1170
00:57:28,512 --> 00:57:31,413
So you need to dispatch async back to the main queue from
1171
00:57:31,415 --> 00:57:33,849
there if you want to do some UI stuff when
1172
00:57:33,851 --> 00:57:37,486
that record is saved. It make sense? Okay, and
1173
00:57:37,488 --> 00:57:39,388
you also just need an architecture code properly.
1174
00:57:39,390 --> 00:57:42,524
You need to think about the whole design of your app
1175
00:57:42,526 --> 00:57:45,093
when the data that's appearing in it
1176
00:57:45,095 --> 00:57:46,962
might be coming asychronously. So
1177
00:57:46,964 --> 00:57:50,098
you need to know how to have apps that have basically blank
1178
00:57:50,100 --> 00:57:52,935
UIs with either spinning wheels or Loading or
1179
00:57:52,937 --> 00:57:55,471
something on there, so the user knows what's going on.
1180
00:57:55,473 --> 00:57:57,673
And, when the data comes, it populates it. Okay?
1181
00:57:57,675 --> 00:58:00,008
It's very similar to, you do the smash tag where
1182
00:58:00,010 --> 00:58:02,544
you have the little profile images, and they come up and
1183
00:58:02,546 --> 00:58:06,148
they were blank. And then they just started filling in. Same
1184
00:58:06,150 --> 00:58:08,517
kind of thing here, you have data in your database that
1185
00:58:08,519 --> 00:58:11,119
starts out, you don't have it and then it starts filling in.
1186
00:58:11,121 --> 00:58:14,389
And what does that mean for the user? Means things should
1187
00:58:14,391 --> 00:58:17,259
start appearing over time. So you gotta think about how to
1188
00:58:17,261 --> 00:58:21,730
architect your app to use asyncronity. All right, so
1189
00:58:21,732 --> 00:58:22,664
here's what it looks like to create
1190
00:58:22,666 --> 00:58:24,867
a record in the database all put together here.
1191
00:58:24,869 --> 00:58:26,435
So I'm going to create a tweet right here.
1192
00:58:26,437 --> 00:58:29,404
So here I'm letting tweet equal ckrecord.
1193
00:58:29,406 --> 00:58:29,972
Just created a record.
1194
00:58:29,974 --> 00:58:32,908
I'm gonna have the Tweets text to be 140 characters of pure
1195
00:58:32,910 --> 00:58:35,811
joy. Then I'm gonna get my database by saying
1196
00:58:35,813 --> 00:58:38,380
CKContainer.defaultContainer publicCloudDatabase. So I'm
1197
00:58:38,382 --> 00:58:40,949
gonna put this in the public database that all users of my
1198
00:58:40,951 --> 00:58:44,219
app can see. Then I'm just gonna ask the database to save
1199
00:58:44,221 --> 00:58:47,322
the record Tweet, right. Here's that record Tweet and
1200
00:58:47,324 --> 00:58:49,591
it's gonna call this closure, this big thing,
1201
00:58:49,593 --> 00:58:53,195
when it's done. And this closure test two arguments,
1202
00:58:53,197 --> 00:58:55,264
the record that it saved or
1203
00:58:55,266 --> 00:59:00,068
nil if it failed to save it and an error, okay.
1204
00:59:00,070 --> 00:59:05,073
Again, nil if it saved it, not nil if it failed to save it,
1205
00:59:05,075 --> 00:59:07,476
okay. It doesn't return anything and so here's why
1206
00:59:07,478 --> 00:59:10,345
doing my closure almost all of the time first I'm gonna say,
1207
00:59:10,347 --> 00:59:12,748
if the error equals nil Hooray okay,
1208
00:59:12,750 --> 00:59:15,684
it saved it successfully, so now I do whatever I do when
1209
00:59:15,686 --> 00:59:19,421
it was successfully saved. Maybe nothing okay? Maybe I do
1210
00:59:19,423 --> 00:59:23,592
something else, I don't know, but here's success. Sometimes
1211
00:59:23,594 --> 00:59:27,362
there are 29 different errors in ck error code, so
1212
00:59:27,364 --> 00:59:31,300
a lot of things can go wrong. Network unavailable, all kinds
1213
00:59:31,302 --> 00:59:35,003
of things I just showed you how you handle these by
1214
00:59:35,005 --> 00:59:37,439
picking one at random, which is not authenticated.
1215
00:59:37,441 --> 00:59:40,008
Okay? Let's say the person is not logged into iCloud.
1216
00:59:40,010 --> 00:59:42,578
Kay? They just never went to their settings and
1217
00:59:42,580 --> 00:59:43,545
logged into iCloud. Okay? So
1218
00:59:43,547 --> 00:59:46,515
you're gonna get this error, CKErrorCode.NotAuthenticated.
1219
00:59:46,517 --> 00:59:49,017
Don't forget put, to put .rawValue so you get it as
1220
00:59:49,019 --> 00:59:51,753
an integer because this error code is an integer. And
1221
00:59:51,755 --> 00:59:54,456
so if there's that then I'm gonna maybe put up an alert
1222
00:59:54,458 --> 00:59:58,360
code that "Please log in to iCloud" or something, right?
1223
00:59:58,362 --> 01:00:02,664
And there's 29 other errors and so doing this programming
1224
01:00:02,666 --> 01:00:07,336
really right, is a lot of error handling code. Okay, so
1225
01:00:07,338 --> 01:00:10,472
in your final project You know the more you do of
1226
01:00:10,474 --> 01:00:13,275
this the more it's kind of like you can get forming
1227
01:00:13,277 --> 01:00:15,877
a one-point use of Cloud kit to a two-point use if you
1228
01:00:15,879 --> 01:00:18,413
actually handle the error, you see what I'm saying?
1229
01:00:18,415 --> 01:00:20,983
I don't expect you to handle every single error and
1230
01:00:20,985 --> 01:00:23,485
uh-uh you get zero points, okay. Of course not, okay,
1231
01:00:23,487 --> 01:00:26,521
the more error you handle, the more robust you are,
1232
01:00:26,523 --> 01:00:27,723
the better you're a synchronize use,
1233
01:00:27,725 --> 01:00:30,859
the more depth point you might get working in Cloud,
1234
01:00:30,861 --> 01:00:34,062
Cloud kit Okay, but there's lots of other errors here.
1235
01:00:34,064 --> 01:00:35,597
I'll show you some other errors that were going to
1236
01:00:35,599 --> 01:00:38,033
handle in some other, ones in a second here.
1237
01:00:38,035 --> 01:00:39,868
But this is how you're going to do it. Else if, else if,
1238
01:00:39,870 --> 01:00:43,572
else if, or you could just switch on the error code,
1239
01:00:43,574 --> 01:00:46,375
to go to handling errors. Okay.
1240
01:00:46,377 --> 01:00:49,945
I mean, at worst, you could just either put an alert up,
1241
01:00:49,947 --> 01:00:52,748
because the user is saying unknown error or something, or
1242
01:00:52,750 --> 01:00:55,150
you could print something on the console. I don't know.
1243
01:00:55,152 --> 01:00:58,120
But it's usually you want to try and collect
1244
01:00:58,122 --> 01:01:01,990
all the errors that you can do something about, okay?
1245
01:01:01,992 --> 01:01:06,361
Now again if tweet the entity doesn't exist in the database
1246
01:01:06,363 --> 01:01:10,265
it's gonna create it, okay? If text field doesn't exist on
1247
01:01:10,267 --> 01:01:12,834
this it's gonna create that attribute automatically for
1248
01:01:12,836 --> 01:01:16,405
me. Okay, so that's it. That's how you put a record in
1249
01:01:16,407 --> 01:01:20,909
the database real easy. Okay? Now, there's
1250
01:01:20,911 --> 01:01:24,613
a special kind of error, that you can get sometimes,
1251
01:01:24,615 --> 01:01:30,318
where, it will actually give you a retry time interval. So,
1252
01:01:30,320 --> 01:01:34,790
not all errors have this, but some errors when you get them,
1253
01:01:34,792 --> 01:01:35,724
Cloud Kit is basically saying hey,
1254
01:01:35,726 --> 01:01:38,627
you know this is the kinda area you probably wanna retry,
1255
01:01:38,629 --> 01:01:41,329
and here's a good amount of time to try, okay.
1256
01:01:41,331 --> 01:01:44,866
So it's basically telling you what an appropriate retry
1257
01:01:44,868 --> 01:01:45,300
interval would be.
1258
01:01:45,302 --> 01:01:49,171
And I believe if things continue to fail it, you know,
1259
01:01:49,173 --> 01:01:51,473
adds, makes the retry interval a little bit longer,
1260
01:01:51,475 --> 01:01:54,242
until it just gives up and says don't retry anymore.
1261
01:01:54,244 --> 01:01:55,944
More, but here is how you get it.
1262
01:01:55,946 --> 01:01:59,114
Error the NSError that you get back here in addition I have
1263
01:01:59,116 --> 01:02:02,284
an error it also has a user info dictionary and
1264
01:02:02,286 --> 01:02:07,055
in that dictionary is a key CKError retry after key and
1265
01:02:07,057 --> 01:02:10,559
it's an NS time interval okay it's actually an NS number but
1266
01:02:10,561 --> 01:02:13,328
you're gonna cast it to an NS time interval there And
1267
01:02:13,330 --> 01:02:16,298
then you gonna wanna dispatch back to the main queue because
1268
01:02:16,300 --> 01:02:17,365
you gonna wanna have do a timer and
1269
01:02:17,367 --> 01:02:20,102
we know that we do our timers on the main queue. And so I'm
1270
01:02:20,104 --> 01:02:23,505
gonna schedule a timer with that retry interval I'm just
1271
01:02:23,507 --> 01:02:27,609
gonna call some method but will probably try this again.
1272
01:02:27,611 --> 01:02:30,846
Okay maybe calls a method that this is in in fact.
1273
01:02:31,081 --> 01:02:35,817
Okay I found So basically something might put in
1274
01:02:35,819 --> 01:02:38,453
any kind of error that you'd be willing to retry.
1275
01:02:38,455 --> 01:02:41,590
Just check to see if there's a retry interval, there's is.
1276
01:02:41,592 --> 01:02:43,992
Give it a go. Try it again. Okay,
1277
01:02:43,994 --> 01:02:46,762
and if there is no retry interval then of course,
1278
01:02:46,764 --> 01:02:51,266
this won't happen. You won't be doing this timer thing.
1279
01:02:51,268 --> 01:02:55,403
Got it? Okay, now we've got the records stored.
1280
01:02:55,405 --> 01:02:58,406
Now we want to get them, okay? So we basically want a query
1281
01:02:58,408 --> 01:03:02,577
to get them. And querying is also super, super simple here.
1282
01:03:02,579 --> 01:03:05,347
You're just gonna do performQuery on the database.
1283
01:03:05,349 --> 01:03:07,549
Just like you did saveRecord on the database you're gonna
1284
01:03:07,551 --> 01:03:09,184
do performQuery and give it a query.
1285
01:03:09,186 --> 01:03:11,920
I'll talk about in a second. This inZoneWithID,
1286
01:03:11,922 --> 01:03:15,724
remember I've told you there were zones, subdatabase zones,
1287
01:03:15,726 --> 01:03:19,161
okay. So you can specify a zone there, or nil, actually
1288
01:03:19,163 --> 01:03:24,232
that's an optional string, so that could be nil. But if you
1289
01:03:24,234 --> 01:03:27,068
specify a zone, it will put, it will search only in zone,
1290
01:03:27,070 --> 01:03:29,971
so you can limit your search and be more efficient, okay,
1291
01:03:29,973 --> 01:03:33,408
if you can sensibly Divide your database into little
1292
01:03:33,410 --> 01:03:36,678
zones. To keep your searches smaller it's a good idea.
1293
01:03:36,680 --> 01:03:38,180
That's what that inZoneWithID is.
1294
01:03:38,182 --> 01:03:40,115
For your final project, again you could say nil and
1295
01:03:40,117 --> 01:03:43,752
just put everything in one big zone, default zone. And
1296
01:03:43,754 --> 01:03:46,121
then of course you're gonna call a completion handler, and
1297
01:03:46,123 --> 01:03:47,789
look what the completion handler has.
1298
01:03:47,791 --> 01:03:51,126
An array of the records that you searched for, couldn't be
1299
01:03:51,128 --> 01:03:55,330
simpler. Okay? Possibly an error. Okay, so this is just
1300
01:03:55,332 --> 01:03:57,732
really straightforward. You give it a query.
1301
01:03:57,734 --> 01:04:00,802
It just calls your handler with the records that matched.
1302
01:04:00,804 --> 01:04:03,638
Or nil if there was an error, okay?
1303
01:04:03,640 --> 01:04:07,309
Now what about this CKQuery, that's also trivial.
1304
01:04:07,311 --> 01:04:10,078
It's initializer is just the entity, okay?
1305
01:04:10,080 --> 01:04:14,549
So you wanna search for tweets and then an NSPredicate, okay?
1306
01:04:14,551 --> 01:04:17,719
And so this NSPredicate is gonna specify which things you
1307
01:04:17,721 --> 01:04:21,423
want in your database. And this NSPredicate is
1308
01:04:21,425 --> 01:04:24,726
obviously much simpler than Core Data because there
1309
01:04:24,728 --> 01:04:28,430
are just fewer things that you can do, fewer data type,
1310
01:04:28,432 --> 01:04:31,900
fewer, the relationships aren't as complicated,
1311
01:04:31,902 --> 01:04:35,604
there's just that reference thing etc. But
1312
01:04:35,606 --> 01:04:37,672
you can look in the documentation for CKQuery,
1313
01:04:37,674 --> 01:04:40,976
it has a very good description of all the things you can do
1314
01:04:40,978 --> 01:04:43,278
with NSPredicate for CKQuery. It's, you know,
1315
01:04:43,280 --> 01:04:46,114
all the things you might think like equals equals.
1316
01:04:46,116 --> 01:04:48,650
But also it has some cool things like in, okay?
1317
01:04:48,652 --> 01:04:50,352
Where you check whether something is in and
1318
01:04:50,354 --> 01:04:53,588
array of other things for example. And so like that,
1319
01:04:53,590 --> 01:04:56,725
it also has a special field name by the way, self, and
1320
01:04:56,727 --> 01:04:59,327
you know, cuz you should, could have a field name like
1321
01:04:59,329 --> 01:05:03,265
text if there's a tweet, text contains some word.
1322
01:05:03,267 --> 01:05:05,834
That's perfectly valid predicate. But you can also
1323
01:05:05,836 --> 01:05:09,604
have self as a keyword, not just tech, but self. And if I
1324
01:05:09,606 --> 01:05:13,541
say self contains whatever, it'll look in all the fields,
1325
01:05:13,543 --> 01:05:16,978
okay, that are index, not just text but all the tweets.
1326
01:05:16,980 --> 01:05:20,248
So maybe you have a tweet that has text, it has hashtags,
1327
01:05:20,250 --> 01:05:23,952
it has you know URLs as different fields. If you'd say
1328
01:05:23,954 --> 01:05:26,922
self-contained something, it'll look in all the fields,
1329
01:05:26,924 --> 01:05:28,523
all the indexed fields to find it, okay?
1330
01:05:28,525 --> 01:05:33,528
So it's kind of a search text over all the index field,
1331
01:05:33,530 --> 01:05:35,730
okay? So this is really, couldn't be simpler,
1332
01:05:35,732 --> 01:05:38,333
they couldn't have made this any simpler. This is as simple
1333
01:05:38,335 --> 01:05:41,136
a kind of queries you can possibly imagine. Okay, so
1334
01:05:41,138 --> 01:05:43,371
here's an example. Here I'm looking for
1335
01:05:43,373 --> 01:05:46,741
a tweet that's text contains some search string so
1336
01:05:46,743 --> 01:05:47,742
I've a predicate which is
1337
01:05:47,744 --> 01:05:51,479
text contains %@ which is the search string there.
1338
01:05:51,481 --> 01:05:54,549
Otherwise, it looks just like NSPredicate of core data.
1339
01:05:54,551 --> 01:05:57,018
Okay, I'm gonna create this query which is looking for
1340
01:05:57,020 --> 01:06:00,422
Tweets without predicate. I'm gonna perform the query
1341
01:06:00,424 --> 01:06:04,159
some time later. It's gonna call my closure right here.
1342
01:06:04,161 --> 01:06:07,629
It's gonna give me a list of all the Tweets, okay,
1343
01:06:07,631 --> 01:06:10,598
that contain that. That's what this records thing is right
1344
01:06:10,600 --> 01:06:13,969
here. Again, I'm checking for errors. Here, I've checked for
1345
01:06:13,971 --> 01:06:17,939
that not authenticated error again. But,
1346
01:06:17,941 --> 01:06:22,143
it's as simple as that, okay? Now,
1347
01:06:22,145 --> 01:06:25,146
one thing, if you're gonna search on any of these things,
1348
01:06:25,148 --> 01:06:28,116
any field that you're gonna search on, make sure you go to
1349
01:06:28,118 --> 01:06:30,986
your dashboard and make sure that the checkbox query for
1350
01:06:30,988 --> 01:06:34,856
indexes is set, otherwise you can't query on it. Okay,
1351
01:06:34,858 --> 01:06:38,159
now when you create new fields automatically on
1352
01:06:38,161 --> 01:06:40,128
the fly like this, it automatically sets those.
1353
01:06:40,130 --> 01:06:42,430
it automatically turns on indexes for all of them.
1354
01:06:42,432 --> 01:06:44,966
It doesn't turn on the indexes for created by and
1355
01:06:44,968 --> 01:06:47,736
created date and all that, but it does do it for
1356
01:06:47,738 --> 01:06:51,106
the new field that you add. And in fact,
1357
01:06:51,108 --> 01:06:52,440
when you go to deploy, you probably wanna turn
1358
01:06:52,442 --> 01:06:54,843
some of those off if you know you never search on it.
1359
01:06:54,845 --> 01:06:59,047
Don't waste the database's time creating indexes that
1360
01:06:59,049 --> 01:07:03,018
it doesn't use. Now, there's a special kind of record
1361
01:07:03,020 --> 01:07:06,354
though that you get with the special method here which
1362
01:07:06,356 --> 01:07:10,392
is the recordID of the user, okay, the currently logged in
1363
01:07:10,394 --> 01:07:12,460
iCloud user. Now why would you ever want this?
1364
01:07:12,462 --> 01:07:14,662
Well, because you might wanna look in the database and
1365
01:07:14,664 --> 01:07:17,632
find records created by that user. And to do that,
1366
01:07:17,634 --> 01:07:21,169
you need the user. You need the user's RecordID so
1367
01:07:21,171 --> 01:07:24,706
that you can make a query that says user, or
1368
01:07:24,708 --> 01:07:28,309
createdBy, let's say, which is a built in one. User, creator,
1369
01:07:28,311 --> 01:07:30,445
something. Actually I'll talk about in the next slide. You
1370
01:07:30,447 --> 01:07:32,881
want to be able to set that. Well, to do that, you need it.
1371
01:07:32,883 --> 01:07:35,216
And you do this with a fetchUserRecordIDWithCompleti-
1372
01:07:35,218 --> 01:07:38,520
onHandler. That is not sent to the database, though.
1373
01:07:38,522 --> 01:07:40,488
That is sent to the container. Okay,
1374
01:07:40,490 --> 01:07:43,691
because the container, all the databases in the container,
1375
01:07:43,693 --> 01:07:46,528
the, I currently logged in iCloud user, is the user for
1376
01:07:46,530 --> 01:07:50,498
them. So you actually go up to CKContainer to do this,
1377
01:07:50,500 --> 01:07:51,699
CKContainer default container,
1378
01:07:51,701 --> 01:07:54,736
fetch record user record with completion handler, and
1379
01:07:54,738 --> 01:07:57,372
it just returns you the RecordID of the user.
1380
01:07:57,374 --> 01:08:00,842
Now, notice this is the RecordID, not the CKRecord,
1381
01:08:00,844 --> 01:08:03,912
you almost never need the actual record of the user.
1382
01:08:03,914 --> 01:08:05,380
You could get the record of the user, and
1383
01:08:05,382 --> 01:08:07,348
you could even add fields to it if you wanted,
1384
01:08:07,350 --> 01:08:10,485
but it's rare to do that. Usually, you just need
1385
01:08:10,487 --> 01:08:13,621
the RecordID because you're going to be querying on it,
1386
01:08:13,623 --> 01:08:17,125
you're gonna be making queries to find out if this user was
1387
01:08:17,127 --> 01:08:19,828
the person who created it or whatever, okay?
1388
01:08:19,830 --> 01:08:22,797
The RecordID has a property record name,
1389
01:08:22,799 --> 01:08:26,935
which is just some randomly created string that you
1390
01:08:26,937 --> 01:08:29,270
can use as kind of a blind username.
1391
01:08:29,272 --> 01:08:31,973
It's not gonna be the person's actual iCloud name, it's just
1392
01:08:31,975 --> 01:08:35,877
some gobbledygook. But inside of your implementation,
1393
01:08:35,879 --> 01:08:40,048
you can use it as a string key that means this user if you
1394
01:08:40,050 --> 01:08:43,818
want, okay? I'm gonna combine the two things,
1395
01:08:43,820 --> 01:08:48,356
the fetchUserRecord with the query here. I'm gonna find all
1396
01:08:48,358 --> 01:08:51,493
the tweets that were created by the currently logged in
1397
01:08:51,495 --> 01:08:55,630
user. Okay, so first I get the container,
1398
01:08:55,632 --> 01:08:58,800
I fetch the user in the completion handler for
1399
01:08:58,802 --> 01:09:01,903
that right here. I have the user. Now I'm gonna create
1400
01:09:01,905 --> 01:09:05,540
a new predicate which is the creatorUserRecordID.
1401
01:09:05,542 --> 01:09:09,711
Which is a built in field on all record types.
1402
01:09:09,713 --> 01:09:14,616
Equals the UserRecordID, a thing I got from fetchRecord.
1403
01:09:14,618 --> 01:09:15,416
See that thing right there.
1404
01:09:15,418 --> 01:09:17,619
And them I'm gonna create a query which is for
1405
01:09:17,621 --> 01:09:20,155
tweets that uses that predicate. Now I'm gonna
1406
01:09:20,157 --> 01:09:22,991
get the public database and perform the query, okay?
1407
01:09:22,993 --> 01:09:26,761
In zone nil, and I'm gonna get the record in error as
1408
01:09:26,763 --> 01:09:29,764
a return value. And so now I have this records containing
1409
01:09:29,766 --> 01:09:32,767
all the tweets created by the currently logged in user.
1410
01:09:32,769 --> 01:09:34,602
See how I chained those two? And
1411
01:09:34,604 --> 01:09:37,872
remember, this fetch record, UserRecordID,
1412
01:09:37,874 --> 01:09:38,606
that's gonna take a little while.
1413
01:09:38,608 --> 01:09:41,109
And when it returns, it's gonna do another query.
1414
01:09:41,111 --> 01:09:44,045
That's gonna take a little while, so it's gonna chain.
1415
01:09:44,047 --> 01:09:47,549
But since this call is inside of this other closure,
1416
01:09:47,551 --> 01:09:52,253
it chains them nicely there. Okay, again
1417
01:09:52,255 --> 01:09:55,423
you have to create, turn that Created By metadata index on
1418
01:09:55,425 --> 01:09:57,792
that I showed you right at the beginning for this to work or
1419
01:09:57,794 --> 01:10:03,198
you can't search on user. Okay, by the way,
1420
01:10:03,200 --> 01:10:06,901
if you ever have a RecordID and you wanna get the record,
1421
01:10:06,903 --> 01:10:09,637
okay, the CKRecord, you can use databases
1422
01:10:09,639 --> 01:10:12,240
fetchRecordWithID and that will search and
1423
01:10:12,242 --> 01:10:16,311
get the actual record, okay? The RecordID, though,
1424
01:10:16,313 --> 01:10:18,846
on its own has some interesting things
1425
01:10:18,848 --> 01:10:22,850
like the creator, the creation date, the record type,
1426
01:10:22,852 --> 01:10:26,321
and all that stuff, so that's kind of fun. All right,
1427
01:10:26,323 --> 01:10:30,158
deleting records from the database couldn't be easier.
1428
01:10:30,160 --> 01:10:30,658
Just say to the database,
1429
01:10:30,660 --> 01:10:33,361
deleteRecordWithID. You have to have the record ID, you can
1430
01:10:33,363 --> 01:10:35,230
of course get the record ID from a record easily,
1431
01:10:35,232 --> 01:10:38,066
there's a bar for it. And then completionHandler when it's
1432
01:10:38,068 --> 01:10:42,237
done, okay? Okay, now let's talk about references, okay?
1433
01:10:42,239 --> 01:10:44,038
I wanna, I want to store a reference. So this is
1434
01:10:44,040 --> 01:10:46,908
like the Twitter user as the tweeter for a tweet.
1435
01:10:46,910 --> 01:10:50,278
You cannot do this code right here. twitterUser equals
1436
01:10:50,280 --> 01:10:54,549
a recordType TwitterUser. Tweet equals a record Tweet.
1437
01:10:54,551 --> 01:10:58,052
Tweet, subTweeter equals TwitterUser. [SOUND] Okay,
1438
01:10:58,054 --> 01:11:02,490
can't do it. This thing has to be a CKReference.
1439
01:11:02,492 --> 01:11:04,993
So you just have to say TweetSubTweeter equals
1440
01:11:04,995 --> 01:11:07,996
CKReference with the record. And then you specify this
1441
01:11:07,998 --> 01:11:10,431
action which is interesting, it's either deletes itself or
1442
01:11:10,433 --> 01:11:16,504
not. This action says what to do if this thing is deleted.
1443
01:11:16,506 --> 01:11:20,308
Okay, when we cascade. So if I say the action is DeleteSelf,
1444
01:11:20,310 --> 01:11:23,811
this reference to twitterUser is DeleteSelf reference.
1445
01:11:23,813 --> 01:11:26,948
Then if the twitterUser is deleted, then the Tweet is
1446
01:11:26,950 --> 01:11:29,984
deleted as well. It cascades the delete. That's why it's
1447
01:11:29,986 --> 01:11:35,089
saying delete myself, okay, if this reference gets deleted.
1448
01:11:35,091 --> 01:11:37,525
None means don't do it. When you delete,
1449
01:11:37,527 --> 01:11:41,829
don't do any cascading, just delete this one thing, okay?
1450
01:11:41,831 --> 01:11:44,399
By the way, when you're creating a predicate,
1451
01:11:44,401 --> 01:11:45,900
you don't have to do that CKReference thing.
1452
01:11:45,902 --> 01:11:47,969
If I wanna create a predicate which is tweeter equals
1453
01:11:47,971 --> 01:11:50,938
something, I can actually pass the twitterUser, I don't have
1454
01:11:50,940 --> 01:11:56,978
to say CKReference twitterUser there. Okay, standing queries,
1455
01:11:56,980 --> 01:12:00,315
the last thing we're talking about, subscriptions, okay? So
1456
01:12:00,317 --> 01:12:02,950
sometimes it's nice to not have to be constantly
1457
01:12:02,952 --> 01:12:06,454
querying, you just want iCloud to tell you when a new Thing
1458
01:12:06,456 --> 01:12:09,657
appears in the database that you're interested in.
1459
01:12:09,659 --> 01:12:12,794
And that's really, really easy to do. So for
1460
01:12:12,796 --> 01:12:15,596
an example here, I'm going to set up a subscription so
1461
01:12:15,598 --> 01:12:18,866
that I get notified by a push notification any time that
1462
01:12:18,868 --> 01:12:19,600
a Tweet is created or
1463
01:12:19,602 --> 01:12:22,737
deleted. So I'm gonna create a predicate which is
1464
01:12:22,739 --> 01:12:25,373
true predicate which means all Tweets. Okay,
1465
01:12:25,375 --> 01:12:28,009
this is a special predicate, that means all.
1466
01:12:28,011 --> 01:12:31,145
And in core dating, you have the predicate nil, means all.
1467
01:12:31,147 --> 01:12:32,814
Here you have to create a predicate,
1468
01:12:32,816 --> 01:12:33,147
it's true predicate.
1469
01:12:33,149 --> 01:12:36,084
And then you create this thing called a CKSubscription
1470
01:12:36,086 --> 01:12:39,120
with the record type that you're interested in, Tweets,
1471
01:12:39,122 --> 01:12:42,657
the predicate you want, all of the them. A subscriptionID,
1472
01:12:42,659 --> 01:12:45,893
this is an English language string that should
1473
01:12:45,895 --> 01:12:48,996
very uniquely describe what you're looking for. Okay,
1474
01:12:48,998 --> 01:12:51,833
because this is gonna identify this subscription in
1475
01:12:51,835 --> 01:12:54,335
the database so you can delete it later for an example.
1476
01:12:54,337 --> 01:12:57,939
Okay so here I called it "All Tweet Creation and Deletion".
1477
01:12:57,941 --> 01:13:02,510
And then options is what kinds of actions of those tweets,
1478
01:13:02,512 --> 01:13:05,446
for example, do you wanna know when the tweets are created?
1479
01:13:05,448 --> 01:13:07,115
Do you wanna know when tweets are deleted?
1480
01:13:07,117 --> 01:13:08,883
You can also find out when tweets are updated,
1481
01:13:08,885 --> 01:13:12,019
their fields change. And you can also have FiresOnce
1482
01:13:12,021 --> 01:13:15,289
in here which means that this subscription only works once.
1483
01:13:15,291 --> 01:13:16,391
It sends you one push notification and
1484
01:13:16,393 --> 01:13:20,128
then gets deleted. Okay, so it's like a one time standing
1485
01:13:20,130 --> 01:13:23,564
query. It also has this thing called a notifcationInfo.
1486
01:13:23,566 --> 01:13:25,466
Okay, this is gonna send a push notification.
1487
01:13:25,468 --> 01:13:28,536
So this can help you configure that push notification right
1488
01:13:28,538 --> 01:13:32,039
here. And then you just say, database.saveSubscription.
1489
01:13:32,041 --> 01:13:34,208
Okay, and it'll call you back when it's done.
1490
01:13:34,210 --> 01:13:36,110
And that puts this subscription in the database.
1491
01:13:36,112 --> 01:13:39,580
And now the database is gonna send you a push notification
1492
01:13:39,582 --> 01:13:42,884
whenever this is changing. Okay, creation or deletion of
1493
01:13:42,886 --> 01:13:46,254
tweets, in this case. Okay, now I have to tell you how to
1494
01:13:46,256 --> 01:13:49,624
deal with push notifications, okay. How to turn them on,
1495
01:13:49,626 --> 01:13:53,761
and how to get them. So, to turn on a push notification,
1496
01:13:53,763 --> 01:13:56,030
you actually need to in your application,
1497
01:13:56,032 --> 01:13:59,333
didFinishLaunchingWithOptions is usually where you would put
1498
01:13:59,335 --> 01:14:02,003
this code, you have to put these yellow lines here.
1499
01:14:02,005 --> 01:14:04,939
Which is, you have to call this method
1500
01:14:04,941 --> 01:14:06,908
registerUserNotificationSetti- ngs.
1501
01:14:06,910 --> 01:14:09,844
Okay, the user notification settings just say what kind
1502
01:14:09,846 --> 01:14:13,714
of push notifications you're willing to receive like,
1503
01:14:13,716 --> 01:14:16,150
alerts, badges, or sounds.
1504
01:14:16,152 --> 01:14:19,220
If you're just alerting to let the code know, you don't have
1505
01:14:19,222 --> 01:14:21,989
to send off any of those three, okay? Alert badge and
1506
01:14:21,991 --> 01:14:24,425
sound means it's gonna put an alert up when things come,
1507
01:14:24,427 --> 01:14:27,562
okay, or a badge. It's gonna put a little red badge
1508
01:14:27,564 --> 01:14:30,331
on your icon, your app icon which is kinda cool maybe,
1509
01:14:30,333 --> 01:14:32,033
you might want that actually.
1510
01:14:32,035 --> 01:14:36,270
You get to specify the, what happens when the push
1511
01:14:36,272 --> 01:14:39,073
notification comes from Cloud Kit back in that notification
1512
01:14:39,075 --> 01:14:41,776
info thing that I was telling you about in the previous
1513
01:14:41,778 --> 01:14:43,644
slide. Subscription.notificationInfo,
1514
01:14:43,646 --> 01:14:46,981
one of the things in there is whether to alert badge or
1515
01:14:46,983 --> 01:14:51,052
sound when the subscription fires. Okay. So
1516
01:14:51,054 --> 01:14:54,622
anyway you register what your kind of settings you want, and
1517
01:14:54,624 --> 01:14:56,791
then you have to register for remote notifications.
1518
01:14:56,793 --> 01:14:59,327
That means I wanna hear about remote notifications.
1519
01:14:59,329 --> 01:15:00,728
If you don't do this, you won't get any push
1520
01:15:00,730 --> 01:15:04,298
notifications of any kind, let alone these Cloud Kit ones.
1521
01:15:04,300 --> 01:15:06,634
Okay. So now once you've done that okay,
1522
01:15:06,636 --> 01:15:08,936
you need to receive them. And you do that with this
1523
01:15:08,938 --> 01:15:12,673
application delegate method didReceiveRemoteNotification.
1524
01:15:12,675 --> 01:15:15,376
So this will get called any time a push notification comes
1525
01:15:15,378 --> 01:15:19,380
to your app, okay. When you get it there's user info
1526
01:15:19,382 --> 01:15:22,950
right here, okay. And inside that userInfo is a dictionary
1527
01:15:22,952 --> 01:15:26,587
that Cloud Kit's gonna send. And you're gonna use this
1528
01:15:26,589 --> 01:15:30,658
CKNotification class, right here, call it initializer,
1529
01:15:30,660 --> 01:15:34,195
with that dictionary to create a CKNotification.
1530
01:15:34,197 --> 01:15:37,532
That CKNotification, Cloud Kit notification, represents
1531
01:15:37,534 --> 01:15:40,001
the push notification that was sent to you. It's got all the
1532
01:15:40,003 --> 01:15:43,104
information you need to know about the push notification
1533
01:15:43,106 --> 01:15:46,574
that got sent because you're subscription. Okay? And
1534
01:15:46,576 --> 01:15:49,176
it's got things in there like the recordID that change
1535
01:15:49,178 --> 01:15:53,748
that cause it. Why the notification is happening s
1536
01:15:53,750 --> 01:15:54,782
it because something was created,
1537
01:15:54,784 --> 01:15:56,717
or something was deleted, or something was updated?
1538
01:15:56,719 --> 01:15:59,687
Okay, will tell you which of those things went on that
1539
01:15:59,689 --> 01:16:02,790
caused this notification to be sent to you and
1540
01:16:02,792 --> 01:16:04,258
which record was affected by it.
1541
01:16:04,260 --> 01:16:07,094
You can even, in the subscription notificationInfo,
1542
01:16:07,096 --> 01:16:10,031
you can set certain fields to be prefetched and
1543
01:16:10,033 --> 01:16:12,733
come along with. Okay, cuz normally it only sends you
1544
01:16:12,735 --> 01:16:15,269
the recordID of what changed in the database.
1545
01:16:15,271 --> 01:16:16,938
You might wanna actually sum of the fields, so
1546
01:16:16,940 --> 01:16:20,675
you don't have to go get, fetch the record itself, okay?
1547
01:16:20,677 --> 01:16:23,477
So that's great. Now that you've got this notification,
1548
01:16:23,479 --> 01:16:27,181
what do you do with it. Okay, you really need to get it
1549
01:16:27,183 --> 01:16:29,750
to the part of your UI, if you control it or
1550
01:16:29,752 --> 01:16:33,287
whatever, that's going to do something with this data
1551
01:16:33,289 --> 01:16:35,756
that has changed, okay. And the best way,
1552
01:16:35,758 --> 01:16:37,425
this is all coming to your AppDelegate, right?
1553
01:16:37,427 --> 01:16:39,694
Is your AppDelegate getting this push notification?
1554
01:16:39,696 --> 01:16:41,829
That's not that useful because usually it's some view
1555
01:16:41,831 --> 01:16:44,265
controller somewhere that really wants the information.
1556
01:16:44,267 --> 01:16:46,734
So what we're gonna do is we're gonna forward it
1557
01:16:46,736 --> 01:16:49,770
using the radio station model, okay?
1558
01:16:49,772 --> 01:16:52,106
That's is real simple way to get this thing to came to
1559
01:16:52,108 --> 01:16:54,809
AppDelegate and get it out to who really wants it. So
1560
01:16:54,811 --> 01:16:57,078
I'm just gonna create a local notification here I'm gonna
1561
01:16:57,080 --> 01:16:59,880
become a radio station broadcaster see in this
1562
01:16:59,882 --> 01:17:00,047
notification.
1563
01:17:00,049 --> 01:17:03,417
Here's my radio station name MyCloudKitNotificationName or
1564
01:17:03,419 --> 01:17:07,388
whatever. Self is this is in AppDelegate so self is gonna
1565
01:17:07,390 --> 01:17:09,790
be my AppDelegate but it probably doesn't matter what I
1566
01:17:09,792 --> 01:17:12,393
pass here as the sender of the radio station. And
1567
01:17:12,395 --> 01:17:15,863
then here in the userInfo, I'm gonna put that ckn,
1568
01:17:15,865 --> 01:17:18,666
this notification right here that I created, okay,
1569
01:17:18,668 --> 01:17:21,335
from the dictionary that came in the push notification.
1570
01:17:21,337 --> 01:17:23,704
I'm gonna put that in the dictionary
1571
01:17:23,706 --> 01:17:25,873
under some key name, ckn or
1572
01:17:25,875 --> 01:17:30,011
whatever, CKNKey, so that the people who are listening to my
1573
01:17:30,013 --> 01:17:33,080
radio station can get this CKNotification. By the way,
1574
01:17:33,082 --> 01:17:36,517
these two strings wanna be global constant somewhere.
1575
01:17:36,519 --> 01:17:40,254
So that the listeners and the sender are using the same keys
1576
01:17:40,256 --> 01:17:46,394
and radio station names, okay? And then I just post it, okay.
1577
01:17:46,396 --> 01:17:48,329
DefaultCenter postNotification disNotification.
1578
01:17:48,331 --> 01:17:51,098
And now everybody's listening to this radio station gonna
1579
01:17:51,100 --> 01:17:52,967
find out about all the push notifications coming
1580
01:17:52,969 --> 01:17:56,737
from Cloud Kit. Cause they're getting forwarded out to them.
1581
01:17:56,739 --> 01:18:00,708
Okay, now, if I'm an observer, what do I do? I just
1582
01:18:00,710 --> 01:18:04,111
do this addObserverForName, okay. So now I'm some view
1583
01:18:04,113 --> 01:18:06,380
controller that wants to hear this push notification, so
1584
01:18:06,382 --> 01:18:09,784
I do addObserveForName, I use the radio station name there,
1585
01:18:09,786 --> 01:18:12,319
I don't care who sends it to me, I'm going to do it on
1586
01:18:12,321 --> 01:18:14,889
the same queue because I know my AppDelegate runs on
1587
01:18:14,891 --> 01:18:18,926
the main queue, but I could put main queue here also. And
1588
01:18:18,928 --> 01:18:20,027
then using block right here.
1589
01:18:20,029 --> 01:18:20,995
This is the block that gets called.
1590
01:18:20,997 --> 01:18:24,432
Here's the notification that came over the radio station.
1591
01:18:24,434 --> 01:18:26,467
I'm gonna look in the notification look at that
1592
01:18:26,469 --> 01:18:30,905
user interface, user info, get that key and cast it
1593
01:18:30,907 --> 01:18:34,608
to be a CKQueryNotification by the way. There's another kind
1594
01:18:34,610 --> 01:18:39,113
of thing it could be which is a zone oriented notification,
1595
01:18:39,115 --> 01:18:41,115
I'm not talking about zones but most of the times it's
1596
01:18:41,117 --> 01:18:43,651
gonna be a query notification cuz it's a subscription.
1597
01:18:43,653 --> 01:18:45,886
So you can do this as query notification.
1598
01:18:45,888 --> 01:18:50,591
Now you're gonna check to see if the subscriptionID of this
1599
01:18:50,593 --> 01:18:54,862
query notification is the same as your subscriptionID because
1600
01:18:54,864 --> 01:18:56,230
there might be other view controllers
1601
01:18:56,232 --> 01:18:57,698
who have subscriptions in Cloud Kit and
1602
01:18:57,700 --> 01:18:59,700
they're getting notified by push notifications.
1603
01:18:59,702 --> 01:19:02,002
You don't want theirs, you want yours. And
1604
01:19:02,004 --> 01:19:04,839
you can tell it's yours because you can look in this
1605
01:19:04,841 --> 01:19:08,275
CKQueryNotification and find the subscriptionID that caused
1606
01:19:08,277 --> 01:19:12,046
this push notification to get pushed, got it? And
1607
01:19:12,048 --> 01:19:15,249
once I'm sure it's for me now I can go react to it.
1608
01:19:15,251 --> 01:19:15,416
The data's available.
1609
01:19:15,418 --> 01:19:18,519
Maybe I'll put it in my table view or whatever I do with
1610
01:19:18,521 --> 01:19:20,921
it cuz the data is here, okay? Maybe I'll query for
1611
01:19:20,923 --> 01:19:24,959
more information, whatever. But now I can get going.
1612
01:19:24,961 --> 01:19:28,262
Make sense? By the way, notice that when did out observe for
1613
01:19:28,264 --> 01:19:31,198
name I grabbed the return value that cookie I told you
1614
01:19:31,200 --> 01:19:34,468
and in this object protocol thing we did and why do I want
1615
01:19:34,470 --> 01:19:37,271
that, because later on when I'm done listening
1616
01:19:37,273 --> 01:19:40,641
to further push notifications via this radio station.
1617
01:19:40,643 --> 01:19:44,278
I'm gonna call removeObserver to remove this observer so
1618
01:19:44,280 --> 01:19:46,947
I no longer get these local notifications.
1619
01:19:46,949 --> 01:19:51,051
So this is something might do in our view did disappear.
1620
01:19:51,053 --> 01:19:54,922
And we might do this in our view will appear or view did
1621
01:19:54,924 --> 01:19:58,192
appear. Right, cuz only when we're on screen do we maybe
1622
01:19:58,194 --> 01:20:00,528
want these notifications. When we go off we don't
1623
01:20:00,530 --> 01:20:01,796
want them any more. When we come back on we wanna
1624
01:20:01,798 --> 01:20:06,500
keep getting them again. Okay? You definitely wanna do this,
1625
01:20:06,502 --> 01:20:11,005
by the way you never want your view controller to go away and
1626
01:20:11,007 --> 01:20:16,143
still be here because this closure will keep your
1627
01:20:16,145 --> 01:20:18,746
view controller in memory. And you don't wanna really do
1628
01:20:18,748 --> 01:20:21,282
weakSelf and all that because you actually wanna explicitly
1629
01:20:21,284 --> 01:20:23,350
remove it otherwise the NotificationCenter's gonna
1630
01:20:23,352 --> 01:20:27,555
always be holding onto you, okay?
1631
01:20:27,557 --> 01:20:31,158
So, make sure you do this, somewhere, removeObserver
1632
01:20:31,160 --> 01:20:33,828
somewhere. Okay, that's it for Cloud Kit. Now you know
1633
01:20:33,830 --> 01:20:36,096
everything about how to create records, query for
1634
01:20:36,098 --> 01:20:38,399
them, and even sign up to get notified.
1635
01:20:38,401 --> 01:20:40,434
I'm gonna do a big demo on this on Wednesday, so you can
1636
01:20:40,436 --> 01:20:45,172
see it all in action. Friday, No Section again. Next week,
1637
01:20:45,174 --> 01:20:48,876
still kinda deciding how or what works to do things.
1638
01:20:48,878 --> 01:20:49,743
I really wanna make to sure you get
1639
01:20:49,745 --> 01:20:54,215
this Segues covered right here but maps are also really cool.
1640
01:20:54,217 --> 01:20:56,450
I won't get to all of these next week but I'll do as much
1641
01:20:56,452 --> 01:20:59,353
as I can. Okay, if you have by the way something you really
1642
01:20:59,355 --> 01:21:01,355
like to see me do because you find a project wants to
1643
01:21:01,357 --> 01:21:06,794
do it. Like Cloud Kit was, let me know. Okay, that's it.
1644
01:21:06,796 --> 01:21:08,696
See you next time. >> For
1645
01:21:08,698 --> 01:21:08,729
more please visit us at stanford.edu.
================================================
FILE: subtitles/16. Notifications and CloudKit.srt
================================================
1
00:00:00,001 --> 00:00:03,702
[MUSIC]
2
00:00:03,704 --> 00:00:08,340
Stanford University. >> All right, well,
3
00:00:08,342 --> 00:00:12,711
welcome to C, Stanford CS193P spring of 2016
4
00:00:12,713 --> 00:00:15,881
lecture number 16. And today it's all demo.
5
00:00:15,883 --> 00:00:18,817
I'm gonna do a big demo on CloudKit to show you how,
6
00:00:18,819 --> 00:00:21,286
how that works. I'm also gonna take a brief little
7
00:00:21,288 --> 00:00:24,023
thing at the beginning to show you about notifications, that
8
00:00:24,025 --> 00:00:27,092
little radio station thing. We talked about how you can find
9
00:00:27,094 --> 00:00:28,427
out about size changes happening and
10
00:00:28,429 --> 00:00:31,630
I'm gonna show you how that works. And that's pretty much
11
00:00:31,632 --> 00:00:34,366
it for today. It's kinda all demo today.
12
00:00:34,368 --> 00:00:37,870
Coming up Friday we're not doing sections anymore.
13
00:00:37,872 --> 00:00:40,606
People weren't that interested in sections this quarter,
14
00:00:40,608 --> 00:00:42,074
that's fine. And then next week,
15
00:00:42,076 --> 00:00:44,977
I'm gonna be covering maps some more segues, and
16
00:00:44,979 --> 00:00:48,680
then just some miscellaneous topics, not quite sure yet
17
00:00:48,682 --> 00:00:52,317
what I'm gonna do there. All right, so,
18
00:00:52,319 --> 00:00:57,356
let's dive right into this demo. Going to launch Xcode
19
00:00:57,358 --> 00:01:02,127
here. We're going to make a new project.
20
00:01:02,129 --> 00:01:04,630
It's gonna be our standard stuff here. So
21
00:01:04,632 --> 00:01:08,667
what I'm gonna do is an app that is a question asking app.
22
00:01:08,669 --> 00:01:12,604
So it lets you author questions with answers, and
23
00:01:12,606 --> 00:01:14,807
then they're out on the cloud, in iCloud,
24
00:01:14,809 --> 00:01:16,108
and people can answer the questions,
25
00:01:16,110 --> 00:01:18,243
okay, and then it keeps track of how many answers people had
26
00:01:18,245 --> 00:01:20,946
and all that stuff. So today we're just gonna kinda do
27
00:01:20,948 --> 00:01:23,082
the start of it, which is, we'll have some UI to make
28
00:01:23,084 --> 00:01:25,584
the question and we'll upload the questions to the cloud,
29
00:01:25,586 --> 00:01:28,020
and then we'll have another view controller which shows
30
00:01:28,022 --> 00:01:30,122
a list of all the questions that you can choose on.
31
00:01:30,124 --> 00:01:33,325
And you can click, some UI to make a new question or
32
00:01:33,327 --> 00:01:35,961
delete old questions. So we're gonna be able to do all
33
00:01:35,963 --> 00:01:37,262
kinds of iCloud stuff there. Deleting,
34
00:01:37,264 --> 00:01:39,998
we're gonna do subscriptions with push notifications,
35
00:01:40,000 --> 00:01:45,070
all that stuff. Okay, so we're gonna call this app Pollster.
36
00:01:45,072 --> 00:01:48,640
I'm also gonna change my unique thing here just to be
37
00:01:48,642 --> 00:01:52,945
something different, how about teacher instead of instructor.
38
00:01:52,947 --> 00:01:55,147
So Pollster, because you could use it to poll,
39
00:01:55,149 --> 00:01:57,783
ask people questions, and get polls. No Core Data,
40
00:01:57,785 --> 00:02:00,819
it's gonna be an iPhone-only application. We're just gonna
41
00:02:00,821 --> 00:02:04,189
have a couple table views is all. And we'll put it in our
42
00:02:04,191 --> 00:02:09,128
standard location here. So here we are in Pollster, and
43
00:02:09,130 --> 00:02:14,433
I'm going to kind of clear out my storyboard to start. So
44
00:02:14,435 --> 00:02:18,637
let's take this little view controller that it
45
00:02:18,639 --> 00:02:22,941
comes with, let's delete its code right here.
46
00:02:22,943 --> 00:02:26,445
So I'll do Delete. Move that to the trash.
47
00:02:26,447 --> 00:02:29,948
Let's go ahead and put these little guys into a little
48
00:02:29,950 --> 00:02:33,852
supporting files thing again, Supporting Files. And notice
49
00:02:33,854 --> 00:02:36,054
I'm gonna leave AppDelegate out of the Supporting Files,
50
00:02:36,056 --> 00:02:38,190
because of course, we're gonna do push notifications.
51
00:02:38,192 --> 00:02:40,893
That involves our AppDelegate, so it's not really
52
00:02:40,895 --> 00:02:43,862
a supporting file, it's kind of a main thing this time.
53
00:02:43,864 --> 00:02:47,833
And back in the storyboard we can just get rid of this view
54
00:02:47,835 --> 00:02:51,069
controller, we don't need it at all, okay.
55
00:02:51,071 --> 00:02:52,905
Now, I also have some other files
56
00:02:52,907 --> 00:02:55,307
here that are gonna be supporting files for
57
00:02:55,309 --> 00:02:58,410
this demo, so I'm gonna drag those in right here.
58
00:02:58,412 --> 00:03:00,979
In fact, I'm gonna put them in Supporting Files.
59
00:03:00,981 --> 00:03:05,317
Copy them in, let's take a quick look at those.
60
00:03:05,319 --> 00:03:08,687
All right, so one is, I just wanted all my structs, with,
61
00:03:08,689 --> 00:03:11,456
you know, my constants in there to be pre-typed in so
62
00:03:11,458 --> 00:03:14,593
I don't have to type them in all the time. And basically,
63
00:03:14,595 --> 00:03:18,397
these constants are gonna be for the push or actually for
64
00:03:18,399 --> 00:03:21,300
the local notification, the radio station stuff,
65
00:03:21,302 --> 00:03:24,436
this is gonna be the name of our radio station and
66
00:03:24,438 --> 00:03:25,537
the key in the dictionary.
67
00:03:25,539 --> 00:03:27,406
And then these are the names of the entities and
68
00:03:27,408 --> 00:03:29,675
attributes that I'm gonna create in CloudKit, okay?
69
00:03:29,677 --> 00:03:32,311
So we're doing QandA, here are questions and answers, and
70
00:03:32,313 --> 00:03:35,547
here's a Response to a QandA, and then we've got questions,
71
00:03:35,549 --> 00:03:36,982
answers, we got a chosenAnswer, and
72
00:03:36,984 --> 00:03:40,886
then we've got a reference qanda. So we got all that.
73
00:03:40,888 --> 00:03:43,388
And then I added this little wasCreatedByThisUser.
74
00:03:43,390 --> 00:03:46,225
It's a bool on a CKRecord, a CloudKit record, that just
75
00:03:46,227 --> 00:03:49,027
says whether this record was created by the currently
76
00:03:49,029 --> 00:03:52,097
logged in iCloud user, okay. So that's that.
77
00:03:52,099 --> 00:03:55,300
Then, over here, I have this nice class here,
78
00:03:55,302 --> 00:03:57,236
QandATableViewController.
79
00:03:57,238 --> 00:04:00,205
It is, has nothing to do with iCloud. It's just a table view
80
00:04:00,207 --> 00:04:03,775
controller that shows a question, okay, a question and
81
00:04:03,777 --> 00:04:06,778
as many answers as you want. Okay, notice that it inherits
82
00:04:06,780 --> 00:04:09,281
from TextTableViewController. That's a generic
83
00:04:09,283 --> 00:04:12,284
view controller that just shows a table view where all
84
00:04:12,286 --> 00:04:14,753
the rows are editable text. Okay, that's all that is.
85
00:04:14,755 --> 00:04:17,055
It doesn't know anything about questions and answers,
86
00:04:17,057 --> 00:04:17,856
it's just rows of editable text,
87
00:04:17,858 --> 00:04:21,226
nothing more. We're not even gonna be accessing this API,
88
00:04:21,228 --> 00:04:24,129
we're gonna be focusing totally at this MVC's level.
89
00:04:24,131 --> 00:04:27,432
And here's the public API for that, super simple. Okay,
90
00:04:27,434 --> 00:04:30,736
it's got this var qanda, which is basically its model,
91
00:04:30,738 --> 00:04:33,305
which is of type QandA, that's this struct, and
92
00:04:33,307 --> 00:04:35,874
it's a question, which is a String, and answers,
93
00:04:35,876 --> 00:04:39,011
which are Strings. Okay, so it couldn't be any simpler,
94
00:04:39,013 --> 00:04:41,313
it's the simplest possible data structure for
95
00:04:41,315 --> 00:04:43,982
a Q&A. It's got, these other vars here is just whether
96
00:04:43,984 --> 00:04:46,685
you're asking the question or answering the question.
97
00:04:46,687 --> 00:04:47,886
That's cuz if you're asking the question,
98
00:04:47,888 --> 00:04:50,289
you can edit the question, right? You're asking it, so
99
00:04:50,291 --> 00:04:52,291
you can add more answers and stuff like that.
100
00:04:52,293 --> 00:04:53,525
Whereas, if you're answering the question,
101
00:04:53,527 --> 00:04:55,627
you can only choose an answer, okay? And
102
00:04:55,629 --> 00:04:57,829
then if you do choose an answer, here's the answer.
103
00:04:57,831 --> 00:05:01,700
So you can just get the answer as a string, okay? It might be
104
00:05:01,702 --> 00:05:03,568
a nil, because maybe they haven't answered yet.
105
00:05:03,570 --> 00:05:06,405
So that's it, that's the entire public API of this, and
106
00:05:06,407 --> 00:05:09,007
this is the only API we're gonna use in our cloud one.
107
00:05:09,009 --> 00:05:12,210
And our cloud one is just gonna be a subclass of this,
108
00:05:12,212 --> 00:05:13,979
because it's gonna be a QandA, but
109
00:05:13,981 --> 00:05:16,548
it's a QandA that's stored in the cloud, okay.
110
00:05:16,550 --> 00:05:19,851
So let's go ahead and create that subclass of this guy, so
111
00:05:19,853 --> 00:05:23,255
I'm just gonna do File > New, okay, > Cocoa Touch Class.
112
00:05:23,257 --> 00:05:28,827
It's gonna be a subclass of our QandATableViewController,
113
00:05:28,829 --> 00:05:33,398
okay. And we're gonna call it CloudQand, whoops,
114
00:05:34,601 --> 00:05:36,068
QandATableViewController, okay?
115
00:05:36,070 --> 00:05:39,004
Cuz it's basically just a QandATableViewController that
116
00:05:39,006 --> 00:05:42,040
stores its question and answer on the cloud. All right, so
117
00:05:42,042 --> 00:05:45,644
here it is. Let's go ahead and blast all this, just so
118
00:05:45,646 --> 00:05:49,147
you can clearly see what new stuff we're adding here. So
119
00:05:49,149 --> 00:05:52,351
there's our super class being a QandATableViewController.
120
00:05:52,353 --> 00:05:54,753
Let's, of course, go to our storyboard and
121
00:05:54,755 --> 00:05:56,722
drag out a Table View Controller, okay.
122
00:05:56,724 --> 00:05:59,958
And we're gonna set its identity to be one of these
123
00:05:59,960 --> 00:06:02,160
CloudQandATableViewControll- ers.
124
00:06:02,162 --> 00:06:05,197
What's interesting about the QandATableViewController is,
125
00:06:05,199 --> 00:06:07,566
you don't need any prototype cells at all.
126
00:06:07,568 --> 00:06:09,601
It creates its prototype cells in code.
127
00:06:09,603 --> 00:06:11,503
And so, if you're interested in how to do that for your
128
00:06:11,505 --> 00:06:13,705
final project or whatever, you could take a look at that
129
00:06:13,707 --> 00:06:15,207
QandATableViewController, actually,
130
00:06:15,209 --> 00:06:17,142
you would wanna look at the TextTableViewController,
131
00:06:17,144 --> 00:06:20,712
the superclass one for how you might have a table view that
132
00:06:20,714 --> 00:06:23,348
creates its cells that way instead of using prototypes.
133
00:06:23,350 --> 00:06:24,983
And you can see why it don't use prototypes,
134
00:06:24,985 --> 00:06:26,518
because it's such a simple cell,
135
00:06:26,520 --> 00:06:28,120
its just has a text view in there, that's it.
136
00:06:28,122 --> 00:06:30,555
The entire cell is just a text view, nothing more.
137
00:06:30,557 --> 00:06:32,290
So it didn't really need a prototype to do that,
138
00:06:32,292 --> 00:06:33,558
and it makes it easier for subclasser too,
139
00:06:33,560 --> 00:06:37,028
they don't have to worry about that. All right, so
140
00:06:37,030 --> 00:06:37,295
we've got our thing here.
141
00:06:37,297 --> 00:06:40,365
Let's also make sure that it's the Initial View Controller,
142
00:06:40,367 --> 00:06:43,402
right here, where we have the arrow coming in. And in fact,
143
00:06:43,404 --> 00:06:48,140
I'm gonna put this thing into a Navigation Controller, okay?
144
00:06:48,142 --> 00:06:52,077
That way I can have titles on things and stuff like that.
145
00:06:52,079 --> 00:06:54,246
Okay, so this is our UI to start. And
146
00:06:54,248 --> 00:06:57,716
we're gonna start by just having a question up here in
147
00:06:57,718 --> 00:07:01,720
this cloud TableViewController right here. So I'm gonna go to
148
00:07:01,722 --> 00:07:04,322
the cloud TableViewController. Where did I put that?
149
00:07:04,324 --> 00:07:06,391
Right here. I'm gonna take that out of Supporting Files,
150
00:07:06,393 --> 00:07:09,661
actually. So I'm gonna go to the cloud TableViewController,
151
00:07:09,663 --> 00:07:11,129
and I'm just gonna, in its viewDidLoad,
152
00:07:11,131 --> 00:07:12,931
I'm just gonna give it a question. Okay, so,
153
00:07:12,933 --> 00:07:15,467
super.viewDidLoad, just so you can see how this thing works.
154
00:07:15,469 --> 00:07:20,839
So qanda = a QandA, and here's the constructor for it.
155
00:07:20,841 --> 00:07:21,239
It takes a question, you know,
156
00:07:21,241 --> 00:07:26,144
"What is your favorite color?" or something like that. And
157
00:07:26,146 --> 00:07:29,915
the answer is ["Blue", "Black", "Red"],
158
00:07:29,917 --> 00:07:34,753
okay, or however many we want, okay? So let's just run and
159
00:07:34,755 --> 00:07:37,689
see what that looks like. We'll go ahead and run I think
160
00:07:37,691 --> 00:07:42,027
we'll run this on the device, actually. Let's try that.
161
00:07:50,804 --> 00:07:54,873
Okay, so this is an iPad, but it's running here in iPhone
162
00:07:54,875 --> 00:07:58,043
Compatibility mode, I'm still sharing an iPad.
163
00:07:58,045 --> 00:07:59,644
So here you go, what is your favorite color?
164
00:07:59,646 --> 00:08:01,179
It shows to you, you can scroll up and
165
00:08:01,181 --> 00:08:03,348
down and you can choose an answer, okay? Now,
166
00:08:03,350 --> 00:08:06,318
that's because we're in answering mode, okay? But
167
00:08:06,320 --> 00:08:11,590
if we go back to our code over here and say asking = oops,
168
00:08:11,592 --> 00:08:14,559
asking = true, okay, and then run,
169
00:08:14,561 --> 00:08:18,263
you'll get very similar UI. The only difference being,
170
00:08:18,265 --> 00:08:20,966
we can edit it. We can add more answers and
171
00:08:20,968 --> 00:08:22,667
move our answers around. Okay, so here we go.
172
00:08:22,669 --> 00:08:26,371
Notice that we can reposition them, okay, so if we want one
173
00:08:26,373 --> 00:08:29,307
answer to be after another, we could add another answer here,
174
00:08:29,309 --> 00:08:34,145
maybe white. Okay, something like that. We can delete
175
00:08:34,147 --> 00:08:38,416
an answer just by backspacing out of it. Gets rid of it.
176
00:08:38,418 --> 00:08:41,653
Okay, we can change the question too if wanted. Okay,
177
00:08:41,655 --> 00:08:43,955
so everyone understand what this QandATableViewController
178
00:08:43,957 --> 00:08:46,525
we just inherited from does? That it. That's all it does.
179
00:08:46,527 --> 00:08:48,226
Okay, now, it knows nothing about the cloud.
180
00:08:48,228 --> 00:08:51,530
And what we're gonna make this Q&A thing here be
181
00:08:51,532 --> 00:08:53,298
stored on the cloud. All right, so
182
00:08:53,300 --> 00:08:56,268
now we're back in the CloudQandATableViewController.
183
00:08:56,270 --> 00:08:59,304
And we want to make this thing work with the cloud. So
184
00:08:59,306 --> 00:09:02,807
it's gonna have a different model than its superclass.
185
00:09:02,809 --> 00:09:06,578
Its superclass's model is a question and answer, which is
186
00:09:06,580 --> 00:09:11,016
good, but our model here is going to be a CKRecord, okay,
187
00:09:11,018 --> 00:09:11,917
a CloudKit record.
188
00:09:11,919 --> 00:09:15,053
So I'm gonna call this thing CKQandA,
189
00:09:15,055 --> 00:09:18,957
okay, called that because it's CloudKit Q&A.
190
00:09:18,959 --> 00:09:22,327
Actually, maybe we can even call it CloudKitQandARecord so
191
00:09:22,329 --> 00:09:26,097
we're clear that it's a record in CloudKit. And
192
00:09:26,099 --> 00:09:29,234
it's gonna be a CKRecord, is all. And of course,
193
00:09:29,236 --> 00:09:32,470
if anyone sets it, just like with almost all of our MVC's,
194
00:09:32,472 --> 00:09:34,372
when someone sets our model to something,
195
00:09:34,374 --> 00:09:36,942
if it's public and we allow people to set it, when we
196
00:09:36,944 --> 00:09:39,644
set it, we want to react to that. And what we want to do
197
00:09:39,646 --> 00:09:42,047
when someone sets our record is call our superclass,
198
00:09:42,049 --> 00:09:45,317
set up our superclass's model, right, that QandA. We're gonna
199
00:09:45,319 --> 00:09:47,485
get the question and answer out of the record and put,
200
00:09:47,487 --> 00:09:52,023
set it up in our superclass. So we'll just let the question
201
00:09:52,025 --> 00:09:56,194
equal the record's, and remember that we access
202
00:09:56,196 --> 00:10:00,465
the records just using dictionary-like notation.
203
00:10:00,467 --> 00:10:04,836
So I'm gonna use one of those constants that I had there,
204
00:10:04,838 --> 00:10:06,972
Attribute.Question. Okay?
205
00:10:06,974 --> 00:10:10,342
That's over here, remember, in the CloudKit extensions.
206
00:10:10,344 --> 00:10:15,180
So Cloud.Attribute.Question is this key, so that's gonna
207
00:10:15,182 --> 00:10:19,084
be a key into my QandA entity right here. Okay, and notice
208
00:10:19,086 --> 00:10:21,720
how I'm not going and creating this scheme anywhere first,
209
00:10:21,722 --> 00:10:24,222
I'm just writing my code on the fly, and it's just gonna
210
00:10:24,224 --> 00:10:26,558
automatically create these things in the database for
211
00:10:26,560 --> 00:10:30,161
me as I go. Now, we also know that this ckQandARecord,
212
00:10:30,163 --> 00:10:31,863
when you use this dictionary notation,
213
00:10:31,865 --> 00:10:35,734
it always returns this type that you have to cast, right.
214
00:10:35,736 --> 00:10:39,671
It returns a CKRecord type or something like that.
215
00:10:39,673 --> 00:10:41,906
So I'm gonna have to say as a String, cuz I know
216
00:10:41,908 --> 00:10:44,376
the question is a string. And by the way, if that's nil,
217
00:10:44,378 --> 00:10:48,313
I'm just gonna use an empty string as my question, okay?
218
00:10:48,315 --> 00:10:50,248
So that way my question's never nil here.
219
00:10:50,250 --> 00:10:52,217
And then it's similar with the answers,
220
00:10:52,219 --> 00:10:54,886
that's the QandARecord, using the attribute for
221
00:10:54,888 --> 00:10:57,455
the answers, except for, that's not a string,
222
00:10:57,457 --> 00:11:00,592
it's an array of strings. And if that's nil, then we'll
223
00:11:00,594 --> 00:11:03,762
use an empty array. So now I've gotten my question and
224
00:11:03,764 --> 00:11:06,731
my answers out of this record that was given to me.
225
00:11:06,733 --> 00:11:10,402
Okay, this is didSet on my public API right here. So now
226
00:11:10,404 --> 00:11:14,606
I'm gonna have my superclass qanda equal a QandA, okay,
227
00:11:14,608 --> 00:11:18,576
where the question is that question and the answers is
228
00:11:18,578 --> 00:11:24,049
that answers. Okay? Now, what about the asking,
229
00:11:24,051 --> 00:11:25,917
okay, what about whether I'm asking or not?
230
00:11:25,919 --> 00:11:28,086
Okay, this thing of whether it's gonna be editable or
231
00:11:28,088 --> 00:11:30,822
not. Well, that depends on whether this record they just
232
00:11:30,824 --> 00:11:33,491
gave me was created by me. If it was created by me, them I'm
233
00:11:33,493 --> 00:11:35,326
clearly editing it. It was created by someone else,
234
00:11:35,328 --> 00:11:39,731
then I'm just choosing it. So I'm just gonna say that that
235
00:11:39,733 --> 00:11:45,270
is ckQandARecord.wasCreated, what did I call it,
236
00:11:45,272 --> 00:11:50,909
wasCreatedByThisUser, okay? So I'm just using that thing
237
00:11:50,911 --> 00:11:55,313
I showed you in that, that little extension over here,
238
00:11:55,315 --> 00:11:56,915
okay? So we've got this record, now,
239
00:11:56,917 --> 00:12:00,885
a couple of problems here, okay? One thing is right here.
240
00:12:00,887 --> 00:12:05,590
Use of undeclared type CKRecord. Why is that? import
241
00:12:05,592 --> 00:12:09,327
CloudKit. Okay? CloudKit is a different framework.
242
00:12:09,329 --> 00:12:10,962
If you want to use it in any of your files,
243
00:12:10,964 --> 00:12:13,765
you have to import CloudKit, okay? Still have an error
244
00:12:13,767 --> 00:12:16,334
here. What's this? The old no initializers.
245
00:12:16,336 --> 00:12:20,105
Why? Because this is never initialized. Okay, well,
246
00:12:20,107 --> 00:12:22,974
this is kind of an interesting case, because I could just,
247
00:12:22,976 --> 00:12:25,610
for example, make this be an optional.
248
00:12:25,612 --> 00:12:29,380
Then it would start out nil, that's great, but really,
249
00:12:29,382 --> 00:12:34,018
I don't want any external person to be giving me
250
00:12:34,020 --> 00:12:36,721
an empty record, you know, a nil record. So,
251
00:12:36,723 --> 00:12:38,890
I'm gonna do something kind of interesting here that you can
252
00:12:38,892 --> 00:12:42,927
do when you want your public API to be non-optional but
253
00:12:42,929 --> 00:12:44,596
you still going to allow,
254
00:12:44,598 --> 00:12:48,166
internally, the thing to be optional or nil.
255
00:12:48,168 --> 00:12:50,435
Okay, what I'm gonna do is, I'm gonna leave this public
256
00:12:50,437 --> 00:12:54,105
one to be optional, non-optional, rather. And
257
00:12:54,107 --> 00:12:59,911
I'm gonna turn it into a get and set, okay, a computed one.
258
00:12:59,913 --> 00:13:04,315
Where it's going to be implementing its get and
259
00:13:04,317 --> 00:13:09,387
set by using a private var _ckQandARecord, and this
260
00:13:09,389 --> 00:13:13,057
one's going to be optional. Okay, it's gonna do all
261
00:13:13,059 --> 00:13:15,627
the same things that it did, it's just gonna be optional.
262
00:13:15,629 --> 00:13:18,396
And in the get and set here, the set here is just going to
263
00:13:18,398 --> 00:13:23,968
set the ckQandARecord private one to be the new value.
264
00:13:23,970 --> 00:13:25,270
Okay, this is always gonna be non-optional,
265
00:13:25,272 --> 00:13:27,806
that's going to be great, that's gonna set it there. And
266
00:13:27,808 --> 00:13:31,342
here, if someone tries to get the ckQandARecord,
267
00:13:31,344 --> 00:13:36,214
okay I'm gonna check to see if my private one is nil.
268
00:13:36,216 --> 00:13:40,185
And if it is, I'm gonna set my private one to just be a blank
269
00:13:40,187 --> 00:13:46,558
one. Okay, and here I'm gonna use this Entity.QandA.
270
00:13:46,560 --> 00:13:47,659
Okay, so I'm gonna create a blank one.
271
00:13:47,661 --> 00:13:50,862
And then I can return _ckQandA unwrapped,
272
00:13:50,864 --> 00:13:53,298
cuz I know I always at least create it.
273
00:13:53,300 --> 00:13:56,201
So this is kind of a little tricky thing here to make sure
274
00:13:56,203 --> 00:13:59,737
that it's always this way. And another good thing about this
275
00:13:59,739 --> 00:14:02,473
is, sorry, you had a question? >> Could you use
276
00:14:02,475 --> 00:14:05,710
a lazy initializer here? >> Yeah, so the question is,
277
00:14:05,712 --> 00:14:07,045
can I use a lazy initializer here?
278
00:14:07,047 --> 00:14:09,414
Like, could I have said ckQandARecord =
279
00:14:09,416 --> 00:14:12,050
CKRecord whatever, or even a lazy one, I wouldn't even have
280
00:14:12,052 --> 00:14:15,453
to be lazy. The reason I'm not doing that is the old problem
281
00:14:15,455 --> 00:14:16,554
where didSet doesn't
282
00:14:16,556 --> 00:14:21,092
get called with the initializer, right? And
283
00:14:21,094 --> 00:14:22,827
I want didSet to always get called, and
284
00:14:22,829 --> 00:14:25,263
this is gonna cause didSet to always get called.
285
00:14:25,265 --> 00:14:28,233
So it's a little bit of a trick there. Okay?
286
00:14:28,235 --> 00:14:31,502
All right, so we've got this all set up for
287
00:14:31,504 --> 00:14:35,240
our public API here. What do we want to do next?
288
00:14:35,242 --> 00:14:38,109
Well, let's go ahead and change our viewDidLoad,
289
00:14:38,111 --> 00:14:40,245
instead of setting our superclass thing,
290
00:14:40,247 --> 00:14:42,780
let's just test and make sure we're working. So
291
00:14:42,782 --> 00:14:46,784
I'm gonna set my ckQandARecord equal to an empty record,
292
00:14:46,786 --> 00:14:55,393
just as a test here. Okay? So we're just gonna do that.
293
00:14:55,395 --> 00:15:00,231
I just wanna see if this is working. So let's run.
294
00:15:00,233 --> 00:15:02,500
And it should come up an empty question, but
295
00:15:02,502 --> 00:15:05,837
it should be editable, because I created this CKRecord.
296
00:15:05,839 --> 00:15:08,640
Right? So here it is. It's empty, empty question.
297
00:15:08,642 --> 00:15:12,277
I could type here, you know, I could say something, you know,
298
00:15:12,279 --> 00:15:18,416
like, When, we'll just say When, okay. Answers, Today.
299
00:15:18,418 --> 00:15:22,654
Maybe Never, okay.
300
00:15:22,656 --> 00:15:27,659
And then Tomorrow. Okay, so I've created this question.
301
00:15:27,661 --> 00:15:29,994
Now, as I edit all this stuff, of course,
302
00:15:29,996 --> 00:15:31,763
none of this is going on the cloud yet.
303
00:15:31,765 --> 00:15:34,565
And in fact, none of it's even going into the CKRecord.
304
00:15:34,567 --> 00:15:37,535
It's all just being held in that Q and A, okay. So
305
00:15:37,537 --> 00:15:40,004
the next step we need to do is to have this thing
306
00:15:40,006 --> 00:15:43,374
write to the cloud. Now, when to write to the cloud is
307
00:15:43,376 --> 00:15:46,344
the question here, okay? We could add a button,
308
00:15:46,346 --> 00:15:48,746
maybe a bar button item up there that says,
309
00:15:48,748 --> 00:15:51,416
Save to Cloud, okay? Or just Save or something like that.
310
00:15:51,418 --> 00:15:54,986
So that, that would be one way of it. But I'm actually kind
311
00:15:54,988 --> 00:15:58,489
of a fan of UIs that the user doesn't have to take extra
312
00:15:58,491 --> 00:16:01,960
steps unnecessarily. So wouldn't it be cool if we just
313
00:16:01,962 --> 00:16:05,697
saved this to the cloud every time someone finishes
314
00:16:05,699 --> 00:16:06,397
editing one of these fields.
315
00:16:06,399 --> 00:16:09,133
Now as soon as the keyboard goes away, any of these times,
316
00:16:09,135 --> 00:16:11,569
boom, we'll just save whatever we got to the cloud.
317
00:16:11,571 --> 00:16:15,239
Now we might also want another UI which is published,
318
00:16:15,241 --> 00:16:16,341
so that it shows it to other users.
319
00:16:16,343 --> 00:16:19,243
But for now users can see things in progress, okay,
320
00:16:19,245 --> 00:16:21,212
just because we're starting out our app.
321
00:16:21,214 --> 00:16:23,715
So that's what I'm gonna do, I'm gonna have it every time
322
00:16:23,717 --> 00:16:26,751
that this first responder gets taken away from a text field,
323
00:16:26,753 --> 00:16:28,319
I'm gonna have it update the cloud.
324
00:16:28,321 --> 00:16:31,189
So how can we find out when a first responder's taken away
325
00:16:31,191 --> 00:16:33,691
from the text view? We gonna use the text view's delegate,
326
00:16:33,693 --> 00:16:36,160
it's almost identical to the text field delegate
327
00:16:36,162 --> 00:16:39,630
that we talked about before. And our super classes, okay,
328
00:16:39,632 --> 00:16:43,101
the QandATableViewController, right here.
329
00:16:43,103 --> 00:16:46,237
It's super class is the text TableViewController and
330
00:16:46,239 --> 00:16:50,108
the text TableViewController already and not surprisingly,
331
00:16:50,110 --> 00:16:54,012
is a UITextViewDelegate. So it's already receiving
332
00:16:54,014 --> 00:16:56,848
UITextViewDelegate message whenever any of its text
333
00:16:56,850 --> 00:16:59,717
views are edited. Okay, so that's great. So all we need
334
00:16:59,719 --> 00:17:02,553
to do here is implement a UITextViewDelegate, so
335
00:17:02,555 --> 00:17:05,990
I'm gonna implement the one which is DidEndEditing.
336
00:17:05,992 --> 00:17:08,593
Okay, TextViewDidEndEditing. Whoops,
337
00:17:08,595 --> 00:17:13,431
not did change. DidEndEditing,
338
00:17:13,433 --> 00:17:18,603
this one. Okay, and let my super class. He might well be
339
00:17:18,605 --> 00:17:23,608
doing something there so, we'll let him do his thing.
340
00:17:23,610 --> 00:17:27,245
And now what I'm going to do with just update the Cloud, so
341
00:17:27,247 --> 00:17:30,448
I'm going to have some method iCloud update, okay?
342
00:17:30,450 --> 00:17:33,584
And it's complaining here because I overrode this so
343
00:17:33,586 --> 00:17:38,056
I need to say override. Okay, so this iCloud update is going
344
00:17:38,058 --> 00:17:40,958
to take our Q&A, ship it up to the iCloud,
345
00:17:40,960 --> 00:17:44,495
to the cloud kit database up there. So let's go ahead and
346
00:17:44,497 --> 00:17:50,635
do that. That's private func iCloud update.
347
00:17:50,637 --> 00:17:53,504
Okay, so what do we need to do, to write this thing,
348
00:17:53,506 --> 00:17:55,406
to the cloud? It's actually really simple.
349
00:17:55,408 --> 00:17:58,342
The first thing we're gonna do is get the stuff out of our
350
00:17:58,344 --> 00:18:02,246
QnA into our record. Then we're gonna save our record,
351
00:18:02,248 --> 00:18:05,450
okay? So let's do the first part of that first.
352
00:18:05,452 --> 00:18:10,555
Which is let's take our ckQandARecord, and set,
353
00:18:10,557 --> 00:18:13,191
for example, its Question, and
354
00:18:13,193 --> 00:18:18,596
we'll set this question equal to be the Q&A question.
355
00:18:18,598 --> 00:18:23,301
So we're just grabbing it out of our super
356
00:18:23,303 --> 00:18:28,639
classes thing and similarly here the Q&A for
357
00:18:28,641 --> 00:18:32,577
the answers equals qanda.answers.
358
00:18:32,579 --> 00:18:35,980
One thing, I'd probably don't wanna be polluting the cloud
359
00:18:35,982 --> 00:18:37,281
with empty question and answers.
360
00:18:37,283 --> 00:18:40,551
So I'm gonna put a little if around this just to say.
361
00:18:40,553 --> 00:18:44,856
if my qanda, qanda.question,
362
00:18:44,858 --> 00:18:48,426
oops, question.isEmpty and
363
00:18:48,428 --> 00:18:54,932
I'm also goinna make sure that my answers, Q and
364
00:18:54,934 --> 00:18:59,637
A answers isn't empty either, okay?
365
00:18:59,639 --> 00:19:02,507
So, in other words, if I don't have an empty question or
366
00:19:02,509 --> 00:19:05,843
answer, then I'll go ahead and do this. Okay, so now that
367
00:19:05,845 --> 00:19:09,547
I've updated this record. I need to save it to the cloud.
368
00:19:09,549 --> 00:19:11,682
So I'm gonna actually have a different function for that.
369
00:19:11,684 --> 00:19:14,285
ICloudSaveRecord I'm gonna call it.
370
00:19:14,287 --> 00:19:18,089
I'm gonna pass my CKQandARecord along. Okay,
371
00:19:18,091 --> 00:19:21,526
we're gonna have this little function that's going to save
372
00:19:21,528 --> 00:19:27,198
things to the cloud. So, private func iCloudSaveRecord,
373
00:19:27,200 --> 00:19:32,370
takes a recordToSave, which is a CKRecord. Okay,
374
00:19:32,372 --> 00:19:35,907
now what's this guy gonna do? This one also surprisingly
375
00:19:35,909 --> 00:19:38,576
straightforward, as you saw from the slides. One thing
376
00:19:38,578 --> 00:19:41,279
is any time we're gonna save something into the Cloud,
377
00:19:41,281 --> 00:19:42,947
we need a database to work with. So
378
00:19:42,949 --> 00:19:48,085
I'm gonna say private let the database equal CKcontainer,
379
00:19:48,087 --> 00:19:52,423
remember? Container, whoops, [LAUGH] CKcontainer,
380
00:19:52,425 --> 00:19:56,060
defaultContainer public. So we're gonna put this in
381
00:19:56,062 --> 00:19:58,129
the public cloud because I want everyone to
382
00:19:58,131 --> 00:19:59,697
see everyone else's questions, okay?
383
00:19:59,699 --> 00:20:02,133
So if I put it in the private database I'd only be able to
384
00:20:02,135 --> 00:20:04,702
see my own questions, so this way I can see everybody's,
385
00:20:04,704 --> 00:20:08,606
okay? So we got that. So I'm gonna ask the database here,
386
00:20:08,608 --> 00:20:14,712
just to save this record. Okay, the record to save. And
387
00:20:14,714 --> 00:20:17,515
here's the completion handler right here. And in this
388
00:20:17,517 --> 00:20:21,519
completions handler, we have the saved record basically.
389
00:20:21,521 --> 00:20:25,723
Which should be the same thing or nil and the error, okay?
390
00:20:25,725 --> 00:20:28,526
And this is asynchronous. Okay, this closure is gonna
391
00:20:28,528 --> 00:20:31,362
be called sometime later when this thing is saved.
392
00:20:31,364 --> 00:20:33,564
Now, this is a demo so I don't have time for
393
00:20:33,566 --> 00:20:36,300
us to go and analyze every realistic error that could
394
00:20:36,302 --> 00:20:38,669
happen every single time we do something with the cloud.
395
00:20:38,671 --> 00:20:40,838
But I'm gonna show you a few of them just so
396
00:20:40,840 --> 00:20:43,574
you kind of get an idea how to handle errors, what kind of
397
00:20:43,576 --> 00:20:46,210
errors will come along. So let's look at an interesting
398
00:20:46,212 --> 00:20:49,313
error that might happen when you're saving a record, okay.
399
00:20:49,315 --> 00:20:52,617
So this error code equals
400
00:20:52,619 --> 00:20:59,790
CKErrorCode.ServerRecordChan- ged.
401
00:20:59,792 --> 00:21:03,060
Okay, don't forget .rawValue at the end because we're
402
00:21:03,062 --> 00:21:06,163
looking at this as an right here.
403
00:21:06,165 --> 00:21:09,133
So what is this error? This error means we tried to save
404
00:21:09,135 --> 00:21:12,303
a record but that record had already been saved newer
405
00:21:12,305 --> 00:21:16,007
by someone else, okay. So now I've got old data that I'm
406
00:21:16,009 --> 00:21:18,376
trying to write on top of somebody who wrote newer data.
407
00:21:18,378 --> 00:21:20,811
Maybe it was one of my other devices. Maybe it's even me.
408
00:21:20,813 --> 00:21:24,115
Maybe I'm writing so fast. I saved three of four records in
409
00:21:24,117 --> 00:21:25,883
a row, okay. They all got queued up and
410
00:21:25,885 --> 00:21:29,053
one of them got through the network faster than the other,
411
00:21:29,055 --> 00:21:32,490
could be. This kind of approach of writing things and
412
00:21:32,492 --> 00:21:34,892
just checking when you write them to see
413
00:21:34,894 --> 00:21:37,194
if it's newer is called optimistic locking.
414
00:21:37,196 --> 00:21:40,097
Okay, it's basically you don't check first before you write,
415
00:21:40,099 --> 00:21:43,234
you just try to write and it's newer you fail. Okay?
416
00:21:43,236 --> 00:21:47,204
So save record which is this convenous method on database,
417
00:21:47,206 --> 00:21:50,775
really has no way to make it overwrite,
418
00:21:50,777 --> 00:21:52,209
have an older thing overwrite a newer thing.
419
00:21:52,211 --> 00:21:53,878
It's not even clear that's what I'd want here.
420
00:21:53,880 --> 00:21:57,048
I probably do want the newer thing, to be what what gets
421
00:21:57,050 --> 00:22:01,786
through. If I did wanna do do overwriting of newer things,
422
00:22:01,788 --> 00:22:02,353
then I can use that whole
423
00:22:02,355 --> 00:22:04,388
operation-based thing I was telling you about in
424
00:22:04,390 --> 00:22:06,757
the slides. Much more complicated that this
425
00:22:06,759 --> 00:22:09,794
convenience method but it can be done. But anyway for server
426
00:22:09,796 --> 00:22:13,164
record change happens there is really nothing we can do or
427
00:22:13,166 --> 00:22:16,600
we would want to do. So you know we can just ignore this,
428
00:22:16,602 --> 00:22:21,072
okay. We just assume that the newer ones is what we wanted.
429
00:22:21,074 --> 00:22:22,707
Okay, what are the kind of stuff going to happen for
430
00:22:22,709 --> 00:22:28,379
writing here? How about, let's say if our error is not Nil.
431
00:22:28,381 --> 00:22:28,579
Well in other words,
432
00:22:28,581 --> 00:22:31,315
we got some other kinds of error. Let's check to see
433
00:22:31,317 --> 00:22:34,485
if that error may be has a retry in there for us. Okay,
434
00:22:34,487 --> 00:22:37,988
something we can go and retry what we' re doing. Okay? Now,
435
00:22:37,990 --> 00:22:41,892
maybe why does that happen? Net, network let, latencies,
436
00:22:41,894 --> 00:22:44,862
something like that. Some network problem that might
437
00:22:44,864 --> 00:22:47,631
recover with a little bit of time, that kind of thing.
438
00:22:47,633 --> 00:22:51,469
So, how do we do this? So, I am just going to call another
439
00:22:51,471 --> 00:22:56,540
function to handle this. I am gonna call it retry after
440
00:22:56,542 --> 00:23:00,444
error. It's goinna take the error whatever happened and
441
00:23:00,446 --> 00:23:03,414
I am just going to give it a selector to call to retry.
442
00:23:03,416 --> 00:23:06,016
Okay, and the selector here is going to be in this case since
443
00:23:06,018 --> 00:23:09,253
we are saving record. I'm gonna have iCloud update,
444
00:23:09,255 --> 00:23:14,024
okay, this thing up here that updates, try to do this again.
445
00:23:14,026 --> 00:23:17,928
Okay? Now let's go and put this t retry in there and
446
00:23:17,930 --> 00:23:20,765
we'll see what some of these errors that are.
447
00:23:20,767 --> 00:23:25,102
So this is a private func retryAfterError,
448
00:23:25,104 --> 00:23:27,671
takes an error, do an NSError.
449
00:23:27,673 --> 00:23:31,409
And then it takes with selector. We'll have a nice
450
00:23:31,411 --> 00:23:35,379
local name for that and this is a selector, okay.
451
00:23:35,381 --> 00:23:38,716
So we have errors here, so what is this error. It says,
452
00:23:38,718 --> 00:23:38,816
selector refers to
453
00:23:38,818 --> 00:23:42,219
a method that does not expose to objective C. That's iCloud.
454
00:23:42,221 --> 00:23:45,723
Now why is this iCloud update not exposed to objective C?
455
00:23:45,725 --> 00:23:47,758
Well, it's because it's private, okay.
456
00:23:47,760 --> 00:23:50,394
This is a class that inherits from in it' s objects, so
457
00:23:50,396 --> 00:23:52,763
that's not the problem. The problem is that we have
458
00:23:52,765 --> 00:23:54,565
a private method here. So we can expose it
459
00:23:54,567 --> 00:23:57,668
either by making it not private or just saying OBJC.
460
00:23:57,670 --> 00:24:01,672
That mean, hey, expose this to Objective-C run time. Okay?
461
00:24:01,674 --> 00:24:06,610
Make sense? All right, here is now saying the call
462
00:24:06,612 --> 00:24:12,016
to the method iCloud update requires explicit self, okay?
463
00:24:12,018 --> 00:24:15,019
So that it knows, in our self because we happen to be
464
00:24:15,021 --> 00:24:17,888
in a closure right here, okay? All references to
465
00:24:17,890 --> 00:24:21,792
things in our selves Have to be explicit. Okay, so
466
00:24:21,794 --> 00:24:24,862
that's good. All right, now, retry after or how we gonna
467
00:24:24,864 --> 00:24:29,233
retry after. Well, if we can't get that retry interval magic
468
00:24:29,235 --> 00:24:32,269
out of the error structure then we cannot do it so
469
00:24:32,271 --> 00:24:33,103
that's the first thing we're gonna do.
470
00:24:33,105 --> 00:24:36,607
We're gonna say if we can let retry interval
471
00:24:37,276 --> 00:24:41,011
equal the errors. User info, okay so
472
00:24:41,013 --> 00:24:44,582
user info is this dictionary that comes with an error that
473
00:24:44,584 --> 00:24:46,317
gives you kind of interesting info.
474
00:24:46,319 --> 00:24:51,989
And, the thing we want is ck error,
475
00:24:51,991 --> 00:24:55,359
retry after key, okay? So
476
00:24:55,361 --> 00:25:00,664
that should be an ns time interval. So if we're able to
477
00:25:00,666 --> 00:25:05,135
get that retry interval, then hey, let's go ahead and retry.
478
00:25:05,471 --> 00:25:09,340
And how are we going to retry here? Well I'm just going to
479
00:25:09,342 --> 00:25:13,777
do an NSTimer, scheduled timer with time interval,
480
00:25:13,779 --> 00:25:18,282
I want this one, let's go ahead and make this
481
00:25:19,819 --> 00:25:24,054
have different lines here so you can see little better what
482
00:25:24,056 --> 00:25:28,359
we're doing with the timer. Okay, so the time interval
483
00:25:28,361 --> 00:25:31,829
here is that retry interval that we got out of the error.
484
00:25:31,831 --> 00:25:34,532
The target is going to be our self.
485
00:25:34,534 --> 00:25:39,270
The selector is the selector we passed on Here, okay?
486
00:25:39,272 --> 00:25:42,606
User info, eh, we don't have any, we're just retrying, so
487
00:25:42,608 --> 00:25:48,779
we don't have any extra data, and it does not repeat. Okay,
488
00:25:48,781 --> 00:25:51,181
now the problem here we gotta be careful of,
489
00:25:51,183 --> 00:25:54,051
we're calling retry after error in this closure,
490
00:25:54,053 --> 00:25:57,922
which is executing off the main thread. Okay, so
491
00:25:57,924 --> 00:26:00,958
we cannot do NSTimers on other threads. So
492
00:26:00,960 --> 00:26:08,399
we need to dispatc_async, to the main queue.
493
00:26:08,401 --> 00:26:13,103
Dispatc_Ge_mai_queue.
494
00:26:13,105 --> 00:26:18,042
We'll do this, like that. Okay,
495
00:26:18,044 --> 00:26:23,047
you got that? Cool, all right so
496
00:26:23,049 --> 00:26:27,184
that's just a couple of the errors that we can do.
497
00:26:27,186 --> 00:26:29,787
If it succeeds, we actually don't need to do anything.
498
00:26:29,789 --> 00:26:30,554
It saved it, we're happy,
499
00:26:30,556 --> 00:26:35,793
we can just move on, okay. All right, so let's try and
500
00:26:35,795 --> 00:26:37,027
run this, let's just see if it happens,
501
00:26:37,029 --> 00:26:40,030
let's see if it can save this thing to the database. So
502
00:26:40,032 --> 00:26:46,971
I'm gonna run, all right, here we crashed,
503
00:26:46,973 --> 00:26:50,874
now why'd we crash? The first place I'm always gonna
504
00:26:50,876 --> 00:26:53,611
look is the console down here. So let's go take a look at
505
00:26:53,613 --> 00:26:57,948
the console and see if it has a reason that it crashed and
506
00:26:57,950 --> 00:26:59,249
it does have a reason. And the reason is,
507
00:26:59,251 --> 00:27:02,853
this application is missing the required entitlement for
508
00:27:02,855 --> 00:27:03,253
iCloud services.
509
00:27:03,255 --> 00:27:05,356
So this is that thing I was telling you about.
510
00:27:05,358 --> 00:27:06,523
We have to go enable iCloud,
511
00:27:06,525 --> 00:27:09,627
all right. Because iCloud is server technology. Gee,
512
00:27:09,629 --> 00:27:11,862
things are gonna have to be created on a server side for
513
00:27:11,864 --> 00:27:14,732
this app, etcetera. So they don't want to create that for
514
00:27:14,734 --> 00:27:16,967
every app, only for apps that are actually gonna use it.
515
00:27:16,969 --> 00:27:20,804
So how do we do that? We go up here, okay, to our inspector,
516
00:27:20,806 --> 00:27:23,040
inspect our project settings here and
517
00:27:23,042 --> 00:27:24,642
we'll go to capabilities.
518
00:27:24,644 --> 00:27:27,578
And we click iCloud here, okay? So you have to make sure
519
00:27:27,580 --> 00:27:30,748
you pick a team, okay, cuz it needs to know who you are so
520
00:27:30,750 --> 00:27:33,550
we can set up this iCloud stuff for you, okay?
521
00:27:33,552 --> 00:27:37,855
Your, account, your developer account up there. So
522
00:27:37,857 --> 00:27:40,824
here we have iCloud enabled, we are going to pick cloud kit
523
00:27:40,826 --> 00:27:45,963
as that's the service that we want, we can pick these other
524
00:27:45,965 --> 00:27:48,899
ones as well but we're going to focus on CloudKit today.
525
00:27:48,901 --> 00:27:52,169
And we'll be looking at the dashboard in a little bit,
526
00:27:52,171 --> 00:27:55,005
actually let's go take a look at our dashboard now.
527
00:27:55,007 --> 00:27:58,409
Once your at dashboard, of course you have to log in.
528
00:27:58,711 --> 00:28:00,611
Here we are, nothing has been created yet and
529
00:28:00,613 --> 00:28:03,714
that's because we haven't done any iCloud where we have
530
00:28:03,716 --> 00:28:08,652
tried to create any entities, okay? All right, so
531
00:28:08,654 --> 00:28:10,587
now that we've done that. Now let's go back and
532
00:28:10,589 --> 00:28:14,091
run, and do that. And actually create some entities. So let's
533
00:28:14,093 --> 00:28:19,063
get our code back up here so you can see that. And run.
534
00:28:23,769 --> 00:28:28,105
All right. So here we go. Blank question and answer.
535
00:28:28,107 --> 00:28:35,813
Let's go ahead a put a question answer here again,
536
00:28:35,815 --> 00:28:40,884
let's try how about what is your
537
00:28:40,886 --> 00:28:46,056
favorite team? Okay,
538
00:28:46,058 --> 00:28:52,396
we'll say, maybe the Sharks. Whoops. Sharks,
539
00:28:52,398 --> 00:28:59,903
or maybe the Warriors, or maybe it's the Giants. Okay,
540
00:29:00,106 --> 00:29:02,473
there are a lot of teams to choose from in the Bay area.
541
00:29:02,475 --> 00:29:07,377
All right? So hopefully this is creating on the server
542
00:29:07,379 --> 00:29:12,116
because remember every time we went to a new field,
543
00:29:12,118 --> 00:29:13,884
it should be riding something up to the Cloud.
544
00:29:13,886 --> 00:29:18,722
So let's go back and take a look at our dashboard here, so
545
00:29:18,724 --> 00:29:23,460
let's reload, Here it is, look at that,
546
00:29:23,462 --> 00:29:26,363
QandA entity got created with answers and questions.
547
00:29:26,365 --> 00:29:28,732
So that's great. So that's the entity side of it.
548
00:29:28,734 --> 00:29:30,400
And we can actually look at the data,
549
00:29:30,402 --> 00:29:33,270
not just the entity, but the data that's been stored by
550
00:29:33,272 --> 00:29:34,872
going down here to this Default Zone.
551
00:29:34,874 --> 00:29:36,840
Now, we'll notice when we try to look in here for
552
00:29:36,842 --> 00:29:39,409
things, it says, records can't be shown because there's no
553
00:29:39,411 --> 00:29:42,446
index on the Record ID field. So you can't look at things
554
00:29:42,448 --> 00:29:46,517
that aren't indexed. And so let me show you where that is.
555
00:29:46,519 --> 00:29:49,486
If you go back here you see metadata indexes,
556
00:29:49,488 --> 00:29:52,856
record ID not being indexed. Same thing created by.
557
00:29:52,858 --> 00:29:55,225
In fact I want both of those to be searchable.
558
00:29:55,227 --> 00:29:58,862
I need created by so I know which Q&As were created by me,
559
00:29:58,864 --> 00:30:00,297
and I want record ID so
560
00:30:00,299 --> 00:30:02,166
I can go find all the ones out there. And
561
00:30:02,168 --> 00:30:04,635
you can actually see here I'm gonna go ahead and save this.
562
00:30:04,637 --> 00:30:06,703
I'll show you something real quick here. So
563
00:30:06,705 --> 00:30:08,772
I've changed this to have these indexes.
564
00:30:08,774 --> 00:30:11,275
Now I can go to the default zone. It can find things and
565
00:30:11,277 --> 00:30:13,610
sure enough. What is your favorite team? Sharks and
566
00:30:13,612 --> 00:30:19,016
Warriors. I guess we didn't save the Giants one here. When
567
00:30:19,018 --> 00:30:21,919
we reload, we'll see that. One thing about the car I
568
00:30:21,921 --> 00:30:26,690
wanted to show you the costs thing, which is down here.
569
00:30:26,692 --> 00:30:31,028
If you look at uhs where is that uhs
570
00:30:31,931 --> 00:30:34,865
usage? If you look at usage right here it is going to show
571
00:30:34,867 --> 00:30:37,668
you how much you using how many active users you have,
572
00:30:37,670 --> 00:30:40,070
how many request per second, how much transfers you
573
00:30:40,072 --> 00:30:42,906
are doing of large files. How much storage you're using. All
574
00:30:42,908 --> 00:30:46,210
that stuff. And these things if you go above these lines,
575
00:30:46,212 --> 00:30:48,478
start costing you money. See these red lines.
576
00:30:48,480 --> 00:30:52,416
Okay, the limits? Usage? So, I'm way, way below everything.
577
00:30:52,418 --> 00:30:53,917
[LAUGH] Obviously. But, you know.
578
00:30:53,919 --> 00:30:58,622
So, this is basically how you get charged for it, okay?
579
00:30:58,624 --> 00:31:03,193
That good? All right. So we've got our nice database is being
580
00:31:03,195 --> 00:31:06,129
loaded up, it's excellent, okay? The next thing we're
581
00:31:06,131 --> 00:31:11,168
gonna do in our UI here is we are going to have another
582
00:31:11,170 --> 00:31:15,038
view controller that has a list of all the question,
583
00:31:15,040 --> 00:31:16,773
the Q&A's that are in the database, okay?
584
00:31:16,775 --> 00:31:20,277
That we can choose from, and take a look at the Q&A's Okay,
585
00:31:20,279 --> 00:31:23,513
so it's just gonna be a simple table view with a list of
586
00:31:23,515 --> 00:31:26,316
the QNAs. All right, so how we gonna do that,
587
00:31:26,318 --> 00:31:29,820
we gonna have to create a new MVC. So lets go ahead and
588
00:31:29,822 --> 00:31:34,091
do that, go file, new, go touch, this one is just going
589
00:31:34,093 --> 00:31:36,860
to be a regular UI table view controller,
590
00:31:36,862 --> 00:31:41,031
am gonna call it my AllQandAsTableViewController,
591
00:31:41,033 --> 00:31:42,866
cuz that's what it's gonna do.
592
00:31:42,868 --> 00:31:45,402
It's gonna be the table view controller, shows all my Q and
593
00:31:45,404 --> 00:31:49,139
As in it, okay? And we'll put it in the same place
594
00:31:49,141 --> 00:31:53,777
as usual here. Here's all Q and As. Let's go ahead and
595
00:31:53,779 --> 00:32:02,953
get rid of all of this stuff. Okay,
596
00:32:02,955 --> 00:32:06,990
so as always, what's gonna be the model of our new MVC?
597
00:32:06,992 --> 00:32:10,661
Well, it shows all Q and A's, so that's gonna be my model,
598
00:32:10,663 --> 00:32:13,897
all the Q and A's. And that's just going to be an array of
599
00:32:13,899 --> 00:32:19,436
CKRecord, okay. And of course, when that changes,
600
00:32:19,438 --> 00:32:22,339
if someone changes that array of records, then I'm going to
601
00:32:22,341 --> 00:32:26,376
have the table view reload its data. Hopefully you're all
602
00:32:26,378 --> 00:32:30,047
familiar with that after Smashtag experiences. Okay, so
603
00:32:30,049 --> 00:32:35,285
it's as simple as that. So how do we get all these Q and A's?
604
00:32:35,287 --> 00:32:39,489
Import CloudKit. How do we get them all? Well,
605
00:32:39,491 --> 00:32:43,327
in my view, let's do it in viewWillAppear, okay,
606
00:32:43,329 --> 00:32:48,498
when I know I'm gonna come on screen. I'm
607
00:32:48,500 --> 00:32:52,035
going to call some method to fetchAllQandAs, okay, and
608
00:32:52,037 --> 00:32:56,440
that's gonna go out to CloudKit and find them all. So
609
00:32:56,442 --> 00:33:01,478
that's private method called fetchAllQAndAs.
610
00:33:01,480 --> 00:33:06,883
Oops. Okay, and how's this thing gonna work? Well,
611
00:33:06,885 --> 00:33:09,853
this is gonna turn out to be very, very simple. Of course,
612
00:33:09,855 --> 00:33:12,022
we need our database here, so let's get that.
613
00:33:12,024 --> 00:33:15,258
CKContainer.defaultContainer, again, we're looking in
614
00:33:15,260 --> 00:33:18,662
the public database there. All right, we have a database. So
615
00:33:18,664 --> 00:33:22,332
fetching all the QandAs really just requires doing a CKQuery.
616
00:33:22,334 --> 00:33:24,468
You remember that from the slide CKQuery?
617
00:33:24,470 --> 00:33:27,871
So for CKQuery, we need a predicate, okay?
618
00:33:27,873 --> 00:33:30,974
And since I'm getting all of the QandA's,
619
00:33:30,976 --> 00:33:34,845
every single one, I'm not searching for anything,
620
00:33:34,847 --> 00:33:38,982
I'm gonna use the predicate TRUEPREDICATE. Okay, this
621
00:33:38,984 --> 00:33:42,753
is a special predicate that means, get them all. Okay,
622
00:33:42,755 --> 00:33:45,856
remember, in Core Data, we have nil means get them all,
623
00:33:45,858 --> 00:33:48,191
but here, you can't have predicate be nil, so
624
00:33:48,193 --> 00:33:52,529
TRUEPREDICATE. It means get them all. Okay? Next,
625
00:33:52,531 --> 00:33:58,268
we can just create the query itself. That's a CKQuery,
626
00:33:58,270 --> 00:34:03,673
okay? And we'll get its initializer here. So it
627
00:34:03,675 --> 00:34:05,876
just wants to know what kind of things are you querying.
628
00:34:05,878 --> 00:34:08,211
It's just like when you're doing NSFetchRequest in
629
00:34:08,213 --> 00:34:10,847
Core Data. You can only query one kind of thing at a time,
630
00:34:10,849 --> 00:34:15,819
one entity at a time. So here we're gonna have Cloud.Entity,
631
00:34:15,821 --> 00:34:20,390
Entity.QandA, cuz that's what we're searching for,
632
00:34:20,392 --> 00:34:22,759
these QandA entities, entities.
633
00:34:22,761 --> 00:34:25,162
And here's the predicate we're gonna use. Now,
634
00:34:25,164 --> 00:34:28,065
one thing I forgot to show you in the slides, very important,
635
00:34:28,067 --> 00:34:30,934
is sorting. Just like in NSFetchRequest, you can
636
00:34:30,936 --> 00:34:35,272
do this here as well. I can say query.sortDescriptors, and
637
00:34:35,274 --> 00:34:37,340
I can specify how I want to sort
638
00:34:37,342 --> 00:34:40,677
this query that I'm doing. Okay? So I'll have a sort,
639
00:34:40,679 --> 00:34:42,813
we'll sort this by the name of the,
640
00:34:42,815 --> 00:34:44,614
by the question name, okay. So
641
00:34:44,616 --> 00:34:49,719
key is Cloud.Attribute.Question, and
642
00:34:49,721 --> 00:34:54,524
we'll have it be ascending. Okay, and of course,
643
00:34:54,526 --> 00:34:57,260
this is an array just like it is with fetch request,
644
00:34:57,262 --> 00:34:59,963
you can search last name first, then by first name,
645
00:34:59,965 --> 00:35:04,067
et cetera. Okay, so that's it. We've created our carry,
646
00:35:04,069 --> 00:35:07,137
query, how we wanna sort it, what the predicate is.
647
00:35:07,139 --> 00:35:11,775
Now we just ask the database to perform this query.
648
00:35:11,777 --> 00:35:16,012
Okay? And the query is query. Now, this inZoneWithID,
649
00:35:16,014 --> 00:35:18,849
I told you that there's these zones, that are like subareas
650
00:35:18,851 --> 00:35:21,618
of databases. By dividing your database into zone,
651
00:35:21,620 --> 00:35:23,386
you can make your queries a lot more efficient.
652
00:35:23,388 --> 00:35:26,223
You can see why already, if I only am searching a small part
653
00:35:26,225 --> 00:35:29,159
of my database, it makes it a lot faster to search.
654
00:35:29,161 --> 00:35:32,329
Here we're not using zones, okay, so we're just
655
00:35:32,331 --> 00:35:35,565
putting nil as our zone. But if you have a huge data set,
656
00:35:35,567 --> 00:35:39,569
you can look into the zones feature. Okay, so here's my
657
00:35:39,571 --> 00:35:42,772
closure, when the results come back sometime later.
658
00:35:42,774 --> 00:35:46,743
This is the array of records that it found with that query.
659
00:35:46,745 --> 00:35:50,680
And here's the error if any that it found. Okay? Now,
660
00:35:50,682 --> 00:35:54,951
what am I gonna do with these records that came back? Well,
661
00:35:54,953 --> 00:35:58,822
if the records is not nil, in other words,
662
00:35:58,824 --> 00:36:03,426
if it found any records, then I just need to set my
663
00:36:03,428 --> 00:36:07,631
allQandAs equal to those records. All right?
664
00:36:07,633 --> 00:36:12,602
But be careful. This is not happening on the main queue.
665
00:36:12,604 --> 00:36:16,206
So, if we're gonna do this, which is gonna cause this,
666
00:36:16,208 --> 00:36:20,877
reloadData to happen, means we have to dispatc_async onto
667
00:36:20,879 --> 00:36:25,782
the main queue. Okay?
668
00:36:25,784 --> 00:36:27,584
So don't forget that dispatching back to the main
669
00:36:27,586 --> 00:36:32,889
queue when you're gonna do UI stuff as a response. Okay? So
670
00:36:32,891 --> 00:36:34,591
that's it. Okay, super easy.
671
00:36:34,593 --> 00:36:38,528
The only thing we have left to do in this TableViewController
672
00:36:38,530 --> 00:36:40,697
is our UITableViewDataSource. So
673
00:36:40,699 --> 00:36:45,769
let's mark UITableViewDataSource.
674
00:36:45,771 --> 00:36:48,205
Okay, we don't have any sections in this table, okay,
675
00:36:48,207 --> 00:36:52,943
it's just all just one big section. The number of rows,
676
00:36:52,945 --> 00:36:56,413
okay, let's do the number of rows.
677
00:36:56,415 --> 00:37:00,116
That's just the number of things in our array,
678
00:37:00,118 --> 00:37:04,688
AllQandAs.count. That's how many rows we have, obviously.
679
00:37:04,690 --> 00:37:07,157
And then we also obviously need cellForRowAtIndexPath.
680
00:37:07,159 --> 00:37:11,561
cellForRowAtIndexPath. So cellForRowAtIndexPath, which
681
00:37:11,563 --> 00:37:14,831
we have, well, here, let's make it a little wider here.
682
00:37:14,833 --> 00:37:15,799
Okay, cellForRowAtIndexPath.
683
00:37:15,801 --> 00:37:22,038
I'm gonna get the cell by using the tableViews.dequeue,
684
00:37:22,040 --> 00:37:26,276
whoops, view's dequeue with identifier.
685
00:37:26,278 --> 00:37:30,180
So what identifier should we use? Let's say a QandA Cell.
686
00:37:30,182 --> 00:37:35,318
So let's go back to our storyboard. And
687
00:37:35,320 --> 00:37:39,789
have the cells of this. First of all, let's put our table
688
00:37:39,791 --> 00:37:42,892
view in here. How about that? So here's a table view.
689
00:37:42,894 --> 00:37:45,595
This is gonna be in between these two. All right.
690
00:37:45,597 --> 00:37:48,131
This one is going to be the root view controller, and we
691
00:37:48,133 --> 00:37:51,101
are going to be segueing from this one to this one which
692
00:37:51,103 --> 00:37:54,170
shows them. Let's make sure we set our identity of this to be
693
00:37:54,172 --> 00:37:58,642
our new allQandAs table here. Here's our cells right here.
694
00:37:58,644 --> 00:38:01,378
So these cells, we're gonna have them be basic style,
695
00:38:01,380 --> 00:38:03,013
cuz they just have the question.
696
00:38:03,015 --> 00:38:04,114
We're just gonna put the question, so
697
00:38:04,116 --> 00:38:04,714
they don't have any subtitle.
698
00:38:04,716 --> 00:38:07,083
They don't need any custom stuff. And
699
00:38:07,085 --> 00:38:12,122
the identifier is the QandA Cell. Okay,
700
00:38:12,124 --> 00:38:15,058
hey, while were here, let's go ahead do the segues for this.
701
00:38:15,060 --> 00:38:17,861
It's really simple. We're gonna segue from these rows.
702
00:38:17,863 --> 00:38:19,863
Okay, anytime someone clicks on these rows,
703
00:38:19,865 --> 00:38:22,098
we're gonna segue over to here and do a show segue.
704
00:38:22,100 --> 00:38:25,969
And we also want to be able to create new questions as well,
705
00:38:25,971 --> 00:38:28,271
right? We're gonna click on questions we got,
706
00:38:28,273 --> 00:38:29,205
but we want to create new ones.
707
00:38:29,207 --> 00:38:31,741
So I'm gonna do that with some UI in here.
708
00:38:31,743 --> 00:38:36,446
I'm gonna create a Bar Button Item right here. And
709
00:38:36,448 --> 00:38:38,982
I'll use the standard plus one, which is add, okay,
710
00:38:38,984 --> 00:38:41,017
cuz we're gonna add something, this is plus.
711
00:38:41,019 --> 00:38:43,219
And then we're just gonna Ctrl+drag from here and
712
00:38:43,221 --> 00:38:47,223
make that show. Okay, so this is gonna do a segue, if fact,
713
00:38:47,225 --> 00:38:48,124
they can do the same segue,
714
00:38:48,126 --> 00:38:50,126
cuz they pretty much do the same thing.
715
00:38:50,128 --> 00:38:51,227
They show a Q and A, it's just,
716
00:38:51,229 --> 00:38:53,863
one of them shows whatever row is selected and
717
00:38:53,865 --> 00:38:55,432
the other one creates a new one. So
718
00:38:55,434 --> 00:38:58,468
we'll call this Show QandA. That's a good name for
719
00:38:58,470 --> 00:39:03,506
that. And we'll call this one also Show QandA. Now,
720
00:39:03,508 --> 00:39:05,475
in our prepare for segue, we're gonna have to be,
721
00:39:05,477 --> 00:39:08,044
do a little bit different thing, in those two cases,
722
00:39:08,046 --> 00:39:11,481
but that's basically what we're doing. Okay? So
723
00:39:11,483 --> 00:39:16,586
let's go back to our code here. allQandAs.
724
00:39:16,588 --> 00:39:21,391
So here's our cell index path there. Let's
725
00:39:21,393 --> 00:39:26,629
just have our cell's textLabel?.text equal what?
726
00:39:26,631 --> 00:39:30,233
allQandAs sub indexPath.row.
727
00:39:30,235 --> 00:39:34,371
That's giving me the CKRecord. Now, out of that CKRecord,
728
00:39:34,373 --> 00:39:37,974
I've gotta get the Cloud.Attribute.Question,
729
00:39:37,976 --> 00:39:40,543
cuz that's what I'm gonna show,
730
00:39:40,545 --> 00:39:41,811
inside that row. And of course,
731
00:39:41,813 --> 00:39:44,080
we know that comes back as a CKRecord value, so
732
00:39:44,082 --> 00:39:50,153
I have to say, as a String. Okay? And then we will
733
00:39:50,155 --> 00:39:55,392
return the cell. Okay? Also super simple.
734
00:39:55,394 --> 00:39:57,660
All right, make sense? And now let's prepare for segue.
735
00:39:57,662 --> 00:40:02,332
So that's mark our Navigation. Prepare for
736
00:40:02,334 --> 00:40:06,403
segue. This is also gonna be pretty straightforward. First,
737
00:40:06,405 --> 00:40:11,141
we're gonna see if the identifier is our
738
00:40:11,143 --> 00:40:15,845
Show QandA. If it is, let's get that MVC.
739
00:40:15,847 --> 00:40:20,049
So we'll say, if we can let the cloud Q and, QandA,
740
00:40:20,051 --> 00:40:24,788
oops, CloudKit Q and A table view controller equal our
741
00:40:24,790 --> 00:40:28,224
segue's destinationViewController as
742
00:40:28,226 --> 00:40:31,060
a CloudQandATableViewController,
743
00:40:31,062 --> 00:40:34,898
okay, so we're segueing into one of those, so
744
00:40:34,900 --> 00:40:39,235
that better be on the other end. If that's true,
745
00:40:39,237 --> 00:40:41,971
then we need the cell, if we're segueing from a row,
746
00:40:41,973 --> 00:40:45,475
we need the cell, so let's try that. So if we can let cell
747
00:40:45,477 --> 00:40:49,846
equal the sender as a UITableViewCell, okay,
748
00:40:49,848 --> 00:40:52,549
hopefully you guys are used to doing this, again, from your
749
00:40:52,551 --> 00:40:56,553
homework assignment. And then we can let the indexPath equal
750
00:40:56,555 --> 00:41:01,324
the tableView's method cell for, or sorry, index path,
751
00:41:01,326 --> 00:41:05,995
PathForCell, this cell.
752
00:41:05,997 --> 00:41:09,232
Okay, so here is kind of a one-liner that gets the index
753
00:41:09,234 --> 00:41:14,237
path of the row that was chosen. And if that works,
754
00:41:14,239 --> 00:41:19,342
then we can just say that the CloudQandA, oops,
755
00:41:19,344 --> 00:41:22,178
I guess I called it ckQandATVC,
756
00:41:22,180 --> 00:41:27,550
we'll set its public API, which is ckQandARecord,
757
00:41:27,552 --> 00:41:32,288
okay, equal to allQandAs sub indexPath.row.
758
00:41:32,290 --> 00:41:36,259
All right? That's what's, that's the record that
759
00:41:36,261 --> 00:41:38,962
was chosen there, obviously. If we can't get the cell,
760
00:41:38,964 --> 00:41:42,031
if we can't get the table view cell, that must be because we
761
00:41:42,033 --> 00:41:45,201
pressed the + button, right? Because the plus button's not
762
00:41:45,203 --> 00:41:47,170
a row in a table. So in that case,
763
00:41:47,172 --> 00:41:51,608
I'm just going to set the ckQandARecord equal to a new
764
00:41:51,610 --> 00:41:56,646
record, because that's what we want. Creating a new
765
00:41:56,648 --> 00:42:01,718
thing here, so that's Cloud.Entity.QandA.
766
00:42:01,720 --> 00:42:05,989
Okay, everyone understand that prepareForSegue?
767
00:42:05,991 --> 00:42:07,724
Okay, so let's do that.
768
00:42:17,035 --> 00:42:18,701
All right, so here's it showing all the ones, and
769
00:42:18,703 --> 00:42:20,937
look, it's even showing, what is your favorite team,
770
00:42:20,939 --> 00:42:22,005
that we already had from before.
771
00:42:22,007 --> 00:42:26,109
I'm gonna touch on that, and it's not showing it to us.
772
00:42:26,111 --> 00:42:31,247
Okay. Why is it not showing it to us? When we click on this,
773
00:42:31,249 --> 00:42:33,483
it shows a blank one. Why does it show a blank one?
774
00:42:33,485 --> 00:42:37,153
Because over here, in our CloudTableViewController,
775
00:42:37,155 --> 00:42:40,223
look what we do in our viewDidLoad.
776
00:42:40,225 --> 00:42:42,458
No! We always set the record to be nil.
777
00:42:42,460 --> 00:42:47,397
So, let's get rid of that. And allow the segue to actually
778
00:42:47,399 --> 00:42:51,901
set it. That's a tricky little bug.
779
00:43:00,812 --> 00:43:02,378
Okay, so now, when we click on this,
780
00:43:02,380 --> 00:43:08,084
we get to see the question out of the database. Okay? And
781
00:43:08,086 --> 00:43:14,691
we can hit + and create a new one,
782
00:43:14,693 --> 00:43:19,462
maybe like, what is your
783
00:43:19,464 --> 00:43:24,367
favorite Stanford class?
784
00:43:24,369 --> 00:43:29,572
Okay, and we'll have some answers here,
785
00:43:29,574 --> 00:43:34,110
how about CS193p, or if not that,
786
00:43:34,112 --> 00:43:40,283
how about iOS development. Okay,
787
00:43:40,285 --> 00:43:47,223
that's a great class too. And how about the iPhone and
788
00:43:47,225 --> 00:43:52,228
iPad programming course.
789
00:43:52,230 --> 00:43:54,063
You can pick whatever Stanford class you'd like.
790
00:43:54,065 --> 00:43:59,302
Okay? So that, when we go back now, it's gonna ask for all of
791
00:43:59,304 --> 00:44:02,805
those Q&As and it's gonna show that one as well. Okay? So
792
00:44:02,807 --> 00:44:05,842
that you can see, very little code we had to write here to
793
00:44:05,844 --> 00:44:09,779
have it so that we're creating these things and editing them.
794
00:44:09,781 --> 00:44:14,317
What about deleting them? Okay? What if I said, eh,
795
00:44:14,319 --> 00:44:15,752
I don't want this one anymore, I wanna delete it.
796
00:44:15,754 --> 00:44:18,788
What would be really cool is if I could just swipe on this
797
00:44:18,790 --> 00:44:20,490
to delete. You've seen that in table views, right?
798
00:44:20,492 --> 00:44:23,459
Where you can swipe to delete, okay? How do we do swipe,
799
00:44:23,461 --> 00:44:26,095
swipe to delete? We have never covered that in class.
800
00:44:26,097 --> 00:44:27,463
This is a great opportunity to do that.
801
00:44:27,465 --> 00:44:28,798
So if you want to do swipe to delete,
802
00:44:28,800 --> 00:44:32,135
you have to implement two of your UITableViewDataSource
803
00:44:32,137 --> 00:44:35,371
methods. One is, you have to answer whether a row
804
00:44:35,373 --> 00:44:37,807
can be deleted or not. Okay? That one is called
805
00:44:37,809 --> 00:44:42,912
canEditRowAtIndexPath. Okay, canEditRowAtIndexPath.
806
00:44:42,914 --> 00:44:46,649
Now, in our case, as long as we created this, then we can
807
00:44:46,651 --> 00:44:49,152
allow it to delete it, but if we didn't create it, we can't.
808
00:44:49,154 --> 00:44:55,058
So here I'm gonna return all Q&As at the indexPath.row was
809
00:44:55,060 --> 00:44:58,828
created by this user. So if it was created by this user,
810
00:44:58,830 --> 00:45:02,632
then I'll allow you to delete it. Okay? The other method you
811
00:45:02,634 --> 00:45:05,001
need is the thing that actually commits the delete,
812
00:45:05,003 --> 00:45:09,338
that does the delete. That's called commitEditingStyle.
813
00:45:09,340 --> 00:45:11,407
Okay, commitEditingStyle right here,
814
00:45:11,409 --> 00:45:14,477
TableView.commitEditingStyle. And so here,
815
00:45:14,479 --> 00:45:17,246
there's actually two editing styles that you can have.
816
00:45:17,248 --> 00:45:18,715
One is deleting and one is inserting.
817
00:45:18,717 --> 00:45:21,417
Well, we do our inserting with that plus, so we don't need
818
00:45:21,419 --> 00:45:25,021
the inserting style there. We only want the deleting, so
819
00:45:25,023 --> 00:45:27,957
I'm gonna say, if the editingStyle == .Delete.
820
00:45:27,959 --> 00:45:32,895
Okay, so if the person wants to delete my row or whatever,
821
00:45:32,897 --> 00:45:36,532
then all I need to do is delete it from the table. But
822
00:45:36,534 --> 00:45:40,436
I also wanna delete it from the database as well, right,
823
00:45:40,438 --> 00:45:44,107
both of those. So what's that look like? So let's say,
824
00:45:44,109 --> 00:45:47,376
if we're deleting, then I'm gonna let the record to delete
825
00:45:47,378 --> 00:45:51,948
equal, my allQandAs indexPath.row.
826
00:45:51,950 --> 00:45:56,686
Okay. Then I'm gonna save the database. Please delete this
827
00:45:56,688 --> 00:46:02,191
record with an ID, and the ID is the record's recordID,
828
00:46:02,193 --> 00:46:05,428
okay. Completion handler here to handle errors.
829
00:46:05,430 --> 00:46:09,999
This is the deleted record, if it happened,
830
00:46:10,001 --> 00:46:14,070
or error if it didn't. And so, handle errors. I'm, again,
831
00:46:14,072 --> 00:46:18,908
short on time. So we're not going to do that. And
832
00:46:18,910 --> 00:46:21,744
in either case, even if it didn't, you know,
833
00:46:21,746 --> 00:46:25,081
even if it failed to delete, I'm still gonna go ahead and
834
00:46:25,083 --> 00:46:29,952
delete it out of allQandAs. So to do that, I just say
835
00:46:29,954 --> 00:46:36,225
allQandAs.removeAtIndex the indexPath.row.
836
00:46:36,227 --> 00:46:38,728
Okay, so it's just gonna delete it from the thing. Now,
837
00:46:38,730 --> 00:46:42,131
hopefully, this was successful and it won't come back later,
838
00:46:42,133 --> 00:46:45,802
but it's gonna delete here. So let's take a look at this.
839
00:46:53,645 --> 00:46:55,845
All right, so we created both of these, so
840
00:46:55,847 --> 00:46:58,881
I can swipe to delete, right, either one I wanted.
841
00:46:58,883 --> 00:47:01,284
So let's delete, let's delete this one.
842
00:47:01,286 --> 00:47:04,687
It's gone. Okay, and we can make sure it actually deleted
843
00:47:04,689 --> 00:47:06,322
it in the database by going away and coming back,
844
00:47:06,324 --> 00:47:09,225
cuz it reloads when it does that. It didn't come back, so
845
00:47:09,227 --> 00:47:10,693
it actually deleted that from the database.
846
00:47:10,695 --> 00:47:13,663
We could also go over and look in our database right here,
847
00:47:13,665 --> 00:47:15,598
in our default zone. And we can see, we only have,
848
00:47:15,600 --> 00:47:19,836
what is your favorite team. We could reload just to be 100%
849
00:47:19,838 --> 00:47:22,238
sure. Default zone. Sure enough, we only have,
850
00:47:22,240 --> 00:47:26,008
what is your favorite team? It deleted it. Okay? All right,
851
00:47:26,010 --> 00:47:29,178
so that's great. All right. One bad thing about our UI,
852
00:47:29,180 --> 00:47:32,415
though is, you see this nice little table view? If I'm just
853
00:47:32,417 --> 00:47:36,085
sitting here and someone else creates something, it doesn't
854
00:47:36,087 --> 00:47:40,790
show me. So let's go over here on iPhone simulator here,
855
00:47:40,792 --> 00:47:42,992
and I'm gonna create a new one.
856
00:47:42,994 --> 00:47:48,197
I'm gonna see what happens. Okay, so it's showing,
857
00:47:48,199 --> 00:47:51,634
what is your favorite team? So I'll create a new one here,
858
00:47:51,636 --> 00:47:54,270
I'll say, what is your favorite color?
859
00:47:54,272 --> 00:48:00,142
That's our kind of our favorite one. Red and Blue.
860
00:48:00,144 --> 00:48:04,213
And black, okay? Now, I've created this but it's not
861
00:48:04,215 --> 00:48:07,049
showing up over here, right? And it's not showing up there
862
00:48:07,051 --> 00:48:10,286
because I'm not subscribing to that change. I'm not watching
863
00:48:10,288 --> 00:48:14,523
the database. Now, if I go away from this and come back,
864
00:48:14,525 --> 00:48:16,726
it is gonna show it. Because when we viewWillAppear,
865
00:48:16,728 --> 00:48:19,495
we reload from the database. But it's really annoying for
866
00:48:19,497 --> 00:48:21,364
the user to always to have to go away and
867
00:48:21,366 --> 00:48:23,566
come back to the see the latest, thing. So,
868
00:48:23,568 --> 00:48:27,136
we need to do a subscription, okay? Perfect opportunity to
869
00:48:27,138 --> 00:48:29,705
do a subscription. So let's do that.
870
00:48:29,707 --> 00:48:34,243
So, how do we do the subscription? Pretty simple,
871
00:48:34,245 --> 00:48:36,746
it's two parts really, three parts to doing it.
872
00:48:36,748 --> 00:48:39,181
The first one is we need to actually subscribe.
873
00:48:39,183 --> 00:48:40,549
So let's do the subscription first.
874
00:48:40,551 --> 00:48:45,922
I'll even put this in a little MARK: Subscription place here.
875
00:48:45,924 --> 00:48:50,159
One thing is we need to have an ID for our subscription.
876
00:48:50,161 --> 00:48:51,193
So, I'm gonna say subscriptionID.
877
00:48:51,195 --> 00:48:54,430
And this is just an English language description of what
878
00:48:54,432 --> 00:48:57,400
we're gonna subscribe to. And so I'm gonna say,
879
00:48:57,402 --> 00:49:00,603
we're gonna get All QandA Creations and Deletions.
880
00:49:00,605 --> 00:49:04,407
And this has gotta uniquely identify this subscription in
881
00:49:04,409 --> 00:49:04,540
the database and
882
00:49:04,542 --> 00:49:07,777
I think that pretty uniquely identifies what we're doing
883
00:49:07,779 --> 00:49:12,515
here. So, let me create a private func here
884
00:49:12,517 --> 00:49:16,652
called iCloudSubscribeToQandAs, okay?
885
00:49:16,654 --> 00:49:19,622
And this thing is going to do the subscription. So
886
00:49:19,624 --> 00:49:22,858
when you do a subscription you need a predicate that says,
887
00:49:22,860 --> 00:49:23,993
what you're looking for, okay?
888
00:49:23,995 --> 00:49:27,430
What you're trying to find out when it changes. So again,
889
00:49:27,432 --> 00:49:31,233
we're gonna use pretty much the same one we used before
890
00:49:31,235 --> 00:49:34,337
which is true predicate, TRUEPREDICATE, yeah.
891
00:49:34,339 --> 00:49:37,239
Because, we are looking for any additions or
892
00:49:37,241 --> 00:49:40,543
deletions of Q and A, not, not just the ones we created, or
893
00:49:40,545 --> 00:49:42,645
not just ones with certain questions but
894
00:49:42,647 --> 00:49:42,812
any of them, so
895
00:49:42,814 --> 00:49:45,314
that's why we're using the same predicate. And then,
896
00:49:45,316 --> 00:49:50,286
we just create the subscription. Okay,
897
00:49:50,288 --> 00:49:57,126
CKSubscription. And, it's, this one right here.
898
00:49:57,128 --> 00:49:59,528
And let's go ahead and put this on separate lines,
899
00:49:59,530 --> 00:50:03,833
as I am wont to do when there are lots of arguments so
900
00:50:03,835 --> 00:50:07,136
you can see what's going on, okay? So the recordType is,
901
00:50:07,138 --> 00:50:09,872
what kind of thing we're watching, and of course here,
902
00:50:09,874 --> 00:50:13,075
we are watching Entity.QandA. That's what we're looking for
903
00:50:13,077 --> 00:50:18,447
searches for. predicate: is our predicate, okay.
904
00:50:18,449 --> 00:50:20,483
subscriptionID: is our subscriptionID.
905
00:50:20,485 --> 00:50:23,452
I'm putting self in there just to emphasize that
906
00:50:23,454 --> 00:50:24,353
it's my subscription ID,
907
00:50:24,355 --> 00:50:26,789
okay? And then, the subscription options: are what
908
00:50:26,791 --> 00:50:29,658
kind of changes are we looking for? So we want ones that
909
00:50:29,660 --> 00:50:34,263
fire when one's created, and we also want to find out when
910
00:50:34,265 --> 00:50:37,867
one's deleted. Cuz we're showing a list of the suf,
911
00:50:37,869 --> 00:50:40,403
of the Q and As, so we don't care if one gets
912
00:50:40,405 --> 00:50:43,339
updated. Although, we might care if it's updated because
913
00:50:43,341 --> 00:50:44,573
maybe it changed the question and
914
00:50:44,575 --> 00:50:45,474
now the list should change there.
915
00:50:45,476 --> 00:50:48,544
But I'm just gonna do creation and deletion here, to show you
916
00:50:48,546 --> 00:50:51,981
that. Now, one thing here that I'm not gonna do, but
917
00:50:51,983 --> 00:50:56,552
that you can do here, is there is a notification info
918
00:50:56,554 --> 00:51:00,089
argument, or, var on subscription that you can use
919
00:51:00,091 --> 00:51:04,460
to say, what happens when the push notification comes, when
920
00:51:04,462 --> 00:51:06,662
this changes. Does it put up an alert? Does it put a badge?
921
00:51:06,664 --> 00:51:10,533
Does it increase the badge, right? On your app icon, okay?
922
00:51:10,535 --> 00:51:13,169
Does it play a sound? Bloop, things arrived. Okay, you can
923
00:51:13,171 --> 00:51:16,138
set that here. I'm gonna have none of those things happening
924
00:51:16,140 --> 00:51:18,674
I'm just going to behind the scenes fix up the table.
925
00:51:18,676 --> 00:51:20,309
I'm not gonna put any alerts up or whatever.
926
00:51:20,311 --> 00:51:23,746
But I just wanna let you know, you can do that here, okay?
927
00:51:23,748 --> 00:51:27,349
So, we got that, so let's just go to our database and
928
00:51:27,351 --> 00:51:29,652
tell it to save this subscription, okay.
929
00:51:29,654 --> 00:51:33,322
Save our subscription with our completion handler right here,
930
00:51:33,324 --> 00:51:36,292
okay. Here is the savedSubscription,
931
00:51:36,294 --> 00:51:39,695
if it succeeded, and here's an error if not.
932
00:51:39,697 --> 00:51:42,698
I'm gonna take a second to show you an error here because
933
00:51:42,700 --> 00:51:48,137
there's a common one that happens. error?.code ==
934
00:51:48,139 --> 00:51:55,010
CKErrorCode.ServerRejectedReq- uest,
935
00:51:55,012 --> 00:51:58,581
okay? And what happens here sometimes is,
936
00:51:58,583 --> 00:52:01,417
if this subscription by its unique name is already on
937
00:52:01,419 --> 00:52:05,054
the server, then it will reject your request, okay?
938
00:52:05,056 --> 00:52:08,157
So, this usually means that you ran your program,
939
00:52:08,159 --> 00:52:10,626
you were developing, you, this code ran,
940
00:52:10,628 --> 00:52:13,162
and then you hit stop in the debugger, okay?
941
00:52:13,164 --> 00:52:15,664
It killed your app. And then you ran it again, and
942
00:52:15,666 --> 00:52:18,434
it tried to do it, it's still there. Okay, the subscriptions
943
00:52:18,436 --> 00:52:21,637
live forever, until you actually remove them, okay?
944
00:52:21,639 --> 00:52:24,773
So quitting your app has no effect on the subscriptions.
945
00:52:24,775 --> 00:52:27,843
So, this is one where usually you'll ignore that
946
00:52:27,845 --> 00:52:29,979
ServerRejectedRequest figuring eh, somehow,
947
00:52:29,981 --> 00:52:33,549
this thing got got already put in there. That's fine,
948
00:52:33,551 --> 00:52:36,218
we'll just use it, okay, it'll continue to work just fine.
949
00:52:36,220 --> 00:52:39,522
Okay and else, you know, if there's some error,
950
00:52:39,524 --> 00:52:40,823
other kind of error, you know,
951
00:52:40,825 --> 00:52:44,226
report that error or whatever you feel like you need to do.
952
00:52:44,228 --> 00:52:48,597
Okay, so that's it, that's all that's necessary to get iCloud
953
00:52:48,599 --> 00:52:53,102
to start, you know, sending us push notifications when,
954
00:52:53,104 --> 00:52:56,839
any of these creations and deletions happen to any Q and
955
00:52:56,841 --> 00:52:59,208
A. Now, one thing is we're subscribing here.
956
00:52:59,210 --> 00:53:02,578
We also are probably want to unsubscribed and
957
00:53:02,580 --> 00:53:04,547
when would we want to do those two things.
958
00:53:04,549 --> 00:53:08,918
So I'm gonna have an unsubscribe as well,
959
00:53:08,920 --> 00:53:12,087
Unsubscribe. And when do we wanna do this?
960
00:53:12,089 --> 00:53:14,790
Probably, we'll want to, when viewWillAppear,
961
00:53:14,792 --> 00:53:16,825
we wanna subscribe and when viewDisappears,
962
00:53:16,827 --> 00:53:19,762
we unsubscribe. Okay, because it's no use sending these push
963
00:53:19,764 --> 00:53:21,163
notifications if we're not even on screen.
964
00:53:21,165 --> 00:53:24,233
Okay, especially since when we reappear we reload ourselves
965
00:53:24,235 --> 00:53:28,070
anyway. Okay, so there's no reason to do that. So,
966
00:53:28,072 --> 00:53:31,473
in viewWillAppear right here,
967
00:53:31,475 --> 00:53:36,645
I'm gonna say, iCloudSubscribeToQandAs,
968
00:53:36,647 --> 00:53:41,784
and in viewDidDisappear, I'm gonna say,
969
00:53:41,786 --> 00:53:45,821
iCloudUnsubscribe, okay? Now,
970
00:53:45,823 --> 00:53:48,991
iCloudUnsubscribe, what do we need to do to unsubscribe?
971
00:53:48,993 --> 00:53:53,295
Very easy, I'm just gonna say to my database, please
972
00:53:53,297 --> 00:53:57,199
deleteSubscriptionWithID, my subscriptionID,
973
00:53:57,201 --> 00:54:01,537
okay? Completion handler to handle errors here. So
974
00:54:01,539 --> 00:54:05,407
this is, the subscription that we tried to delete.
975
00:54:05,409 --> 00:54:08,344
There's an error. We will not handle errors cuz we're
976
00:54:08,346 --> 00:54:11,947
running out of time. Handle it, okay? So, we'll just
977
00:54:11,949 --> 00:54:16,518
delete those, descriptions there and that's it. Okay, so
978
00:54:16,520 --> 00:54:20,489
now we've set up our table here to get those updates but
979
00:54:20,491 --> 00:54:23,158
of course, we have to be able to receive push notifications
980
00:54:23,160 --> 00:54:26,762
for this to work, okay? And we do that, over in our app
981
00:54:26,764 --> 00:54:29,898
delegate. So lets go back to our app delegate right here,
982
00:54:29,900 --> 00:54:32,067
and look at that code that we covered in lecture.
983
00:54:32,069 --> 00:54:34,436
One, in did finish launching with options,
984
00:54:34,438 --> 00:54:36,171
we need to register for
985
00:54:36,173 --> 00:54:38,307
the kinds of notifications
986
00:54:38,309 --> 00:54:40,242
we're willing to present to our user, okay?
987
00:54:40,244 --> 00:54:43,112
And the user actually can choose in settings,
988
00:54:43,114 --> 00:54:46,248
the general settings, whether they want alerts or badges or
989
00:54:46,250 --> 00:54:48,917
sounds or whatever, but we're going to explain here
990
00:54:48,919 --> 00:54:52,454
which ones we think we want to give them, okay? So,
991
00:54:52,456 --> 00:54:56,859
we do that by just creating the settings, by that's
992
00:54:56,861 --> 00:55:01,430
a UIUserNotificationSettings, and for
993
00:55:01,432 --> 00:55:04,733
the types that we want. And this can be like alerts,
994
00:55:04,735 --> 00:55:09,405
badges, sounds, okay? And I'll go ahead and register for
995
00:55:09,407 --> 00:55:11,874
all of them, although, I'm not gonna use any of them cuz I,
996
00:55:11,876 --> 00:55:14,043
that subscription.notification info, remember that?
997
00:55:14,045 --> 00:55:16,912
I didn't put that line in, so it's not gonna do any of these
998
00:55:16,914 --> 00:55:20,215
things. But, I, I'm gonna tell the user, hey, I wanna do all
999
00:55:20,217 --> 00:55:22,818
these things cuz maybe some of my other push notifications
1000
00:55:22,820 --> 00:55:25,854
wanted to do it or whatever. But I wouldn't sign up for
1001
00:55:25,856 --> 00:55:28,490
these actually if you're not gonna use them cuz it just
1002
00:55:28,492 --> 00:55:32,861
creates unnecessary stuff for your user, all right?
1003
00:55:32,863 --> 00:55:35,297
So there's the settings, so now I have to register
1004
00:55:35,299 --> 00:55:39,068
those settings, so I just say application.registerUserNotif-
1005
00:55:39,070 --> 00:55:40,669
icationSettings, those settings.
1006
00:55:40,671 --> 00:55:44,807
And now, I need to turn on push notifications, okay?
1007
00:55:44,809 --> 00:55:47,810
registerForRemoteNotificati- ons, okay?
1008
00:55:47,812 --> 00:55:51,013
Push notifications are called remote notifications, okay?
1009
00:55:51,015 --> 00:55:53,449
Boom, so now I've turned on push notifications,
1010
00:55:53,451 --> 00:55:54,850
they're gonna start coming, okay?
1011
00:55:54,852 --> 00:55:58,187
From Cloudkit, or whatever else ones that I set up.
1012
00:55:58,189 --> 00:56:02,191
So now I have to handle them when they arrive, and I do
1013
00:56:02,193 --> 00:56:06,595
that with this thing called the remote notification,
1014
00:56:06,597 --> 00:56:09,498
didReceiveRemoteNotification, okay?
1015
00:56:09,500 --> 00:56:12,301
A remote notification came in, what am I gonna do with it?
1016
00:56:12,303 --> 00:56:15,671
Well, I'm gonna assume that it's a CloudKit one here.
1017
00:56:15,673 --> 00:56:17,740
I don't have any other ones that I'm expecting, so
1018
00:56:17,742 --> 00:56:19,641
that should be fine. If I had other ones,
1019
00:56:19,643 --> 00:56:23,011
I'd have to do more code here. But I'm just gonna say,
1020
00:56:23,013 --> 00:56:27,383
if I can, or actually, I mean, yeah, if I'm gonna let CQ,
1021
00:56:27,385 --> 00:56:31,453
ckqn, that's short for CloudKit query notification,
1022
00:56:31,455 --> 00:56:35,357
equals CKQueryNotification, CKQueryNotification,
1023
00:56:35,359 --> 00:56:40,996
okay, that's not completing because, import CloudKit.
1024
00:56:41,866 --> 00:56:43,699
Okay, so I'm gonna create a query notification.
1025
00:56:43,701 --> 00:56:47,169
The only way to create one of these things is to hand
1026
00:56:47,171 --> 00:56:51,707
the dictionary that comes with the push notification off to
1027
00:56:51,709 --> 00:56:55,444
this constructor, okay, this initializer.
1028
00:56:55,446 --> 00:56:56,178
So I'm gonna give it that.
1029
00:56:56,180 --> 00:56:58,914
One thing that's unfortunate about that is that it wants
1030
00:56:58,916 --> 00:57:03,952
that to be a dictionary where Strings are the keys and
1031
00:57:03,954 --> 00:57:05,387
NSObjects are the values,
1032
00:57:05,389 --> 00:57:09,258
instead of NSObjects as the keys, AnyObject as the value.
1033
00:57:09,260 --> 00:57:12,995
Okay? So, you just have to do this. All right, so
1034
00:57:12,997 --> 00:57:17,132
I've got my query notification that came from CloudKit.
1035
00:57:17,134 --> 00:57:20,369
Awesome. Now I need to hand it out to all my view controllers
1036
00:57:20,371 --> 00:57:22,104
that might be interested. And as we said in lecture,
1037
00:57:22,106 --> 00:57:24,273
we're gonna do this with a radio station. Okay, so
1038
00:57:24,275 --> 00:57:30,612
let's create a radio station. notification = NSNotification,
1039
00:57:30,614 --> 00:57:35,984
all right, we're gonna use this one right here. Again,
1040
00:57:35,986 --> 00:57:41,323
I will help you out a little bit. See what's going on.
1041
00:57:42,159 --> 00:57:44,993
All right, so name is the name of the radio station, and
1042
00:57:44,995 --> 00:57:47,062
as I told you, in that CloudKit extensions,
1043
00:57:47,064 --> 00:57:50,365
I created something for that. So I called that
1044
00:57:50,367 --> 00:57:54,803
CloudKitNotifications.notific- ation,
1045
00:57:54,805 --> 00:57:58,440
what did I call it? So I have to go back and look here.
1046
00:57:58,442 --> 00:58:02,444
Supporting Files, extension, CloudKitNotification.
1047
00:58:02,446 --> 00:58:05,848
NotificationReceived. That's the name of my radio station,
1048
00:58:05,850 --> 00:58:09,017
NotificationReceived. Sorry.
1049
00:58:13,724 --> 00:58:17,926
NotificationReceived. Okay, so that's the name. AnyObject,
1050
00:58:17,928 --> 00:58:19,928
this object is the sender. Ha, that's self,
1051
00:58:19,930 --> 00:58:22,064
that's my AppDelegate. Doesn't really matter too much,
1052
00:58:22,066 --> 00:58:24,299
cuz I'm not gonna look at it in the other end. And
1053
00:58:24,301 --> 00:58:28,337
then the userInfo, I need to pass this ckqn on.
1054
00:58:28,339 --> 00:58:30,839
So I'm gonna create a little dictionary on the fly here.
1055
00:58:30,841 --> 00:58:35,143
And the key is gonna be my CloudKitNotifications.Notific-
1056
00:58:35,145 --> 00:58:41,216
ationKey. And the value is gonna be that ckqn. Okay?
1057
00:58:41,218 --> 00:58:44,753
So this is the thing I'm gonna pass out onto, this is
1058
00:58:44,755 --> 00:58:48,957
Received, this is the thing I'm gonna pass out on my radio
1059
00:58:48,959 --> 00:58:53,462
station. So let's go ahead and broadcast it by saying
1060
00:58:53,464 --> 00:58:59,935
NSNotificationCenter.defaultC- enter, post this notification,
1061
00:58:59,937 --> 00:59:03,705
that means broadcast on this radio station.
1062
00:59:03,707 --> 00:59:06,875
Okay, now everybody who is signed up to listen is going
1063
00:59:06,877 --> 00:59:11,013
to get this push notification forwarded onto them. Okay?
1064
00:59:11,015 --> 00:59:15,484
So now let's go back and sign up over here to get that.
1065
00:59:15,486 --> 00:59:16,718
And I'm gonna do it in my subscribe,
1066
00:59:16,720 --> 00:59:19,054
when I subscribe to the Q and A's, I'm gonna then go and
1067
00:59:19,056 --> 00:59:22,925
sign up for that radio station so I can hear the results. And
1068
00:59:22,927 --> 00:59:26,061
to do that, I need that little, cookie. So
1069
00:59:26,063 --> 00:59:31,066
private var, we'll call it cloudKitObserver. It's gonna
1070
00:59:31,068 --> 00:59:34,369
be an NSObjectProtocol optional right there.
1071
00:59:34,371 --> 00:59:38,040
Okay, I'm gonna say cloudKitObserver = and
1072
00:59:38,042 --> 00:59:42,311
I'm just gonna ask the notification center if I can
1073
00:59:42,313 --> 00:59:46,181
please listen to that radio station by saying,
1074
00:59:46,183 --> 00:59:52,921
addObserverForName. It's this one. Okay, again.
1075
00:59:55,826 --> 00:59:58,427
I'd love if there was a nice little single key to do that
1076
00:59:58,429 --> 01:00:01,330
for me. So here's the radio station that I wanna listen
1077
01:00:01,332 --> 01:00:04,900
to, which is CloudKitNotifications.Notific-
1078
01:00:04,902 --> 01:00:08,203
ationReceived, that's the name of the radio station.
1079
01:00:08,205 --> 01:00:10,939
AnyObject, I don't care. And if anybody sends me that, I'm
1080
01:00:10,941 --> 01:00:12,975
gonna listen, okay? I could put the AppDelegate here, but
1081
01:00:12,977 --> 01:00:15,677
I'm just gonna say anybody. And I'm gonna do some UI here,
1082
01:00:15,679 --> 01:00:19,715
so I'd better say [INAUDIBLE] NSOperationQueue.mainQueue,
1083
01:00:19,717 --> 01:00:21,850
okay, send me this on the main queue. And
1084
01:00:21,852 --> 01:00:24,186
then here's the block, okay? This argument is
1085
01:00:24,188 --> 01:00:28,490
the notification that's coming along in the radio station.
1086
01:00:28,492 --> 01:00:33,662
And I need to get the ck, ckqn out of the user info
1087
01:00:33,664 --> 01:00:37,466
in that notification. Okay, so what does that look like?
1088
01:00:37,468 --> 01:00:40,168
Actually, I'm gonna do all that in a different method.
1089
01:00:40,170 --> 01:00:44,673
I'm gonna have iCloudHandleSubscriptionNotif-
1090
01:00:44,675 --> 01:00:50,212
ication, and I'm just gonna pass ckqn on to that.
1091
01:00:50,214 --> 01:00:52,648
So the cq, ckqn here, we can say,
1092
01:00:52,650 --> 01:00:57,085
if we can let ckqn equal the notification's userInfo,
1093
01:00:57,087 --> 01:01:01,456
which might be nil, you have to be careful about that,
1094
01:01:01,458 --> 01:01:06,194
and I'm gonna get the CloudKit notification key here,
1095
01:01:06,196 --> 01:01:09,698
notification's NotificationKey, okay.
1096
01:01:09,700 --> 01:01:13,702
And that better be a CKQuery notification, or
1097
01:01:13,704 --> 01:01:15,904
I don't know what to do with it,
1098
01:01:15,906 --> 01:01:19,708
okay? So if I can get that out of the little radio station
1099
01:01:19,710 --> 01:01:23,712
broadcast, then I'm gonna handle it. Okay, so
1100
01:01:23,714 --> 01:01:30,285
how do I handle it? private func this thing,
1101
01:01:30,287 --> 01:01:34,723
okay. And, there, we have a syntax
1102
01:01:34,725 --> 01:01:39,528
error right here, this should be NotificationKey,
1103
01:01:39,530 --> 01:01:42,864
NotificationKey. So handling it is pretty simple,
1104
01:01:42,866 --> 01:01:47,636
actually. First, when handling it, I'm gonna make sure that
1105
01:01:47,638 --> 01:01:52,441
this is for me. I'm gonna look at the subscription,
1106
01:01:52,443 --> 01:01:55,644
sorry, ckqn is a CKQueryNotification.
1107
01:01:55,646 --> 01:01:58,480
Okay, I'm gonna make sure that this thing is for me,
1108
01:01:58,482 --> 01:02:02,184
to make sure its subscription ID equals my subscription ID.
1109
01:02:02,186 --> 01:02:04,386
Because, remember, you might have lots of different view
1110
01:02:04,388 --> 01:02:07,122
controllers getting push notifications from CloudKit,
1111
01:02:07,124 --> 01:02:09,758
okay, doing different things. So here I want to make sure
1112
01:02:09,760 --> 01:02:14,329
it's this one that I'm getting the answer back from.
1113
01:02:14,331 --> 01:02:16,832
All right, so now that I have that, I'm just gonna get
1114
01:02:16,834 --> 01:02:19,835
the record that changed, okay, the recordID that changed
1115
01:02:19,837 --> 01:02:24,473
is ckqn.recordID, okay, so hopefully some record changed,
1116
01:02:24,475 --> 01:02:27,976
deleted or created here, otherwise I wouldn't be here.
1117
01:02:27,978 --> 01:02:32,013
I can also find out what changed. So I can say,
1118
01:02:32,015 --> 01:02:36,084
what is the reason that I got this notification? And
1119
01:02:36,086 --> 01:02:38,120
one reason could be because a record
1120
01:02:38,122 --> 01:02:42,657
was created. Another reason might be cuz a record was
1121
01:02:42,659 --> 01:02:46,361
deleted, okay? Otherwise, I don't care,
1122
01:02:46,363 --> 01:02:48,196
cuz I only handle creations and deletions,
1123
01:02:48,198 --> 01:02:53,502
all right? Let's do deletion first. It's a pretty easy one.
1124
01:02:53,504 --> 01:02:56,705
Okay, so what happens, if something was deleted? Well,
1125
01:02:56,707 --> 01:03:00,208
I'm just gonna dispatc_async back the the main queue and
1126
01:03:00,210 --> 01:03:02,344
remove it from my all QAs thing. So
1127
01:03:02,346 --> 01:03:08,350
dispatc_ge_mai_queue. And I'm just gonna say,
1128
01:03:08,352 --> 01:03:14,055
self.allQandAs equals self.allQandAs,
1129
01:03:14,057 --> 01:03:18,927
and I'm just gonna filter out any records
1130
01:03:18,929 --> 01:03:23,598
where the $0.recordID does not equal
1131
01:03:23,600 --> 01:03:27,669
this recordID that was just deleted.
1132
01:03:27,671 --> 01:03:30,739
Okay, so filter goes through all the things in the array,
1133
01:03:30,741 --> 01:03:31,673
$0 is each thing in the array,
1134
01:03:31,675 --> 01:03:34,543
I'm gonna get the recordID of the thing that's in there.
1135
01:03:34,545 --> 01:03:35,844
And if it's not equal to the recordID,
1136
01:03:35,846 --> 01:03:38,480
then I'm gonna filter it, if it is equal to the record ID,
1137
01:03:38,482 --> 01:03:41,516
then it's not gonna get through the filter.
1138
01:03:41,718 --> 01:03:44,920
Everybody got that? Super simple. Created,
1139
01:03:44,922 --> 01:03:47,155
slightly more difficult, although only slightly.
1140
01:03:47,157 --> 01:03:50,625
So, here, I'm gonna check if I have a record,
1141
01:03:50,627 --> 01:03:53,795
if the record okay, let's, first of all,
1142
01:03:53,797 --> 01:03:57,699
we have the record ID here. To show this record in our,
1143
01:03:57,701 --> 01:04:01,236
in our table, we actually need the record,
1144
01:04:01,238 --> 01:04:01,670
not the record ID.
1145
01:04:01,672 --> 01:04:05,373
By the way, it is possible in our subscription to have said,
1146
01:04:05,375 --> 01:04:08,910
hey when you give this thing, also give me the question.
1147
01:04:08,912 --> 01:04:11,213
In other words, you can ask for certain fields so
1148
01:04:11,215 --> 01:04:13,448
that I don't have to do what I'm doing right now,
1149
01:04:13,450 --> 01:04:16,384
which is I'm going back to my database and
1150
01:04:16,386 --> 01:04:21,790
fetching that record. Database. fetchRecordWithID.
1151
01:04:21,792 --> 01:04:24,626
Which is kind of unfortunate that I have this record ID and
1152
01:04:24,628 --> 01:04:27,395
I'm having to turn around and go back and fetch it.
1153
01:04:27,397 --> 01:04:29,497
So, you don't have to do that if you,
1154
01:04:29,499 --> 01:04:30,665
in your description say just give me
1155
01:04:30,667 --> 01:04:34,502
that right off the bat, okay? You just specify which fields
1156
01:04:34,504 --> 01:04:37,272
you want. Okay. So here's the record coming back, hopefully,
1157
01:04:37,274 --> 01:04:42,510
not nil, and here's the error. Okay. So, if the record
1158
01:04:42,512 --> 01:04:46,815
is not nil, I got it back. Now I need to add it to my Q and
1159
01:04:46,817 --> 01:04:49,784
As, now, a slight complication here is when I put it in my
1160
01:04:49,786 --> 01:04:53,321
list of Q and As, I obviously need to sort it. Because my
1161
01:04:53,323 --> 01:04:55,423
list of Q and As was sorted by the database, but now I'm
1162
01:04:55,425 --> 01:04:58,593
getting this one record that I have to put in the list.
1163
01:04:58,595 --> 01:04:59,060
So, I have to do sorting.
1164
01:04:59,062 --> 01:05:00,362
So, I'll show you a little bit how to do, ahh,
1165
01:05:00,364 --> 01:05:05,400
sorting in here. Ahh, so, I'm gonna dispatc_async back to
1166
01:05:05,402 --> 01:05:10,639
the main queue. Okay.
1167
01:05:10,641 --> 01:05:13,808
And on here, I'm gonna say self.allQandAs, okay?
1168
01:05:13,810 --> 01:05:18,146
Get ready for this, equals the self.allQandAs we got so far,
1169
01:05:18,148 --> 01:05:22,784
plus this record. Okay. So now, this is the new thing
1170
01:05:22,786 --> 01:05:25,320
with it appended on to the end, but the end is no good so
1171
01:05:25,322 --> 01:05:29,257
I'm gonna sort. Okay? And when I sort, sort is
1172
01:05:29,259 --> 01:05:32,727
gonna pass me dollar zero and dollar one. And I have to say,
1173
01:05:32,729 --> 01:05:35,297
if dollar zero is less than or eq, or greater than dollar
1174
01:05:35,299 --> 01:05:38,667
one. And that's how it's gonna sort everything. So, let's go
1175
01:05:38,669 --> 01:05:44,339
ahead and return here. Dollar zero, which is a record,
1176
01:05:44,341 --> 01:05:48,043
a ck record, so I'm gonna get the, Cloud.Attribute.Question,
1177
01:05:48,045 --> 01:05:52,113
cuz I'm comparing the questions, quest- question,
1178
01:05:52,115 --> 01:05:55,750
all right. But of course, I have to do it as a string,
1179
01:05:55,752 --> 01:05:59,387
and I'm gonna see if that's less than $1,
1180
01:05:59,389 --> 01:06:07,228
Cloud.Attribute.Question, also as a String.
1181
01:06:07,230 --> 01:06:10,832
Okay. So, this is nice but
1182
01:06:10,834 --> 01:06:14,602
this is really messy code, it looks absolutely awesome,
1183
01:06:14,604 --> 01:06:16,905
awful here. Wouldn't it be cool if in,
1184
01:06:16,907 --> 01:06:17,872
like we had in core data,
1185
01:06:17,874 --> 01:06:19,641
where we could have the subclass and
1186
01:06:19,643 --> 01:06:22,344
we just use vars for these things? Well,
1187
01:06:22,346 --> 01:06:25,413
we can't quite do that, but watch this little trick.
1188
01:06:25,415 --> 01:06:27,682
I'm going to go back to my supporting files,
1189
01:06:27,684 --> 01:06:30,618
this CloudKit extensions thing that I did over here, okay?
1190
01:06:30,620 --> 01:06:36,124
And I'm going to do this extension to CKRecord
1191
01:06:36,426 --> 01:06:43,431
var question is a String, okay. I'm gonna return self
1192
01:06:43,433 --> 01:06:50,205
sub Cloud.Attribute.Question as a String. And
1193
01:06:50,207 --> 01:06:53,708
in fact, I want this to not be an optional. So I'm gonna say,
1194
01:06:53,710 --> 01:06:59,481
??, empty. So, you see I've added a var question,
1195
01:06:59,483 --> 01:07:02,617
to CkRecord that returns this thing.
1196
01:07:02,619 --> 01:07:07,188
So now back in my other code over here, all this gunk just
1197
01:07:07,190 --> 01:07:11,860
becomes .question. Okay, get rid of all this,
1198
01:07:11,862 --> 01:07:18,767
.question. Okay, looks a lot nicer, doesn't it, okay?
1199
01:07:18,769 --> 01:07:21,236
And in fact, we could use this .question in other places,
1200
01:07:21,238 --> 01:07:23,004
like down here, in cell for row at indexPath,
1201
01:07:23,006 --> 01:07:27,342
we do the same. A big mess right here, ,question.
1202
01:07:27,344 --> 01:07:30,311
Okay. Now, okay, all your attributes would have to
1203
01:07:30,313 --> 01:07:32,947
be unique across all of your entities or you would have to
1204
01:07:32,949 --> 01:07:35,750
start putting the entity names somehow, in, in this name in
1205
01:07:35,752 --> 01:07:38,853
this bar, but you know, something to consider,
1206
01:07:38,855 --> 01:07:40,755
cleans up your code quite a bit.
1207
01:07:40,757 --> 01:07:44,526
All right. Everyone, make sense what I did right there?
1208
01:07:44,528 --> 01:07:47,729
By the way, if this record couldn't be fetched,
1209
01:07:47,731 --> 01:07:52,167
then I'm kind of like, what the heck am I gonna do here?
1210
01:07:52,169 --> 01:07:53,701
So, I might wanna do something here like,
1211
01:07:53,703 --> 01:07:57,005
go reload my whole table, you know, do a fetch all q and
1212
01:07:57,007 --> 01:07:58,940
a's, call fetch, dispatch, async, and all that, but
1213
01:07:58,942 --> 01:08:02,177
I'm not gonna do it cuz we're out of time. Ahm, but, that's,
1214
01:08:02,179 --> 01:08:07,649
I might want to react to this error somehow here. Okay?
1215
01:08:07,651 --> 01:08:09,851
All right, so let's go back and
1216
01:08:09,853 --> 01:08:17,459
let's run it on iPhone. And we'll run it on our device.
1217
01:08:26,570 --> 01:08:33,741
All right. Okay. So let's go and create a new one here.
1218
01:08:33,743 --> 01:08:39,647
Let's try, how do you greet people?
1219
01:08:40,050 --> 01:08:44,886
And the answer is you say Hello, or maybe you say Aloha,
1220
01:08:44,888 --> 01:08:47,489
and you can see as soon as I typed enough to get this thing
1221
01:08:47,491 --> 01:08:51,025
to upload, it appeared here because it got noti, notified
1222
01:08:51,027 --> 01:08:54,629
via the push notification. And in fact, I can look at it,
1223
01:08:54,631 --> 01:08:57,765
let's see how do you greet people? Here we got hello,
1224
01:08:57,767 --> 01:09:00,568
the Aloha has not even, been uploaded yet, so
1225
01:09:00,570 --> 01:09:05,240
let's go ahead and hit the Aloha, then we get the Aloha.
1226
01:09:06,776 --> 01:09:09,110
Okay? And same thing with delete, if we go back here and
1227
01:09:09,112 --> 01:09:10,845
say, well we don't want this to greet people,
1228
01:09:10,847 --> 01:09:13,248
hit delete. And it deleted on our iPhone,
1229
01:09:13,250 --> 01:09:16,818
we don't have to wait back and forth. Okay,
1230
01:09:16,820 --> 01:09:21,156
I will see you next time. >> For more,
1231
01:09:21,158 --> 01:09:21,189
please visit us at stanford.edu.
================================================
FILE: subtitles/17. Segues, Core Location, and MapKit.srt
================================================
1
00:00:00,001 --> 00:00:03,802
[MUSIC]
2
00:00:03,804 --> 00:00:08,440
Stanford University. >> Okay, well,
3
00:00:08,442 --> 00:00:14,146
welcome to Lecture 17 of Stanford CS193P
4
00:00:14,148 --> 00:00:19,518
spring of 2016. This is our penultimate lecture that
5
00:00:19,520 --> 00:00:23,822
actually has content. Next week we have Memorial Day, and
6
00:00:23,824 --> 00:00:27,760
then we'll have our alternate final on Wednesday.
7
00:00:27,762 --> 00:00:28,861
So what are we gonna talk about today?
8
00:00:28,863 --> 00:00:31,130
Today we're gonna talk about segues, okay?
9
00:00:31,132 --> 00:00:32,398
We've talked a lot about segues, but
10
00:00:32,400 --> 00:00:36,602
there's still quite a few more segues to be talked about. And
11
00:00:36,604 --> 00:00:39,738
then we're gonna talk about core location and MapKit,
12
00:00:39,740 --> 00:00:43,876
which is kind of where is the device in the world. And then
13
00:00:43,878 --> 00:00:46,812
I'm gonna do a demo, I'll do as much as the demo as I can,
14
00:00:46,814 --> 00:00:48,247
and we'll just continue it on Wednesday.
15
00:00:48,249 --> 00:00:50,616
The demo's gonna be about maps and segues, obviously,
16
00:00:50,618 --> 00:00:53,719
that's what we're talking about today. All right? So
17
00:00:53,721 --> 00:00:55,220
let's dive right in here to segues.
18
00:00:55,222 --> 00:00:59,792
The first segue I'm gonna talk about is the modal segue. So,
19
00:00:59,794 --> 00:01:03,095
this modal segue is the segue to an MVC that is gonna
20
00:01:03,097 --> 00:01:05,697
completely take over your screen, okay?
21
00:01:05,699 --> 00:01:08,834
You are fully responsible if you put a modal segue up for
22
00:01:08,836 --> 00:01:11,670
providing a way for a user to get out of it, okay.
23
00:01:11,672 --> 00:01:15,307
Because that MVC is completely in control.
24
00:01:15,309 --> 00:01:18,977
This kind of UI should be used with care, okay, because you
25
00:01:18,979 --> 00:01:22,214
really are locking down the UI, there's nothing else
26
00:01:22,216 --> 00:01:24,817
the user can do. They can't. If they change their mind,
27
00:01:24,819 --> 00:01:27,653
they can't go to another tab cuz it's not a tab bar thing.
28
00:01:27,655 --> 00:01:30,022
They can't just hit back in a navigation controller,
29
00:01:30,024 --> 00:01:32,224
they can't go to the other side of their split view or
30
00:01:32,226 --> 00:01:35,494
whatever. They're pretty much stuck here until you let them,
31
00:01:35,496 --> 00:01:39,598
out of it. And so, I don't even show you this until late
32
00:01:39,600 --> 00:01:40,466
in the quarter because
33
00:01:40,468 --> 00:01:43,802
I really don't want you to think of this as a go to way
34
00:01:43,804 --> 00:01:47,406
to present an MVC on screen, okay? But it does have its
35
00:01:47,408 --> 00:01:50,242
uses, okay? Here's an example, perhaps. Okay,
36
00:01:50,244 --> 00:01:54,746
this is a contacts app, or an app that has contacts in it.
37
00:01:54,748 --> 00:01:56,281
And we're showing the contacts and
38
00:01:56,283 --> 00:01:59,585
the little plus button in the corner here, let's say that,
39
00:01:59,587 --> 00:02:02,521
that button's going to allow us to enter a new contact.
40
00:02:02,523 --> 00:02:05,691
Now, you could argue if the user has chosen to enter a new
41
00:02:05,693 --> 00:02:09,428
contact. They don't wanna do anything else except for
42
00:02:09,430 --> 00:02:10,028
that right now, and so
43
00:02:10,030 --> 00:02:13,298
it's okay to present this adding contact view controller
44
00:02:13,300 --> 00:02:17,436
modally. Modally just means in a mode where you can't
45
00:02:17,438 --> 00:02:19,605
do anything else but this. Yeah,
46
00:02:19,607 --> 00:02:21,573
one could argue there might be other ways to do this,
47
00:02:21,575 --> 00:02:24,209
but let's, let's say that we're gonna do it this way.
48
00:02:24,211 --> 00:02:27,913
So we tap on this plus sign, and from the bottom slides up
49
00:02:27,915 --> 00:02:30,916
this new MVC. Now, this new MVC is completely controlling
50
00:02:30,918 --> 00:02:34,253
the screen. It doesn't have a back button that cancel button
51
00:02:34,255 --> 00:02:37,256
that you see right there. Is a button that this MVC is
52
00:02:37,258 --> 00:02:40,893
putting in that bar, okay, it happens to cancel and
53
00:02:40,895 --> 00:02:42,995
not add a new contact in this case.
54
00:02:42,997 --> 00:02:45,564
Cuz it's not a push segue, it's a modal segue,
55
00:02:45,566 --> 00:02:48,901
different kind of segue. And the thing about modal, as you
56
00:02:48,903 --> 00:02:52,137
can actually have embedded modal inside modal, okay? So,
57
00:02:52,139 --> 00:02:55,541
for example here, I am adding my contact, let's say now,
58
00:02:55,543 --> 00:02:58,010
I wanna choose the photo for my contact, okay?
59
00:02:58,012 --> 00:03:01,280
That's, also going to put up a modal MVC to get that. So
60
00:03:01,282 --> 00:03:05,117
I click on that, another MVC slides off in the bottom so
61
00:03:05,119 --> 00:03:06,451
now, I have two modal ones.
62
00:03:06,453 --> 00:03:08,554
And you might think of this as a little bit like,
63
00:03:08,556 --> 00:03:08,787
navigation control but
64
00:03:08,789 --> 00:03:10,756
they're on a stack, but that's really not the way it is.
65
00:03:10,758 --> 00:03:14,393
It's just the ad contact MVC took over the entire world and
66
00:03:14,395 --> 00:03:18,096
then, it gave it to the photos one and it completely took
67
00:03:18,098 --> 00:03:20,599
over the world. And until it's dismissed,
68
00:03:20,601 --> 00:03:22,234
the other ones don't even really matter,
69
00:03:22,236 --> 00:03:26,872
okay. Now, again notice no back button here. Also notice
70
00:03:26,874 --> 00:03:30,042
by the way that cancel button has moved over from the right,
71
00:03:30,044 --> 00:03:32,110
whereas on previous MVC had it on the left.
72
00:03:32,112 --> 00:03:35,113
I'm not a big fun of this kind of UI, by the way.
73
00:03:35,115 --> 00:03:36,181
I think, things like cancel,
74
00:03:36,183 --> 00:03:38,784
if they're conceptually the same thing in two MVCs.
75
00:03:38,786 --> 00:03:41,753
Especially, two MVCs that appear back to back like this,
76
00:03:41,755 --> 00:03:43,088
should probably be in the same place.
77
00:03:43,090 --> 00:03:47,292
So I would have put the cancel buttons both on the left here.
78
00:03:47,294 --> 00:03:51,430
But whatever app this is or from where these contacts
79
00:03:51,432 --> 00:03:55,100
came from, I decided to do that. But in any case,
80
00:03:55,102 --> 00:03:59,538
no back button here. Right? Make sense? Okay,
81
00:03:59,540 --> 00:04:04,243
so, let's cancel this and we'll see what happens to this
82
00:04:04,245 --> 00:04:07,112
modal MVC. This cancel is a way to get out of it.
83
00:04:07,114 --> 00:04:07,579
And when we cancel it,
84
00:04:07,581 --> 00:04:10,549
of course, it slides back out. And the other MVC is still
85
00:04:10,551 --> 00:04:14,720
there. Okay? Nothing has changed. And here, of course,
86
00:04:14,722 --> 00:04:17,789
if we cancel this one, then we'll go back to where we were
87
00:04:17,791 --> 00:04:21,193
before. And this MVC goes away. So that's modal, right?
88
00:04:21,195 --> 00:04:24,029
You press the MVC. It lives, it does whatever it does.
89
00:04:24,031 --> 00:04:26,265
It completely takes over the app and then somehow,
90
00:04:26,267 --> 00:04:29,167
you press something to get out of it. Cancel or done, or
91
00:04:29,169 --> 00:04:33,605
whatever, but that's totally up to that MVC. Right? So,
92
00:04:33,607 --> 00:04:37,109
be careful is all I say about this. Don't go to modal first.
93
00:04:37,111 --> 00:04:39,845
Think about whether there's a way to put it in a tab or
94
00:04:39,847 --> 00:04:43,348
in your navigation stack, or something like that. Before
95
00:04:43,350 --> 00:04:46,752
you jump right to choosing modal. All right, so how do we
96
00:04:46,754 --> 00:04:49,154
set up a modal segue? How do we do one of these?
97
00:04:49,156 --> 00:04:51,957
Just like all the other segues we've been doing,
98
00:04:51,959 --> 00:04:55,927
you just CTRL+drag, from the thing that's going to cause
99
00:04:55,929 --> 00:04:59,331
the modal segue to happen, to the MVC that's going to take
100
00:04:59,333 --> 00:05:02,601
over the world. When you do that by the way, an important
101
00:05:02,603 --> 00:05:05,137
thing to notice is that you can inspect the segue, and
102
00:05:05,139 --> 00:05:07,939
a modal segue has different kinda things you can choose.
103
00:05:07,941 --> 00:05:10,709
Which I'm gonna talk about, like how the modal segue is
104
00:05:10,711 --> 00:05:12,511
presented. Does it slide up from the bottom or
105
00:05:12,513 --> 00:05:15,013
do something else? And also, what does the modal segue
106
00:05:15,015 --> 00:05:18,216
look like when it's there? Is it a take over the full screen
107
00:05:18,218 --> 00:05:20,319
kind, okay, or is it some other kind.
108
00:05:20,321 --> 00:05:23,822
We'll talk about what those other kinds can be. All right?
109
00:05:23,824 --> 00:05:28,727
If you wanna present a modal MVC not from a button, okay or
110
00:05:28,729 --> 00:05:30,062
from a bar button item.
111
00:05:30,064 --> 00:05:31,496
You wanna do it from something else. Of course,
112
00:05:31,498 --> 00:05:33,865
you can present it using the normal form segue with
113
00:05:33,867 --> 00:05:35,934
identifier from your code. We've seen a couple of
114
00:05:35,936 --> 00:05:39,705
examples of that in the demos and of course, modal, you can
115
00:05:39,707 --> 00:05:42,741
do that as well. You also, if you have a view controller in
116
00:05:42,743 --> 00:05:44,710
your hand, you actually have an instance of a view
117
00:05:44,712 --> 00:05:47,212
controller like remember alert view controller, right?
118
00:05:47,214 --> 00:05:49,681
You say new alert view controller and you get one,
119
00:05:49,683 --> 00:05:53,118
now you have one. You can put it up using this method right
120
00:05:53,120 --> 00:05:56,455
here, which we actually saw in one of the other demos,
121
00:05:56,457 --> 00:05:59,257
alert demo there. Actually I don't know if I demoed it,
122
00:05:59,259 --> 00:06:01,660
but we talked about it in alerts, which is present
123
00:06:01,662 --> 00:06:03,628
view controller. And you just give it the view controller
124
00:06:03,630 --> 00:06:06,264
that you have in your hand and whether you want it
125
00:06:06,266 --> 00:06:09,668
to be an animated transition, which usually you do. And
126
00:06:09,670 --> 00:06:12,070
then you just got a little handler here at the end,
127
00:06:12,072 --> 00:06:15,440
that will get called when it has finished being presented.
128
00:06:15,442 --> 00:06:18,009
You usually don't need that, but just in case, you do.
129
00:06:18,011 --> 00:06:20,178
All right. So this is how you present something modally.
130
00:06:20,180 --> 00:06:23,115
This is only for modalPresentations, all right?
131
00:06:23,117 --> 00:06:24,516
If you have the view controller in your hand,
132
00:06:24,518 --> 00:06:28,453
which is rare. Generally, it's things you got from iOS,
133
00:06:28,455 --> 00:06:30,922
like maybe the camera view controller, or
134
00:06:30,924 --> 00:06:34,059
the alert view controller, some things like that.
135
00:06:34,061 --> 00:06:37,462
Notice that in horizontally regular environment,
136
00:06:37,464 --> 00:06:40,732
horizontally regular, not compact. You there's
137
00:06:40,734 --> 00:06:44,169
a modalPresentationStyle var in a view controller, that'll,
138
00:06:44,171 --> 00:06:47,305
that'll specify basically how this view controller gets
139
00:06:47,307 --> 00:06:50,175
presented when it gets presented modally. Does it get
140
00:06:50,177 --> 00:06:53,512
presented full screen where it takes the whole full screen?
141
00:06:53,514 --> 00:06:57,382
OverFullScreen which means, it goes over the whole screen but
142
00:06:57,384 --> 00:06:58,283
it, the one behind,
143
00:06:58,285 --> 00:07:01,286
whatever is existing on this screen is visible. So
144
00:07:01,288 --> 00:07:03,288
if you had a clear view controller,
145
00:07:03,290 --> 00:07:03,989
you'd be able to see through it.
146
00:07:03,991 --> 00:07:07,592
So you have things underneath. Pop over, of course you guys
147
00:07:07,594 --> 00:07:10,462
know what a pop over is. A pop over is just a modal,
148
00:07:10,464 --> 00:07:15,033
a modal MVC, but it's kinda got some special presentation
149
00:07:15,035 --> 00:07:18,136
characteristics. There's also form sheet and page sheet,
150
00:07:18,138 --> 00:07:21,339
things like that, which kinda present in different ways.
151
00:07:21,341 --> 00:07:24,242
You can play with those, by inspecting the segue in
152
00:07:24,244 --> 00:07:27,712
the storyboard and choosing different presentation styles.
153
00:07:27,714 --> 00:07:31,283
Note that in horizontally compact environments, however,
154
00:07:31,285 --> 00:07:35,754
it's basically the iPhone in portrait. The system, when you
155
00:07:35,756 --> 00:07:40,091
do the presentViewController will by default,
156
00:07:40,093 --> 00:07:42,327
adapt to be full screen, okay?
157
00:07:42,329 --> 00:07:46,164
So if you try to present something in form sheet mode,
158
00:07:46,166 --> 00:07:50,101
okay? On an iPhone, it'll come up as full screen, cuz it'll
159
00:07:50,103 --> 00:07:54,406
adapt to the fact that it's on the phone. It doesn't really
160
00:07:54,408 --> 00:07:58,844
have enough width to do a form sheet for you, okay?
161
00:07:58,846 --> 00:08:02,047
How do you prepare for a Modal segue? Nothing to see here,
162
00:08:02,049 --> 00:08:04,616
okay? It's just like any other segue you've ever done.
163
00:08:04,618 --> 00:08:06,751
Nothing special about it. Just check the identifier,
164
00:08:06,753 --> 00:08:09,321
get the destination view controller and prepare it.
165
00:08:09,323 --> 00:08:12,958
How do you hear back from a modal view controller?
166
00:08:12,960 --> 00:08:15,460
You put something some up modally and it does something,
167
00:08:15,462 --> 00:08:18,230
okay? You add to new contact, how do you hear back
168
00:08:18,232 --> 00:08:21,166
with the results of what it did? Okay? Well,
169
00:08:21,168 --> 00:08:26,271
there's a couple of ways to do this. One is that the add
170
00:08:26,273 --> 00:08:29,674
contact thing might be putting the contact in a database,
171
00:08:29,676 --> 00:08:32,644
some modal that is shared by the guy who put it up.
172
00:08:32,646 --> 00:08:35,113
So then you don't need to do anything, okay. When the modal
173
00:08:35,115 --> 00:08:36,715
thing gets dismissed, you'll come back and there'll be
174
00:08:36,717 --> 00:08:39,384
something else in the database that wasn't there before.
175
00:08:39,386 --> 00:08:42,988
Another way is using an unwind segue which I'm gonna talk
176
00:08:42,990 --> 00:08:45,891
about in few slides here. And then another way is
177
00:08:45,893 --> 00:08:48,660
delegation. And the reason we have to use delegation or
178
00:08:48,662 --> 00:08:51,396
unwind or something like that is because when you present
179
00:08:51,398 --> 00:08:53,932
a view controller modally, it's part of your view.
180
00:08:53,934 --> 00:08:56,701
It's part of the presenting view controller's view.
181
00:08:56,703 --> 00:09:00,472
So that thing can't talk back in any way, except for
182
00:09:00,474 --> 00:09:02,240
those blind structured ways we talked about,
183
00:09:02,242 --> 00:09:07,245
like delegation, okay? How do you dismiss a view controller,
184
00:09:07,247 --> 00:09:10,115
right? So a view controller's up, and it's collecting
185
00:09:10,117 --> 00:09:12,584
the new contact, and then the user puts Cancel or
186
00:09:12,586 --> 00:09:16,154
Done, how do we make it go away? The answer is you ask
187
00:09:16,156 --> 00:09:19,424
the presenting view controller to dismiss you, always
188
00:09:19,426 --> 00:09:22,928
the presenting view controller dismisses you. All right? And
189
00:09:22,930 --> 00:09:26,164
you send it the message dismissViewControllerAnimated
190
00:09:26,166 --> 00:09:27,432
to the presenting view controller and
191
00:09:27,434 --> 00:09:31,069
it's going to dismiss whatever modal view controller it has
192
00:09:31,071 --> 00:09:34,973
presented, okay? So that's sent to the presenting.
193
00:09:34,975 --> 00:09:38,143
I can't emphasize that enough. Now, in the old days,
194
00:09:38,145 --> 00:09:40,312
people used to send it to the presented.
195
00:09:40,314 --> 00:09:41,846
The API was a little bit different, okay?
196
00:09:41,848 --> 00:09:44,215
And then sent it to the presented view controller,
197
00:09:44,217 --> 00:09:46,084
the one that's actually up, okay?
198
00:09:46,086 --> 00:09:49,287
And, that's wrong. For backwards compatibility,
199
00:09:49,289 --> 00:09:50,622
if you sent it to the presented one,
200
00:09:50,624 --> 00:09:53,892
and the presented one had not presented another one,
201
00:09:53,894 --> 00:09:56,561
then it would dismiss it. But don't write your code to
202
00:09:56,563 --> 00:09:59,464
depend on that kind of backwards compatibility mode.
203
00:09:59,466 --> 00:10:02,534
When you wanna dismiss it, you send it to the presenting.
204
00:10:02,536 --> 00:10:05,937
Now the great thing is that the presented has a var
205
00:10:05,939 --> 00:10:08,640
called presenting view controller, which is the view
206
00:10:08,642 --> 00:10:11,009
controller that presented it. So it's very easy for
207
00:10:11,011 --> 00:10:13,778
the presented one to dismiss itself by asking
208
00:10:13,780 --> 00:10:17,682
its presenting view controller to dismiss it. You see.
209
00:10:17,684 --> 00:10:20,251
But that's the way you wanna write that code.
210
00:10:20,253 --> 00:10:22,420
Okay, if you do an unwind segue,
211
00:10:22,422 --> 00:10:24,322
again, which I'm gonna talk about in a few slides,
212
00:10:24,324 --> 00:10:26,791
it automatically dismisses. So when you unwind,
213
00:10:26,793 --> 00:10:29,094
you don't have to call dismissViewController.
214
00:10:29,096 --> 00:10:30,195
Just automatically dismisses. So
215
00:10:30,197 --> 00:10:33,031
we're talking about unwinding in a second. All right,
216
00:10:33,033 --> 00:10:37,902
so in addition to how the modal view controller appears,
217
00:10:37,904 --> 00:10:39,704
like form sheet or popover or whatever,
218
00:10:39,706 --> 00:10:42,440
there's also how it gets transitioned onto the screen,
219
00:10:42,442 --> 00:10:45,977
what animation gets used. Okay, so, CoverVertical is
220
00:10:45,979 --> 00:10:48,313
the slides up from the bottom, FlipHorizontal,
221
00:10:48,315 --> 00:10:50,248
exactly what you would think, the presented and
222
00:10:50,250 --> 00:10:53,952
the presenting flip, are on like two sides of a card and
223
00:10:53,954 --> 00:10:54,319
it flips over.
224
00:10:54,321 --> 00:10:56,955
You got CrossDissolve which is all between the two.
225
00:10:56,957 --> 00:11:00,792
Even PartialCurl which will partially curl a presenting
226
00:11:00,794 --> 00:11:05,263
one up showing the presented behind it. Kind of just at
227
00:11:05,265 --> 00:11:06,731
the bottom. It's kind of looks really cool.
228
00:11:06,733 --> 00:11:08,466
You see it sometimes with map applications and
229
00:11:08,468 --> 00:11:13,071
stuff you curl the map up and there's something underneath.
230
00:11:14,274 --> 00:11:17,275
All right, let's talk about that wi, unwind segue.
231
00:11:17,277 --> 00:11:18,243
Right, you have these MVCs,
232
00:11:18,245 --> 00:11:21,012
they wanna communicate back, or they wanna jump back.
233
00:11:21,014 --> 00:11:23,248
Maybe, something's on a navigation stack and
234
00:11:23,250 --> 00:11:26,317
wants to jump back farther than just one pop, okay?
235
00:11:26,319 --> 00:11:27,752
You can do all this with an unwind.
236
00:11:27,754 --> 00:11:31,423
Now, unwind segues are weird because they violate the thing
237
00:11:31,425 --> 00:11:32,924
I told you would never violate it,
238
00:11:32,926 --> 00:11:37,062
which is that segues always create a new MVC. Okay, and
239
00:11:37,064 --> 00:11:41,132
that's generally true if you modally segue or show segue or
240
00:11:41,134 --> 00:11:42,701
any of these other kinds of segues,
241
00:11:42,703 --> 00:11:46,604
it creates a new MVC every time except unwind. Because
242
00:11:46,606 --> 00:11:50,375
unwind is gonna segue back to somebody who presented you,
243
00:11:50,377 --> 00:11:52,844
maybe not the guy who immediately presented to you,
244
00:11:52,846 --> 00:11:54,279
maybe somebody who presented to the guy
245
00:11:54,281 --> 00:11:54,779
who presented to you but
246
00:11:54,781 --> 00:11:57,282
you're gonna be going back to an existing view controller.
247
00:11:57,284 --> 00:12:01,786
You're gonna unwind. Okay, your view controllers.
248
00:12:01,788 --> 00:12:03,822
What's it good for? Like I said, good for
249
00:12:03,824 --> 00:12:06,925
communicating back information after a modal one is done,
250
00:12:06,927 --> 00:12:09,561
and also good for jumping back up the stack of cards in
251
00:12:09,563 --> 00:12:13,498
the navigation controller more than one step, okay?
252
00:12:13,500 --> 00:12:15,767
Because it can go back to guys, who presented guys,
253
00:12:15,769 --> 00:12:18,470
who presented guys so it can jump all the way back.
254
00:12:18,472 --> 00:12:20,805
How does it work? Okay, so instead of
255
00:12:20,807 --> 00:12:24,709
Ctrl+dragging to another MVC to set up this segue which is
256
00:12:24,711 --> 00:12:27,545
what you usually do, you actually Ctrl+drag to this
257
00:12:27,547 --> 00:12:30,181
little button at the top. It's kinda reddish.
258
00:12:30,183 --> 00:12:34,185
The Exit button. Okay, so if I have this row in this table
259
00:12:34,187 --> 00:12:37,622
right here and when you click on it, it wants to unwind and
260
00:12:37,624 --> 00:12:41,025
go back to one of the MVCs that presented this MVC,
261
00:12:41,027 --> 00:12:45,163
you just Ctrl+drag up to this little exit. Now when you do
262
00:12:45,165 --> 00:12:49,501
that, it's gonna show you a list of methods that
263
00:12:49,503 --> 00:12:53,805
are implemented by other MVCs in your app, okay? They're
264
00:12:53,807 --> 00:12:57,342
special, and I'll show you what makes them special but
265
00:12:57,344 --> 00:13:00,879
these special things will appear in this list.
266
00:13:00,881 --> 00:13:04,549
And all you do is pick which one you want. Then when you do
267
00:13:04,551 --> 00:13:07,685
the segue, it will start looking up the list of MVC's
268
00:13:07,687 --> 00:13:09,354
that presented you, and the one that presented that, and
269
00:13:09,356 --> 00:13:11,523
the one that presented that until it finds that method.
270
00:13:11,525 --> 00:13:14,359
And it will dismiss all the way back to that one and
271
00:13:14,361 --> 00:13:19,330
call this method, okay? So let me show you some more
272
00:13:19,332 --> 00:13:22,801
pictures to make this a little bit clearer right here.
273
00:13:22,803 --> 00:13:26,104
So this go back method right here, okay,
274
00:13:26,106 --> 00:13:28,306
I want, I connected to exit and
275
00:13:28,308 --> 00:13:28,740
I have to go back.
276
00:13:28,742 --> 00:13:32,110
Go back let's say is in the guy who directly presented me.
277
00:13:32,112 --> 00:13:34,712
He could be farther up the stack but let's say he's right
278
00:13:34,714 --> 00:13:37,282
here. And here's this go back method right here. Now,
279
00:13:37,284 --> 00:13:40,552
what's special about this go back method in this presenter?
280
00:13:40,554 --> 00:13:44,355
Well, one thing is it has to be marked IBAction, okay?
281
00:13:44,357 --> 00:13:47,292
So if it has IBAction. And the second thing is,
282
00:13:47,294 --> 00:13:51,162
the argument has to be a UIStoryboardSegue. So
283
00:13:51,164 --> 00:13:53,731
any method that's, is marked IBAction and
284
00:13:53,733 --> 00:13:57,802
has UIStoryboardSegue as the argument is gonna appear in
285
00:13:57,804 --> 00:14:02,240
this list. Okay, now this IBAction,
286
00:14:02,242 --> 00:14:06,377
once you kind of pick the one you want.
287
00:14:06,379 --> 00:14:09,314
This segue is gonna happen and it's still gonna be a normal
288
00:14:09,316 --> 00:14:10,982
segue and then it's gonna have prepare.
289
00:14:10,984 --> 00:14:13,985
Now the prepare, okay, here's the prepare for segue.
290
00:14:13,987 --> 00:14:17,689
It's going to be in the aah MVC that you wired the exit
291
00:14:17,691 --> 00:14:20,725
button up to, the one on the right here. Okay,
292
00:14:20,727 --> 00:14:23,027
so it lives down in there. And it gets to prepare for
293
00:14:23,029 --> 00:14:26,564
this segue. The segue is gonna come back to this presenter.
294
00:14:26,566 --> 00:14:28,967
So, the destination view controller,
295
00:14:28,969 --> 00:14:33,872
here, is the presenter. The source view controller,
296
00:14:33,874 --> 00:14:34,906
which you haven't seen yet,
297
00:14:34,908 --> 00:14:38,710
in previous segues, is the thing on the right, okay?
298
00:14:38,712 --> 00:14:42,180
The thing it's segueing from. That's the source,
299
00:14:42,182 --> 00:14:46,351
this is the destination, okay? Make sense? And
300
00:14:46,353 --> 00:14:49,220
when this happens, okay, when the segue happens,
301
00:14:49,222 --> 00:14:52,156
you'll be dismissed, right? The source one
302
00:14:52,158 --> 00:14:55,226
will be dismissed. And again, it doesn't just have to
303
00:14:55,228 --> 00:14:57,829
go one level up, it could go up the stack of the navigation
304
00:14:57,831 --> 00:15:00,431
controller things. Or if you had four or five modals in
305
00:15:00,433 --> 00:15:02,634
a row, it could jump all the way back, it's perfectly fine.
306
00:15:02,636 --> 00:15:07,038
It's just whoever implements the method. All right?
307
00:15:07,040 --> 00:15:10,341
Now let's talk another kind of segue, popovers, okay?
308
00:15:10,343 --> 00:15:11,009
You've all seen the popover.
309
00:15:11,011 --> 00:15:13,544
Here's an example of popover here on the right.
310
00:15:13,546 --> 00:15:16,247
This is probably some kind of Search for Appointment to
311
00:15:16,249 --> 00:15:21,486
popover, okay? So this MVC just leaves in here.
312
00:15:21,488 --> 00:15:23,721
And it's being presented modally, okay?
313
00:15:23,723 --> 00:15:26,958
It's just that it presents in a way that looks like this,
314
00:15:26,960 --> 00:15:29,961
like it's in a pop-over. And the popover of course has
315
00:15:29,963 --> 00:15:32,196
a little arrow at the top, a little white arrow,
316
00:15:32,198 --> 00:15:35,767
you can see it, that points to the thing, the UI element that
317
00:15:35,769 --> 00:15:40,405
caused it to pop up. Usually that's a bar button item, but
318
00:15:40,407 --> 00:15:43,341
it could also just be an arbitrary rectangle inside
319
00:15:43,343 --> 00:15:47,712
some view somewhere, okay? This area out here, okay,
320
00:15:47,714 --> 00:15:51,015
everywhere else except for this popover and
321
00:15:51,017 --> 00:15:54,652
the keyboard is grayed out. If you click there,
322
00:15:54,654 --> 00:15:58,556
it will dismiss this popover. So the one difference between
323
00:15:58,558 --> 00:16:02,293
popover and other kinda modal one is that popovers are easy
324
00:16:02,295 --> 00:16:05,964
to dismiss. Just click outside of them, you'll dismiss, so
325
00:16:05,966 --> 00:16:08,633
you don't need a cancel button in a popover or
326
00:16:08,635 --> 00:16:11,502
modal popover. But it's still modal in that obviously you
327
00:16:11,504 --> 00:16:13,738
can't do anything in that grayed out area.
328
00:16:13,740 --> 00:16:16,007
You can only do what this search for
329
00:16:16,009 --> 00:16:18,409
appointment MBC wants to do right now.
330
00:16:18,411 --> 00:16:21,112
Okay, so it's still a modal, it just looks different.
331
00:16:21,114 --> 00:16:26,317
Popover is just a presentation style basically for modal. So
332
00:16:26,319 --> 00:16:29,320
popovers, you think of a popover's kind of like
333
00:16:29,322 --> 00:16:32,824
a navigational controller or split view controller, a tab
334
00:16:32,826 --> 00:16:36,294
bar because it's putting a view controller inside another
335
00:16:36,296 --> 00:16:39,497
another view controller, but it's not. You should think of
336
00:16:39,499 --> 00:16:44,569
popover just as a presentation style. Dial for modal, okay?
337
00:16:44,571 --> 00:16:48,373
So segue into a popover is setup the same way that you
338
00:16:48,375 --> 00:16:51,642
setup a oni modal one, it's just that when you drag over,
339
00:16:51,644 --> 00:16:56,080
you're gonna pick popover instead of modal, okay? When
340
00:16:56,082 --> 00:16:59,350
you're in the storyboard you drag over. Now, an important
341
00:16:59,352 --> 00:17:02,253
thing to note when you prepare for a popover segue,
342
00:17:02,255 --> 00:17:04,455
this is what's different between a popover and
343
00:17:04,457 --> 00:17:10,461
immodal encode, okay? When you prepare all presentations of
344
00:17:10,463 --> 00:17:14,265
any kind of segue are done by a UI presentation controller.
345
00:17:14,267 --> 00:17:17,335
You don't see this, I'm not really talking about this
346
00:17:17,337 --> 00:17:20,071
in this class, it's not really kind of a introductory
347
00:17:20,073 --> 00:17:24,342
material. But, the popover one has a sub class of that called
348
00:17:24,344 --> 00:17:26,244
UI popover presentation controller,
349
00:17:26,246 --> 00:17:27,278
and it's in the thing that knows
350
00:17:27,280 --> 00:17:28,613
how to present it as a popover.
351
00:17:28,615 --> 00:17:31,516
So it's the thing that knows, for example, where to have
352
00:17:31,518 --> 00:17:35,053
the little popover thing point, okay. That's something
353
00:17:35,055 --> 00:17:39,123
that that popover presentation controller knows about. So in
354
00:17:39,125 --> 00:17:42,360
your prepare for segue, you're gonna get this UI popover
355
00:17:42,362 --> 00:17:44,462
presentation controller and I'll show you how to do that.
356
00:17:44,464 --> 00:17:47,398
And you're going to use it to configure how the popover
357
00:17:47,400 --> 00:17:51,869
presents, okay? Now in addition to setting things
358
00:17:51,871 --> 00:17:53,805
like where the popover's arrow can point,
359
00:17:53,807 --> 00:17:57,008
you can also control things like how the popover adapts
360
00:17:57,010 --> 00:18:00,912
to different size classes, okay? And we'll talk about
361
00:18:00,914 --> 00:18:04,615
that in a second too. So here's the prepare for segue,
362
00:18:04,617 --> 00:18:06,384
the prepare for a popover, okay?
363
00:18:06,386 --> 00:18:09,954
All the green, except for what's in this yellow if
364
00:18:09,956 --> 00:18:13,524
statement right here is the same as any other prepare. But
365
00:18:13,526 --> 00:18:14,592
when we get down to the bottom here,
366
00:18:14,594 --> 00:18:17,295
we're gonna get that popover presentation controller by
367
00:18:17,297 --> 00:18:20,698
asking the view controller that we're putting up modally.
368
00:18:20,700 --> 00:18:24,402
Please give me your popover presentation controller,
369
00:18:24,404 --> 00:18:27,271
okay? So that view controller has the popover presentation
370
00:18:27,273 --> 00:18:30,475
going. We get it. Once we have it, we can do things like
371
00:18:30,477 --> 00:18:33,044
configure it like the permitted arrow directions.
372
00:18:33,046 --> 00:18:35,980
Which direction we want the arrow to be? We can also
373
00:18:35,982 --> 00:18:40,118
specify things like where the arrows are pointing,
374
00:18:40,120 --> 00:18:40,852
all that kind of stuff.
375
00:18:40,854 --> 00:18:44,021
Okay, everything you would imagine a popover needs to be
376
00:18:44,023 --> 00:18:47,158
configured with. All right, now also importantly,
377
00:18:47,160 --> 00:18:51,863
we can set that presentation controllers delegate, okay?
378
00:18:51,865 --> 00:18:54,365
And with that delegate we control a little bit more
379
00:18:54,367 --> 00:18:56,701
about how the popover works. So let's talk about that.
380
00:18:56,703 --> 00:19:00,004
What can we do with the delegate? One thing we can do
381
00:19:00,006 --> 00:19:04,942
is find out when that popover was dismissed, okay? So
382
00:19:04,944 --> 00:19:06,144
the delegate will be sent this message,
383
00:19:06,146 --> 00:19:08,946
popoverPresentationController- DidDismissPopover.
384
00:19:08,948 --> 00:19:11,782
Did dismiss popover. Okay, so that's nice to be able to
385
00:19:11,784 --> 00:19:14,919
know when that popover was dismissed. Another thing is we
386
00:19:14,921 --> 00:19:18,623
can control the adaptation behavior when we're in
387
00:19:18,625 --> 00:19:22,193
different size classes like horizontally compact,
388
00:19:22,195 --> 00:19:25,029
okay? So when a popover comes up, as you can
389
00:19:25,031 --> 00:19:28,032
imagine, maybe it wouldn't fit in a horizontally compact
390
00:19:28,034 --> 00:19:30,535
environment unless it's kind of the small popover.
391
00:19:30,537 --> 00:19:34,839
So by default, the system adapts to full screen.
392
00:19:34,841 --> 00:19:37,642
So if you have a popover segue and it's on horizontally
393
00:19:37,644 --> 00:19:41,812
compact, it gets changed to modal full screen. So it'll
394
00:19:41,814 --> 00:19:44,182
take over the whole screen, slide up from the bottom.
395
00:19:44,184 --> 00:19:46,751
Okay, now you might now want that. Maybe it is a small
396
00:19:46,753 --> 00:19:49,687
little popover and you do want it to be a pop over on iPhone
397
00:19:49,689 --> 00:19:52,590
and you can control that. And here's how you do that.
398
00:19:52,592 --> 00:19:55,526
There is a method. Okay, this is a popover. This is actually
399
00:19:55,528 --> 00:19:59,397
a presentation controller delegate method. And
400
00:19:59,399 --> 00:20:01,832
you can see it just sends you the controller here and
401
00:20:01,834 --> 00:20:05,903
a traitCollection. So that's the size class is right here
402
00:20:05,905 --> 00:20:07,905
and you're gonna return a presentation style.
403
00:20:07,907 --> 00:20:10,975
Now, if you don't implement this delegation method, then
404
00:20:10,977 --> 00:20:14,278
in horizontally compact, it returns full screen otherwise,
405
00:20:14,280 --> 00:20:16,714
it returns whatever it was setup in the storyboard,
406
00:20:16,716 --> 00:20:19,217
popover or form sheet or whatever. But
407
00:20:19,219 --> 00:20:21,919
if you do implement it, you could return for example UI
408
00:20:21,921 --> 00:20:25,823
modal presentation style none which means don't adapt.
409
00:20:25,825 --> 00:20:29,093
This is asking for, how do I adapt to this trait
410
00:20:29,095 --> 00:20:31,796
collection. If you say none, that means don't adapt.
411
00:20:31,798 --> 00:20:34,031
Just do the same thing on every single platform.
412
00:20:34,033 --> 00:20:37,068
Okay, and so if you do this, then your popover will appear
413
00:20:37,070 --> 00:20:40,071
as a popover on your iPhone. Look just like on an iPad but
414
00:20:40,073 --> 00:20:43,140
it better be small coz it didn't have a lot of room.
415
00:20:43,142 --> 00:20:47,178
So this is how you can control that adaptation behavior,
416
00:20:47,180 --> 00:20:49,747
okay. But here's a problem, let's say you
417
00:20:49,749 --> 00:20:52,316
do that, okay, let's say you don't do that. Let's say
418
00:20:52,318 --> 00:20:54,885
the popover is too big and it comes up full screen,
419
00:20:54,887 --> 00:20:59,624
how do you dismiss that popover, okay? Because it used
420
00:20:59,626 --> 00:21:01,592
to be a popover I could click somewhere else and
421
00:21:01,594 --> 00:21:03,494
it will disappear. But now it's full screen.
422
00:21:03,496 --> 00:21:06,464
I can't click anywhere else. How do I dismiss it? Okay,
423
00:21:06,466 --> 00:21:08,599
that's a problem. What would be really cool,
424
00:21:08,601 --> 00:21:11,335
is if you could put it inside a navigation controller and
425
00:21:11,337 --> 00:21:15,740
then add a little done button, right at the top? And
426
00:21:15,742 --> 00:21:18,242
this method is how you do that. So, if you implement
427
00:21:18,244 --> 00:21:20,911
the delegate of the popover presentation controller, and
428
00:21:20,913 --> 00:21:22,713
you implement this thing, view controller for
429
00:21:22,715 --> 00:21:25,650
adapter presentation style, it allows you to return
430
00:21:25,652 --> 00:21:30,655
a different view controller to present. For example,
431
00:21:30,657 --> 00:21:33,057
you might, preser, present, return a navigation
432
00:21:33,059 --> 00:21:36,994
controller. Whose visible view controller is, the actual view
433
00:21:36,996 --> 00:21:39,897
controller that was inside the popover, you see. And
434
00:21:39,899 --> 00:21:42,633
you could add a done button to it or whatever you want. So
435
00:21:42,635 --> 00:21:44,435
here you can return, if you return nil here
436
00:21:44,437 --> 00:21:47,338
it's going to just return the thing that was inside,
437
00:21:47,340 --> 00:21:48,339
gonna be inside the popover.
438
00:21:48,341 --> 00:21:50,374
But if you return the navigation controller or
439
00:21:50,376 --> 00:21:53,311
whatever, it'll put that up In the modal adapted. And this is
440
00:21:53,313 --> 00:21:56,280
only happening in the adapted case, right? View control for
441
00:21:56,282 --> 00:22:01,652
adapted presentation style, okay? All right,
442
00:22:01,654 --> 00:22:04,789
very important issue about popover is its size, okay?
443
00:22:04,791 --> 00:22:07,625
iOS implements its size in a very object-jointed way,
444
00:22:07,627 --> 00:22:10,828
which is that when you have an MVC that's gonna be put up as
445
00:22:10,830 --> 00:22:14,131
a popover, the system asks that MVC what size would
446
00:22:14,133 --> 00:22:15,466
you prefer it to be?
447
00:22:15,468 --> 00:22:18,102
Okay, because that MVC obviously knows what the best
448
00:22:18,104 --> 00:22:22,139
size for it to be is, and it does that via this var
449
00:22:22,141 --> 00:22:25,643
preferredContentSize is a CGSize and you can either
450
00:22:25,645 --> 00:22:29,947
set this preferredContectSize. The MVC can set its
451
00:22:29,949 --> 00:22:32,550
own preferredContectSize to something reasonable or
452
00:22:32,552 --> 00:22:34,652
you could actually even override it. And
453
00:22:34,654 --> 00:22:38,789
have it return its correct size, its prefered size.
454
00:22:38,791 --> 00:22:42,059
It's called preferred by the way because, obviously
455
00:22:42,061 --> 00:22:44,395
the system will try to fit it on screen but if, for
456
00:22:44,397 --> 00:22:47,298
example, it's on horizontally compact and the adaptive thing
457
00:22:47,300 --> 00:22:49,734
is turned off then it might have to jam it in there,
458
00:22:49,736 --> 00:22:51,769
it might have to be a little smaller than preferred wants
459
00:22:51,771 --> 00:22:55,973
it to be, kay? So this is not guaranteed to be this size,
460
00:22:55,975 --> 00:23:00,878
this size is preferred. Okay, the last segue I'm gonna talk
461
00:23:00,880 --> 00:23:03,781
about is embed segues, okay? These are really,
462
00:23:03,783 --> 00:23:07,785
really cool segues. Basically you could take an MVC,
463
00:23:07,787 --> 00:23:10,321
take its view, you know, the top level view, and
464
00:23:10,323 --> 00:23:14,225
put it as a view inside the view hierarchy of another MVC.
465
00:23:14,227 --> 00:23:17,061
Okay it'll look just a view in another MVC, it's just that,
466
00:23:17,063 --> 00:23:19,130
it's completely controlled by an MVC,
467
00:23:19,132 --> 00:23:21,699
okay? Really cool that's why it's called embedded.
468
00:23:21,701 --> 00:23:24,301
You're embedding an MVC inside of another one.
469
00:23:24,303 --> 00:23:25,736
Xcode makes it really easy to do this.
470
00:23:25,738 --> 00:23:30,174
Just go down to your little object pallet down there where
471
00:23:30,176 --> 00:23:31,809
you get your buttons and stuff. And look for
472
00:23:31,811 --> 00:23:35,646
container view and drag that into the place that you want
473
00:23:35,648 --> 00:23:38,215
this thing to be embedded. It will look just like a view,
474
00:23:38,217 --> 00:23:40,184
you can do auto layout constraints on it.
475
00:23:40,186 --> 00:23:42,653
You can put it as a sub-view of anything else you want.
476
00:23:42,655 --> 00:23:45,656
It's just a view, okay. And when you do it,
477
00:23:45,658 --> 00:23:50,060
you're gonna see this kind of cool look where it puts what
478
00:23:50,062 --> 00:23:51,529
looks like a Segue way thing.
479
00:23:51,531 --> 00:23:53,998
Okay, here's the thing in the container, what I dragged out.
480
00:23:54,000 --> 00:23:57,535
This segue right here and here's the NVC. Okay so
481
00:23:57,537 --> 00:24:00,905
here's the normal NVC like in your storyboard anywhere else,
482
00:24:00,907 --> 00:24:03,874
and it's got this little segue here, so kinda seem like
483
00:24:03,876 --> 00:24:06,177
a segue and it is in the sense kinda segue to it.
484
00:24:06,179 --> 00:24:09,380
It's just that instead of doing it modelly or something
485
00:24:09,382 --> 00:24:12,383
that, it's just going to put its view right in here.
486
00:24:12,385 --> 00:24:16,320
Okay, make sense and you can prepare for that segue,
487
00:24:16,322 --> 00:24:20,224
it's a perfectly normal segue. One thing to be careful
488
00:24:20,226 --> 00:24:22,660
about this though is the View Loading Timing.
489
00:24:22,662 --> 00:24:24,662
Remember that when you're in prepare for segue,
490
00:24:24,664 --> 00:24:27,431
none of the outlets of the MVC are set. And that's true with
491
00:24:27,433 --> 00:24:30,067
embed as well, all right? So might have this embed segue,
492
00:24:30,069 --> 00:24:33,003
has all this cool UI but none of its outlets are set up. So
493
00:24:33,005 --> 00:24:35,606
a lot of times in the embed, when you're preparing for
494
00:24:35,608 --> 00:24:38,509
an embed segue, you're just grabbing the MVC and
495
00:24:38,511 --> 00:24:40,377
holding onto it with a pointer. And
496
00:24:40,379 --> 00:24:42,613
then later like in your own viewDidLoad,
497
00:24:42,615 --> 00:24:46,283
okay your MVC will be loaded by then, then you can go and
498
00:24:46,285 --> 00:24:49,153
set whatever you want to the embayed, the embedded thing to
499
00:24:49,155 --> 00:24:52,189
look like okay. So just be careful of that.
500
00:24:52,191 --> 00:24:55,259
It's just standard thing here that prepare for
501
00:24:55,261 --> 00:24:59,230
segue outlets aren't set in the destination MVC but for
502
00:24:59,232 --> 00:25:02,566
embed it comes, it's even more obvious, kinda,
503
00:25:02,568 --> 00:25:07,571
it bites you more often. Okay, so that's segues. Okay,
504
00:25:07,573 --> 00:25:10,741
that's all the segues there are. Okay, Ben, now you can
505
00:25:10,743 --> 00:25:14,011
cover. Yeah, question? >> So, so in, in that segues,
506
00:25:14,013 --> 00:25:16,814
if you have an existing view controller,
507
00:25:16,816 --> 00:25:18,816
can you connect into the inside or
508
00:25:18,818 --> 00:25:21,852
does it have to look funny like, you know?
509
00:25:21,854 --> 00:25:22,887
>> Yeah so the question is can
510
00:25:22,889 --> 00:25:26,190
I connect just a regular mvc from somewhere like maybe my
511
00:25:26,192 --> 00:25:29,059
Cassini image view controller for example. It's just
512
00:25:29,061 --> 00:25:31,328
sitting around there. Could I embed it? And the answer is
513
00:25:31,330 --> 00:25:35,199
yes you can. Okay the mvc has embedded nothing special about
514
00:25:35,201 --> 00:25:39,803
it whatsoever okay it can be embedded just as easily.
515
00:25:40,206 --> 00:25:43,774
All right. Core Location is my next topic.
516
00:25:43,776 --> 00:25:45,309
Okay we're done with segues now we're moving on to
517
00:25:45,311 --> 00:25:49,413
Core Location and map kit here. So Core Location and map
518
00:25:49,415 --> 00:25:53,417
kit are really two part pieces of the same puzzle here.
519
00:25:53,419 --> 00:25:57,121
Core Location is the non-UI way to find out about your
520
00:25:57,123 --> 00:26:01,258
location. Okay? And then map kit is the bunch of UI to show
521
00:26:01,260 --> 00:26:04,028
your location on maps and stuff. So when you talk about
522
00:26:04,030 --> 00:26:06,297
correlation first because it has a lot of the intrinsic
523
00:26:06,299 --> 00:26:09,099
stuff that you need to understand the map kit stuff.
524
00:26:09,101 --> 00:26:13,537
All right. The basic object in core location is called
525
00:26:13,539 --> 00:26:17,641
a CLLocation. Core location, location. CLLocation has
526
00:26:17,643 --> 00:26:21,145
the coordinate latitude and longitude, altitude,
527
00:26:21,147 --> 00:26:24,481
the horizontal and vertical accuracy of this location,
528
00:26:24,483 --> 00:26:27,551
because sometimes depending on how you got this location,
529
00:26:27,553 --> 00:26:28,552
it might be highly accurate,
530
00:26:28,554 --> 00:26:29,887
you know, within a meter or so, or
531
00:26:29,889 --> 00:26:32,222
might be very inaccurate, maybe it's a kilometer.
532
00:26:32,224 --> 00:26:35,459
Okay? Timestamp, when was this thing was taken, you'll see
533
00:26:35,461 --> 00:26:39,029
that that's really important. A speed if the system detected
534
00:26:39,031 --> 00:26:42,266
that you were moving at this time that this thing was
535
00:26:42,268 --> 00:26:45,970
taken, and a course will tell you what direction you were
536
00:26:45,972 --> 00:26:50,007
headed in if it detects that you were moving at this point.
537
00:26:50,009 --> 00:26:55,179
So that's the basic collection of stuff is in CLLocation.
538
00:26:55,181 --> 00:26:58,248
The CL, coordinate, this coordinate thing right here is
539
00:26:58,250 --> 00:27:02,019
a CLLocationCOordinate2D which is just a latitude and
540
00:27:02,021 --> 00:27:03,253
a longitude struct.
541
00:27:03,255 --> 00:27:06,023
The altitude is a CLLocationDistance,
542
00:27:06,025 --> 00:27:10,594
which is meters. Okay? Now let's talk about
543
00:27:10,596 --> 00:27:14,932
the accuracy thing, okay? When you are asking for
544
00:27:14,934 --> 00:27:18,302
a location and when you get a location, in both cases,
545
00:27:18,304 --> 00:27:20,471
you're gonna specify an accuracy, okay?
546
00:27:20,473 --> 00:27:23,240
Specifying the accuracy is going to determine what
547
00:27:23,242 --> 00:27:25,976
mechanism your device uses to get the accuracy. And
548
00:27:25,978 --> 00:27:29,380
then when you get the accuracy back It tells you, kind of,
549
00:27:29,382 --> 00:27:32,416
how it got it, somewhat. Okay, so, we're a little bit
550
00:27:32,418 --> 00:27:35,486
abstracting the hardware away from the concept of
551
00:27:35,488 --> 00:27:38,889
an accurate measurement of location. Here
552
00:27:38,891 --> 00:27:41,892
are the different accuracies that it can have here,
553
00:27:41,894 --> 00:27:42,393
best for navigation,
554
00:27:42,395 --> 00:27:45,129
best nearest ten meters, hundred meters, kilometer,
555
00:27:45,131 --> 00:27:49,266
or three kilometers and as you can imagine best for
556
00:27:49,268 --> 00:27:51,535
navigation. It's really really really really accurate.
557
00:27:51,537 --> 00:27:55,639
It's also really really really uses a lot of battery, okay.
558
00:27:55,641 --> 00:27:58,809
And basically you can almost rename these things as
559
00:27:58,811 --> 00:27:59,810
lots of battery usage,
560
00:27:59,812 --> 00:28:02,813
a little bit less, medium battery usage, not so
561
00:28:02,815 --> 00:28:05,883
much battery usage, very very battery efficient by the time
562
00:28:05,885 --> 00:28:09,453
you get to the bottom. Okay? It's really batteries are big
563
00:28:09,455 --> 00:28:12,322
deal when it comes to getting your location. Now,
564
00:28:12,324 --> 00:28:15,926
how does the system get these different accuracy locations?
565
00:28:15,928 --> 00:28:19,129
Well, there's three different ways really at least for
566
00:28:19,131 --> 00:28:21,331
now that the system could get location and
567
00:28:21,333 --> 00:28:23,333
you don't know which one is using. Okay,
568
00:28:23,335 --> 00:28:25,836
all you get to do is specify the accuracy you want and
569
00:28:25,838 --> 00:28:28,472
it will get the locations as best it can and re-tell you
570
00:28:28,474 --> 00:28:32,710
what accuracy it got. But the three ways it knows how to do
571
00:28:32,712 --> 00:28:37,981
are GPS which is very accurate and uses a lot of power.
572
00:28:37,983 --> 00:28:42,519
Okay, then there's WiFi nodes. Believe it or not your phone
573
00:28:42,521 --> 00:28:45,689
can, as you walk around, see what WiFi nodes are around.
574
00:28:45,691 --> 00:28:48,358
It's got a huge database on the network of all the WiFi
575
00:28:48,360 --> 00:28:50,828
nodes and where they are. And so based on which ones
576
00:28:50,830 --> 00:28:54,031
are close to you, which ones you're getting good signal,
577
00:28:54,033 --> 00:28:56,266
it can figure our generally where you are.
578
00:28:56,268 --> 00:28:57,701
Which is kind of a cool feature, right?
579
00:28:57,703 --> 00:29:00,571
Pretty low power, WiFi is fairly low power not as
580
00:29:00,573 --> 00:29:04,074
low-power as cellular towers which is the next one, and,
581
00:29:04,076 --> 00:29:07,544
but certainly a lot less power than doing GPS. And so,
582
00:29:07,546 --> 00:29:10,380
it can find, you know, these kind of medium accuracies
583
00:29:10,382 --> 00:29:12,616
by doing that. And in fact, if you walk around at Stanford,
584
00:29:12,618 --> 00:29:15,352
it can be very accurate because there's an incredible
585
00:29:15,354 --> 00:29:17,688
number of WiFi nodes around Stanford campus. You
586
00:29:17,690 --> 00:29:20,758
are probably in range of 50 of them at almost given time.
587
00:29:20,760 --> 00:29:23,761
Certainly 20 so, we pretty much know almost exactly where
588
00:29:23,763 --> 00:29:27,297
you are. And then last one is cell tower triangulation. So
589
00:29:27,299 --> 00:29:30,134
same way with the WiFi nodes it can look around to the cell
590
00:29:30,136 --> 00:29:33,403
towers. Now there's fewer cell towers than there are WiFi
591
00:29:33,405 --> 00:29:36,073
nodes okay, and they tend to be farther away from you.
592
00:29:36,075 --> 00:29:39,843
Okay, so triangulating them is a little more iffy okay.
593
00:29:39,845 --> 00:29:41,211
And sometimes there's not one for
594
00:29:41,213 --> 00:29:44,748
very very far distance from you, a kilometer or more. So
595
00:29:44,750 --> 00:29:47,417
that's why you start getting these really low accuracies
596
00:29:47,419 --> 00:29:51,355
out here but the cellular radio is incredibly low power.
597
00:29:51,357 --> 00:29:53,323
Super super low power, okay. And
598
00:29:53,325 --> 00:29:56,460
your cellular radio is also automatically being turned on
599
00:29:56,462 --> 00:29:58,896
and off and used as, you know you walk around and
600
00:29:58,898 --> 00:30:01,565
you're making phone calls, or receiving phone calls etc. So
601
00:30:01,567 --> 00:30:03,934
the cellular infrastructure's very low power but
602
00:30:03,936 --> 00:30:08,939
low accuracy. Okay? Got all of that? All right
603
00:30:08,941 --> 00:30:10,974
there's other things you can find out in the core location,
604
00:30:10,976 --> 00:30:13,377
your speed, your course, that timestamp.
605
00:30:13,379 --> 00:30:16,113
The reason the time stamp is important is that there
606
00:30:16,115 --> 00:30:18,115
are a lot of ways to ask the system to give you
607
00:30:18,117 --> 00:30:21,051
CLLocations, not in real time. In other words it's not going
608
00:30:21,053 --> 00:30:23,554
to give you the location right now. Like you might be going
609
00:30:23,556 --> 00:30:25,789
out for a run and you put your phone in your pocket and
610
00:30:25,791 --> 00:30:28,559
it goes to sleep it can still collect locations. And
611
00:30:28,561 --> 00:30:31,562
then when it wake back up it will send them all to you. And
612
00:30:31,564 --> 00:30:33,997
when it does you're going to want to know the timestamps so
613
00:30:33,999 --> 00:30:35,065
you knew where you were at the time.
614
00:30:35,067 --> 00:30:39,002
So time stamp is an important part of the CLLocation.
615
00:30:39,004 --> 00:30:42,339
All right. So how do we get a CLLocation? Well, you get it
616
00:30:42,341 --> 00:30:45,676
from a CLLocationManager usually. Just like we had
617
00:30:45,678 --> 00:30:47,611
a core motion, we have the CM motion manager. Well,
618
00:30:47,613 --> 00:30:51,381
in the core location, we have the CLLocationManager. And
619
00:30:51,383 --> 00:30:53,917
it has a delegate that's going to generally give you,
620
00:30:53,919 --> 00:30:57,387
locations. What's really cool about it is you can actually
621
00:30:57,389 --> 00:31:00,490
test in the simulator by simulating yourself being in
622
00:31:00,492 --> 00:31:03,227
different locations. If you go down near the debugger bar,
623
00:31:03,229 --> 00:31:06,830
there's a little guy there and that you can even upload
624
00:31:06,832 --> 00:31:09,333
with the GPX file. The GPX file is just
625
00:31:09,335 --> 00:31:11,235
a file with a bunch of waypoints in it, okay.
626
00:31:11,237 --> 00:31:14,438
GPS locations can have other stuff in there like actually
627
00:31:14,440 --> 00:31:17,875
URLs for photos taken at the location, stuff like that. But
628
00:31:17,877 --> 00:31:20,510
it can have these location you can upload it and
629
00:31:20,512 --> 00:31:25,782
have whatever testing you want in here okay. So
630
00:31:25,784 --> 00:31:26,250
the CLLocationManager.
631
00:31:26,252 --> 00:31:29,086
How do you use it? Similar to the CMMotion manager. First
632
00:31:29,088 --> 00:31:31,722
you're going to check like to see what hardware's available.
633
00:31:31,724 --> 00:31:34,858
Okay and then you're going to create a CLLocationManager and
634
00:31:34,860 --> 00:31:37,628
set as delegate so that you can receive updates.
635
00:31:37,630 --> 00:31:41,198
Then you're going to configure the manager to say what kind
636
00:31:41,200 --> 00:31:43,934
of location you want. And we'll talk about what that
637
00:31:43,936 --> 00:31:45,836
means and then you're going to start it going.
638
00:31:45,838 --> 00:31:49,006
And once you start it going it's going to start sending
639
00:31:49,008 --> 00:31:52,042
your locations based on how you, what you asked for,
640
00:31:52,044 --> 00:31:55,112
all right. How, what kind of location monitoring is
641
00:31:55,114 --> 00:31:58,515
available in the system? Well one is accuracy based
642
00:31:58,517 --> 00:32:02,452
continual updates. This is what you would normally think.
643
00:32:02,454 --> 00:32:04,988
You set an accuracy that you want. Okay, I want
644
00:32:04,990 --> 00:32:08,525
highly accurate best for navigation or low accuracy and
645
00:32:08,527 --> 00:32:11,395
the system will start sending you locations based on that
646
00:32:11,397 --> 00:32:13,630
accuracy. So if you ask for best for navigation it's
647
00:32:13,632 --> 00:32:15,265
gonna be sending you locations all the time and
648
00:32:15,267 --> 00:32:17,701
they're gonna be highly accurate. If you ask for
649
00:32:17,703 --> 00:32:19,569
every three kilometers. You could walk for
650
00:32:19,571 --> 00:32:22,205
20 minutes before it sends you another location because it
651
00:32:22,207 --> 00:32:25,742
notices another cell tower and can figure out its location.
652
00:32:25,744 --> 00:32:30,480
Okay? So that's one way to do it. Another way is you can
653
00:32:30,482 --> 00:32:34,084
get notified only when a significant location change
654
00:32:34,086 --> 00:32:37,321
happens, okay. This is basically, I'm not talking
655
00:32:37,323 --> 00:32:40,190
about how it's implemented here but I kinda am.
656
00:32:40,192 --> 00:32:44,594
But it's basically when it sees a new cell tower. Okay.
657
00:32:44,596 --> 00:32:47,931
So that's a significant enough change that it'll probably
658
00:32:47,933 --> 00:32:48,098
send you one. So
659
00:32:48,100 --> 00:32:54,237
this is not highly accurate locations necessarily. Okay.
660
00:32:54,239 --> 00:32:57,441
The other one is region based updates so you can define
661
00:32:57,443 --> 00:33:01,979
these regions either little circular areas in the world or
662
00:33:01,981 --> 00:33:06,083
even where beacons live. Okay? Little bluetooth beacons and
663
00:33:06,085 --> 00:33:09,252
you can get notified when you get close to that beacon or
664
00:33:09,254 --> 00:33:10,387
you get real close to the beacon or
665
00:33:10,389 --> 00:33:14,157
when you walk inside the circular area. Okay? And
666
00:33:14,159 --> 00:33:16,360
then you also can have your heading monitored if
667
00:33:16,362 --> 00:33:20,497
you change direction, it can send you a new location.
668
00:33:20,799 --> 00:33:23,133
All right, so let's talk about how we use CLO,
669
00:33:23,135 --> 00:33:26,036
CL location manager, and then we'll talk about how to make
670
00:33:26,038 --> 00:33:28,672
each of those things work. First we want to find out what
671
00:33:28,674 --> 00:33:32,376
our, hardware can do. One of the most important things
672
00:33:32,378 --> 00:33:35,846
to find out about hardware is whether you're authorized to
673
00:33:35,848 --> 00:33:38,982
get the user's location. As you can imagine, users might
674
00:33:38,984 --> 00:33:41,518
not want you to know where they are, tracking their every
675
00:33:41,520 --> 00:33:45,188
move out there. It's kind of a sensitive subject, okay. So
676
00:33:45,190 --> 00:33:48,658
you have to make sure you're authorized to receive it.
677
00:33:48,660 --> 00:33:52,029
And you can do this with these various things I'll talk about
678
00:33:52,031 --> 00:33:52,129
authorization, on the next page. By the way, anytime you
679
00:33:52,131 --> 00:33:54,831
in detail,
680
00:33:54,833 --> 00:33:57,234
have an authorization status of any kind, not just for
681
00:33:57,236 --> 00:34:00,737
location, you notice there's the states Authorized, Denied,
682
00:34:00,739 --> 00:34:04,641
or Restricted. What Restricted means right there is,
683
00:34:04,643 --> 00:34:08,045
it's denied, and the user can't change it.
684
00:34:08,047 --> 00:34:11,615
So don't put up an alert that says, you, I'm not authorized
685
00:34:11,617 --> 00:34:14,918
to get your location. Please go to Settings and change it.
686
00:34:14,920 --> 00:34:16,820
Okay because they're not allowed to. There are ways
687
00:34:16,822 --> 00:34:19,723
in enterprises inside companies where companies can
688
00:34:19,725 --> 00:34:23,193
configure the phones of their employees. So for example,
689
00:34:23,195 --> 00:34:26,396
they can't do this location service or other services.
690
00:34:26,398 --> 00:34:28,331
Okay so be careful about the restricted state.
691
00:34:28,333 --> 00:34:32,102
It's restricted just accept the fact you can't do it.
692
00:34:32,104 --> 00:34:35,272
All right. And then you can also check for certain
693
00:34:35,274 --> 00:34:38,308
kinds of monitoring like can you monitor for beacons or
694
00:34:38,310 --> 00:34:41,778
can you monitor for circular regions? Most new hardware can
695
00:34:41,780 --> 00:34:44,548
do all of these stuff or if you have an older phone maybe
696
00:34:44,550 --> 00:34:47,617
it doesn't have the same capabilities to do that.
697
00:34:47,619 --> 00:34:50,387
Alright? So let's talk about
698
00:34:50,389 --> 00:34:52,255
this authorization piece though because it is
699
00:34:52,257 --> 00:34:54,458
very important interesting piece to do it.
700
00:34:54,460 --> 00:34:57,160
There's couple of steps you have to do to make it work.
701
00:34:57,162 --> 00:35:00,797
One is you have to request authorization, okay.
702
00:35:00,799 --> 00:35:03,400
This is ASynchronous, okay. You send this to the CL
703
00:35:03,402 --> 00:35:06,336
location manager and you're gonna request one of
704
00:35:06,338 --> 00:35:08,371
these two different kinds of authorizations.
705
00:35:08,373 --> 00:35:11,842
In Use Authorization or Always Authorization. So
706
00:35:11,844 --> 00:35:14,578
InUseAuthorization means you only wanna be allowed to
707
00:35:14,580 --> 00:35:18,148
get the location when it's the foreground app the user is
708
00:35:18,150 --> 00:35:20,784
using. Okay? When the user is in the background or
709
00:35:20,786 --> 00:35:22,652
anything else, you can't find this location.
710
00:35:22,654 --> 00:35:24,287
Users are fairly comfortable with that level
711
00:35:24,289 --> 00:35:26,756
of authorization cuz they know when you're getting their,
712
00:35:26,758 --> 00:35:29,860
their location. AlwaysAuthorization is you can
713
00:35:29,862 --> 00:35:32,429
get their location anytime even the background and
714
00:35:32,431 --> 00:35:35,999
that's very invasive of their privacy. So
715
00:35:36,001 --> 00:35:39,736
you know a lot of people, users won't do this.
716
00:35:39,738 --> 00:35:42,672
They won't give you that authorization, okay? So
717
00:35:42,674 --> 00:35:45,809
the system might be putting up an alert asking the user if
718
00:35:45,811 --> 00:35:48,912
it's okay, they're checking in their settings to see what
719
00:35:48,914 --> 00:35:51,781
settings are there, things like that to find so
720
00:35:51,783 --> 00:35:54,651
that's what its asynchronous. Eventually a delegate method
721
00:35:54,653 --> 00:35:56,520
will be called in your CL location saying,
722
00:35:56,522 --> 00:35:58,054
okay, here's your authorization task.
723
00:35:58,056 --> 00:36:01,925
It was denied or it was accept, it was allowed. Okay?
724
00:36:01,927 --> 00:36:05,729
Now, when you go off to request this authorization,
725
00:36:05,731 --> 00:36:09,766
you have to have the user in their setting,
726
00:36:09,768 --> 00:36:10,433
if they go into settings and
727
00:36:10,435 --> 00:36:13,370
look at location services, your app will be there,
728
00:36:13,372 --> 00:36:15,739
there has to be a switch they can click that says,
729
00:36:15,741 --> 00:36:20,644
yes I'll allow my, this app to have always authorization or
730
00:36:20,646 --> 00:36:25,448
in use authorization. To put that UI in the settings,
731
00:36:25,450 --> 00:36:28,752
you have to put something in your info P list. Okay?
732
00:36:28,754 --> 00:36:31,388
Which is this key right here NSLocation when
733
00:36:31,390 --> 00:36:32,789
in use description or
734
00:36:32,791 --> 00:36:35,492
NSLocation always use a description. This is a string
735
00:36:35,494 --> 00:36:41,231
explaining why you want this particular authorization.
736
00:36:41,233 --> 00:36:45,235
And if this key exists then, in settings,
737
00:36:45,237 --> 00:36:47,837
the user will be able to have a switch to turn this on or
738
00:36:47,839 --> 00:36:51,007
off. So if you don't put these in here, these things on top,
739
00:36:51,009 --> 00:36:54,344
these funcs will always fail. Because there's no way for
740
00:36:54,346 --> 00:36:56,479
the user to set that in their settings, so
741
00:36:56,481 --> 00:36:58,882
they'll all just always fail. Okay? And
742
00:36:58,884 --> 00:37:02,118
the system can't put up an alert even asking if it's okay
743
00:37:02,120 --> 00:37:03,920
because if they said yes there's no way,
744
00:37:03,922 --> 00:37:06,189
there's no switch for the system to turn on.
745
00:37:06,191 --> 00:37:07,891
So you'd just be always denied, so
746
00:37:07,893 --> 00:37:13,296
you need these two steps to get authorization. All right.
747
00:37:13,298 --> 00:37:17,167
Now, how do you then get the CL location from the location
748
00:37:17,169 --> 00:37:19,002
manager? Of course you can pull, but
749
00:37:19,004 --> 00:37:21,705
like with motion manager we say that's not a good idea.
750
00:37:21,707 --> 00:37:24,341
But what you can do you can just ask the location manager
751
00:37:24,343 --> 00:37:25,075
please give me the current location. And
752
00:37:25,077 --> 00:37:28,178
it will give you the location with whatever accuracy it has.
753
00:37:28,180 --> 00:37:31,648
Okay but really the way we do it is we set this var
754
00:37:31,650 --> 00:37:34,384
in the location manager called desired accuracy.
755
00:37:34,386 --> 00:37:36,620
That's one of those accuracies like best for navigation and
756
00:37:36,622 --> 00:37:39,723
all those things. And then also a distance filter which
757
00:37:39,725 --> 00:37:43,360
is how far the user has to move before we're gonna give
758
00:37:43,362 --> 00:37:46,863
a new location. Okay? So, if you take ten meters,
759
00:37:46,865 --> 00:37:50,033
you just gotta walk ten meters down the way before another
760
00:37:50,035 --> 00:37:50,867
location will come. And
761
00:37:50,869 --> 00:37:53,970
those two things together will give the system a good idea of
762
00:37:53,972 --> 00:37:57,807
how hard it has to work to get the users location. Okay. So
763
00:37:57,809 --> 00:38:01,611
you set those and then you say start updating the location.
764
00:38:01,613 --> 00:38:04,481
And as soon as you say start updating location, as long as
765
00:38:04,483 --> 00:38:07,250
you've set these, you're going to start getting locations
766
00:38:07,252 --> 00:38:11,588
appropriate to these settings up here. Now, be sure,
767
00:38:11,590 --> 00:38:12,455
just like with the motion manager,
768
00:38:12,457 --> 00:38:14,924
in fact even more importantly than the motion manager,
769
00:38:14,926 --> 00:38:17,627
to stop them when you're not gonna do anything with
770
00:38:17,629 --> 00:38:20,430
the results. If locations are coming, and you're ignoring
771
00:38:20,432 --> 00:38:23,967
them, you should have stopped it because this is basically
772
00:38:23,969 --> 00:38:26,102
turn on battery save. [LAUGH] Okay. If you turn, turn,
773
00:38:26,104 --> 00:38:29,072
if you don't stop updating location and
774
00:38:29,074 --> 00:38:31,141
you're not using it, you're just draining your battery for
775
00:38:31,143 --> 00:38:33,043
nothing, it's probably the number one way to drain
776
00:38:33,045 --> 00:38:37,480
their battery, actually. Okay. And you people will rapidly,
777
00:38:37,482 --> 00:38:40,550
you'll get it on your app store thing on the comments
778
00:38:40,552 --> 00:38:42,886
it'll say drained your battery don't buy this thing.
779
00:38:42,888 --> 00:38:46,890
Okay? So you want to be really careful not to do that. Okay.
780
00:38:46,892 --> 00:38:50,160
So once you turn it on you're gonna start getting this
781
00:38:50,162 --> 00:38:54,698
delegate method sent to you. Did update locations is plural
782
00:38:54,700 --> 00:38:56,333
which in a, within array of locations.
783
00:38:56,335 --> 00:38:58,535
Now, why is that an array of locations instead of
784
00:38:58,537 --> 00:39:00,170
just sending you each location one by one.
785
00:39:00,172 --> 00:39:03,073
Well, it's that running example, you got for a run,
786
00:39:03,075 --> 00:39:06,343
your phone goes to sleep, it collects a bunch of locations
787
00:39:06,345 --> 00:39:09,312
and I'm not gonna talk about how you'd set that up but
788
00:39:09,314 --> 00:39:11,781
you can look in the documentation. And when you're
789
00:39:11,783 --> 00:39:14,317
pick your phone up and then and say, how was my run. Boom!
790
00:39:14,319 --> 00:39:16,619
You're gonna get all these locations as an array here.
791
00:39:16,621 --> 00:39:19,289
Okay, with time stamps and all that, so you'll be able to
792
00:39:19,291 --> 00:39:23,727
reconstruct the run. Okay, similar APIs this, for
793
00:39:23,729 --> 00:39:27,797
heading, if you wanna just monitor the user's heading.
794
00:39:28,333 --> 00:39:29,799
Error reporting is important and
795
00:39:29,801 --> 00:39:32,669
easy to miss because you have to implement another delegate
796
00:39:32,671 --> 00:39:35,171
method here, which is locationManagerDidFailWithEr-
797
00:39:35,173 --> 00:39:38,908
ror. But you definitely do want to be looking at this.
798
00:39:38,910 --> 00:39:39,142
Okay if you have,
799
00:39:39,144 --> 00:39:41,478
if you are using a location you have to implement this.
800
00:39:41,480 --> 00:39:45,148
Because for example the user could at any time go to their
801
00:39:45,150 --> 00:39:45,215
say no I don't want this guy using my location.
802
00:39:45,217 --> 00:39:46,916
settings and
803
00:39:46,918 --> 00:39:50,420
In which case you're gonna start getting this denied.
804
00:39:50,422 --> 00:39:53,289
And if you don't look for this, your output's just gonna
805
00:39:53,291 --> 00:39:55,792
go into some wacky state because it's not gonna know
806
00:39:55,794 --> 00:39:57,861
what's happening. Okay you need to what's happening.
807
00:39:57,863 --> 00:40:01,598
Also, error location unknown. If from some reason, it,
808
00:40:01,600 --> 00:40:04,300
we can't find the location. Maybe you asked for best for
809
00:40:04,302 --> 00:40:07,404
navigation, it just can't find any, thing to give you your
810
00:40:07,406 --> 00:40:10,273
location, so you wanna know that as well, okay?
811
00:40:10,275 --> 00:40:11,808
So definitely wanna look at this thing.
812
00:40:11,810 --> 00:40:14,978
If you're doing location manager in your final project,
813
00:40:14,980 --> 00:40:19,482
you got to implement this, okay? All right,
814
00:40:19,484 --> 00:40:21,718
lets talk about getting your location in the background,
815
00:40:21,720 --> 00:40:24,487
verses about getting your location in the foreground.
816
00:40:24,489 --> 00:40:26,723
Okay, so when you get in the foreground it's all
817
00:40:26,725 --> 00:40:29,225
the things we just talked about.
818
00:40:29,227 --> 00:40:31,628
There's a bird in here with it.
819
00:40:31,630 --> 00:40:32,929
When you're in the background, though,
820
00:40:32,931 --> 00:40:34,764
things are a little different because you know that
821
00:40:34,766 --> 00:40:36,800
generally you're not allowed to run in the background.
822
00:40:36,802 --> 00:40:40,170
You kind of go quiet, and then you get in those cycles. But
823
00:40:40,172 --> 00:40:42,439
there are ways you, you remember when we went and
824
00:40:42,441 --> 00:40:45,408
enabled iCloud in that capabilities thing? Well,
825
00:40:45,410 --> 00:40:48,478
there's things in there where you can enable backgrounding
826
00:40:48,480 --> 00:40:52,015
for locations, okay? Now if you do this
827
00:40:52,017 --> 00:40:53,883
you will continue to get stuff in the background.
828
00:40:53,885 --> 00:40:56,653
You can imagine it's even more important to be sure you're
829
00:40:56,655 --> 00:40:58,188
not sucking the battery at this,
830
00:40:58,190 --> 00:41:01,090
in this situation, okay? And this is the kind of thing,
831
00:41:01,092 --> 00:41:03,326
if you're sucking the battery in the background there,
832
00:41:03,328 --> 00:41:06,162
your App Store app might not get approved, okay?
833
00:41:06,164 --> 00:41:07,764
That's how important it is not to be sitting there
834
00:41:07,766 --> 00:41:10,633
sucking the battery in the background, okay? Now,
835
00:41:10,635 --> 00:41:14,304
there are other ways though, very battery-efficient ways,
836
00:41:14,306 --> 00:41:15,738
to get your location in the background, so
837
00:41:15,740 --> 00:41:19,476
let's talk about those. One is that significant monitoring I
838
00:41:19,478 --> 00:41:22,779
thing I was telling you about where you're walking around,
839
00:41:22,781 --> 00:41:24,814
and it notices a new cell tower so
840
00:41:24,816 --> 00:41:27,183
it's very kind of large grained.
841
00:41:27,185 --> 00:41:29,953
That's an awesome way to get locations for
842
00:41:29,955 --> 00:41:33,289
very low battery, uses almost no battery to do this. And
843
00:41:33,291 --> 00:41:35,959
what's more and really cool about it, if you're app is in
844
00:41:35,961 --> 00:41:39,662
the background. You'll get the notification if it's not even
845
00:41:39,664 --> 00:41:44,234
running, it will get launched and told this information. So
846
00:41:44,236 --> 00:41:49,472
this a very powerful telling you where the location is.
847
00:41:49,474 --> 00:41:51,074
And when it gets launched, by the way, if,
848
00:41:51,076 --> 00:41:53,776
if they're using some other app, it will launch it and
849
00:41:53,778 --> 00:41:54,844
launch you in the background.
850
00:41:54,846 --> 00:41:56,513
So you can figure out where you are and
851
00:41:56,515 --> 00:41:57,747
decide if you need to do something.
852
00:41:57,749 --> 00:42:00,416
Okay, so this one is really cool, really low power,
853
00:42:00,418 --> 00:42:03,520
the only problem with it, it's large grained. Now,
854
00:42:03,522 --> 00:42:06,823
once you get woken up, that you've significantly changed,
855
00:42:06,825 --> 00:42:10,793
you could start doing more close location, KGPS location,
856
00:42:10,795 --> 00:42:12,662
try and find out a little bit exactly where you are,
857
00:42:12,664 --> 00:42:16,666
if you want. Although be careful not to take too long,
858
00:42:16,668 --> 00:42:17,133
when you get launched for
859
00:42:17,135 --> 00:42:19,802
this in the background because the system will stop doing
860
00:42:19,804 --> 00:42:24,674
this. If you, you know starts consuming a lot of resources
861
00:42:24,676 --> 00:42:29,779
in the background there, okay? So be careful with that.
862
00:42:29,781 --> 00:42:32,615
Similarly is the region base monitoring, okay?
863
00:42:32,617 --> 00:42:34,551
Where you specify a circle or beacons,
864
00:42:34,553 --> 00:42:38,254
okay? That also will wake you up and even launch your
865
00:42:38,256 --> 00:42:41,991
application if it notices you going into that region. Okay,
866
00:42:41,993 --> 00:42:45,862
and run you in the background. Also low power, you know,
867
00:42:45,864 --> 00:42:48,798
really nice way to do it. So if that will meet your needs
868
00:42:48,800 --> 00:42:52,535
as well, that's a good one to do, okay.
869
00:42:52,537 --> 00:42:56,039
When you enter a region, either beacon or
870
00:42:56,041 --> 00:42:59,576
circular region, you're gonna get these delegate methods.
871
00:42:59,578 --> 00:43:01,244
didEnterRegion, didExitRegion, and
872
00:43:01,246 --> 00:43:05,114
monitoringDidFailForRegion. Also here's the error handler
873
00:43:05,116 --> 00:43:09,118
for that one, okay? So that's kind of a fun one.
874
00:43:09,120 --> 00:43:12,355
Region-monitoring works when you're not running because all
875
00:43:12,357 --> 00:43:15,592
your regions are named, okay? They all have to have unique
876
00:43:15,594 --> 00:43:18,695
names, and so when you get launched you'll know which one
877
00:43:18,697 --> 00:43:23,600
fired by its name. You also, there is by the way a maximum
878
00:43:23,602 --> 00:43:26,269
limit to the circular region called maximum region
879
00:43:26,271 --> 00:43:28,504
monitoring distance, you want to look at that.
880
00:43:28,506 --> 00:43:30,940
You can't say when I enter the United States,
881
00:43:30,942 --> 00:43:33,009
okay that won't take that big of a region.
882
00:43:33,011 --> 00:43:36,546
It has to be much much smaller,and this will tell you
883
00:43:36,548 --> 00:43:41,084
how big it can be, okay? This is the beacon thing.
884
00:43:41,086 --> 00:43:42,518
When you're looking for beacons,
885
00:43:42,520 --> 00:43:45,288
what you're really interested in is how far from the beacon
886
00:43:45,290 --> 00:43:48,858
am I? Okay, am I close to it, like really, really next to
887
00:43:48,860 --> 00:43:51,294
it? Or a little bit far away or across the ways. So
888
00:43:51,296 --> 00:43:54,364
beacons are used for things like, you're going to a coffee
889
00:43:54,366 --> 00:43:56,599
shop and when you get right up to the register.
890
00:43:56,601 --> 00:43:58,701
Maybe it offers you a coupon or something like that,
891
00:43:58,703 --> 00:44:00,803
well it needs to know you're right near the register.
892
00:44:00,805 --> 00:44:03,773
But maybe when you just walk in the store it just gives you
893
00:44:03,775 --> 00:44:07,010
an advertisement or directs you to some display, okay?
894
00:44:07,012 --> 00:44:09,679
So the beacon would be sitting maybe near the register and
895
00:44:09,681 --> 00:44:12,515
so this thing is telling you. That's why we call it ranging,
896
00:44:12,517 --> 00:44:15,652
start ranging these beacons. Now these beacons could be
897
00:44:15,654 --> 00:44:19,055
other iOS devices, could serve as a beacon, okay? Or
898
00:44:19,057 --> 00:44:23,159
you could even buy these third party stand alone eye beacons
899
00:44:23,161 --> 00:44:26,963
basically that will act as beacons. If you wanted to be
900
00:44:26,965 --> 00:44:30,033
a beacon, okay, that's beyond the scope of this class.
901
00:44:30,035 --> 00:44:32,135
I can't really tell you how to do that, okay.
902
00:44:32,137 --> 00:44:35,471
You need to get the Core Bluetooth Framework involved
903
00:44:35,473 --> 00:44:37,940
here and check out CBPeripheralManager.
904
00:44:37,942 --> 00:44:41,344
It will show you how to do it but it's a little more
905
00:44:41,346 --> 00:44:44,747
complicated than I can show in a couple slides so.
906
00:44:44,749 --> 00:44:48,084
So that's it for core location. Let's now talk about
907
00:44:48,086 --> 00:44:50,687
Map Kit which is the UI way of showing location.
908
00:44:50,689 --> 00:44:55,191
MKMapView is a UI view that displays a map, okay?
909
00:44:55,193 --> 00:44:59,862
Looks just like the maps app, on iOS.
910
00:44:59,864 --> 00:45:03,533
A map can have annotations, like this little red pin
911
00:45:03,535 --> 00:45:07,570
down there, okay? Each annotation has a coordinate,
912
00:45:07,572 --> 00:45:11,140
obviously the GPS location where that thing is, and also
913
00:45:11,142 --> 00:45:15,545
a title. And a subtitle, okay, there's no subtitle shown
914
00:45:15,547 --> 00:45:16,412
in this one right here,
915
00:45:16,414 --> 00:45:21,451
okay? The annotations though can also have this call out,
916
00:45:21,453 --> 00:45:24,587
this white area. That's not part of the annotation view,
917
00:45:24,589 --> 00:45:29,625
that's a call out that the annotation view brings up. So
918
00:45:29,627 --> 00:45:32,595
we're going to talk about the components of this callout
919
00:45:32,597 --> 00:45:35,431
in a second as well. Actually it's talking right now.
920
00:45:35,433 --> 00:45:38,301
[COUGH] It's got a left accessory view and
921
00:45:38,303 --> 00:45:39,335
a right accessory view.
922
00:45:39,337 --> 00:45:42,505
And these are usually things like UI image views or
923
00:45:42,507 --> 00:45:46,843
maybe UI buttons things like that, okay. So this is how
924
00:45:46,845 --> 00:45:51,047
we're going to build our map UIs out of these components.
925
00:45:51,049 --> 00:45:52,281
So how do you create with MapView?
926
00:45:52,283 --> 00:45:55,852
Really easy. MKMapView or MKMapView with a frame,
927
00:45:55,854 --> 00:45:59,388
initializer, or you can drag it out in your storyboard,
928
00:45:59,390 --> 00:46:02,759
which is what we'll do in the demo. And MapView, what
929
00:46:02,761 --> 00:46:06,129
does it, how does it basically work? It's got an array,
930
00:46:06,131 --> 00:46:09,932
this var annotations which is an array of MKAnnotation,
931
00:46:09,934 --> 00:46:15,271
okay. MKAnnotation is not a class, it's a protocol. So
932
00:46:15,273 --> 00:46:18,074
anything could be an annotation on a map as long as
933
00:46:18,076 --> 00:46:21,577
it implements this protocol. What is that protocol?
934
00:46:21,579 --> 00:46:26,048
It has coordinate, title, and subtitle,
935
00:46:26,050 --> 00:46:29,085
okay? If you implement those three bars, boom,
936
00:46:29,087 --> 00:46:32,655
you can be thrown onto a map. You will be an MKAnnotation.
937
00:46:32,657 --> 00:46:36,626
They're all get, see? Var title is an optional string by
938
00:46:36,628 --> 00:46:38,528
the way but it's really expected to be implemented.
939
00:46:38,530 --> 00:46:41,998
Don't ever leave this nil or system does not like that,
940
00:46:42,000 --> 00:46:45,201
okay? I'm not sure why, it's optional, should be and then
941
00:46:45,203 --> 00:46:47,937
the core location coordination coordinate remember that's
942
00:46:47,939 --> 00:46:53,643
cllocation coordinate 2D, latitude and longitude Ok,
943
00:46:53,645 --> 00:46:56,445
so that's it. So it's got this array of annotations but
944
00:46:56,447 --> 00:47:00,550
you notice this array is read only. So to add annotations or
945
00:47:00,552 --> 00:47:03,719
remove annotations you have to use these methods, add or
946
00:47:03,721 --> 00:47:05,087
remove annotations.
947
00:47:06,825 --> 00:47:08,991
It's generally a good idea from a performance perspective
948
00:47:08,993 --> 00:47:13,229
to add all of your annotations that you know about up front.
949
00:47:13,231 --> 00:47:14,564
And that's because the annotation views,
950
00:47:14,566 --> 00:47:19,101
those little pins. Get reused just like in a table view,
951
00:47:19,103 --> 00:47:21,671
okay? So you might as well add them all upfront and
952
00:47:21,673 --> 00:47:24,974
let the system reuse the pins as you scroll,
953
00:47:24,976 --> 00:47:30,246
scroll around the world. Okay, can reuse them. What do
954
00:47:30,248 --> 00:47:32,782
annotations look like on the map? Well we saw it before,
955
00:47:32,784 --> 00:47:37,453
right? If you use an MKPinAnnotationView, which is
956
00:47:37,455 --> 00:47:40,890
a subclass of MKAnnotationView you get a little pin.
957
00:47:40,892 --> 00:47:44,126
It can be red or purple I think or maybe another
958
00:47:44,128 --> 00:47:47,063
color. But it does allow you to even change this image. If
959
00:47:47,065 --> 00:47:49,599
you don't like a pin, you can change it to something else.
960
00:47:49,601 --> 00:47:51,934
And when you click on the PinAnnotationView,
961
00:47:51,936 --> 00:47:52,568
you get this call out. And
962
00:47:52,570 --> 00:47:56,973
that's what the call out looks like, okay? So what happens
963
00:47:56,975 --> 00:48:00,509
when you press the pin besides this call-out coming out? And
964
00:48:00,511 --> 00:48:05,214
by the way, this call-out only comes out if canShowCallout is
965
00:48:05,216 --> 00:48:08,551
true on the MK annotation pin annotation view.
966
00:48:08,553 --> 00:48:11,520
So you have to have that be true. So this will come up,
967
00:48:11,522 --> 00:48:14,690
but in addition to this appearing, a delegate method
968
00:48:14,692 --> 00:48:17,994
will be sent to the map views delegate. It's called
969
00:48:17,996 --> 00:48:22,999
Map view did select annotation view, okay? And so this
970
00:48:23,001 --> 00:48:25,601
is important little method right here because if you're
971
00:48:25,603 --> 00:48:30,039
gonna show anything expensive in your call out, like so
972
00:48:30,041 --> 00:48:32,008
you're gonna go over their network and grab an image or
973
00:48:32,010 --> 00:48:34,777
something like that. You're gonna wanna wait to do it
974
00:48:34,779 --> 00:48:37,580
until didSelectAnnotationView. Because if the user never
975
00:48:37,582 --> 00:48:40,549
clicks on this pin, he's never gonna see that image. So you
976
00:48:40,551 --> 00:48:43,552
don't wanna waste your time fetching it. Okay? So this
977
00:48:43,554 --> 00:48:45,821
is a place where you're gonna do expensive stuff that's
978
00:48:45,823 --> 00:48:48,291
gonna show in the call out. You also, though,
979
00:48:48,293 --> 00:48:51,027
might segue out of this. When someone clicks on a pin,
980
00:48:51,029 --> 00:48:54,463
maybe you wanna go segue to some other view, I don't know.
981
00:48:54,465 --> 00:48:58,868
Okay, but this is the target action of MKAnnotations.
982
00:48:59,003 --> 00:49:01,671
All right, so how are these AnnotationViews, these little
983
00:49:01,673 --> 00:49:04,440
pins created and associated with those annotations?
984
00:49:04,442 --> 00:49:07,610
Because we know MKMap View only has this array of
985
00:49:07,612 --> 00:49:11,347
annotations. So how do those pins get there to show them?
986
00:49:11,349 --> 00:49:14,550
And the answer is from this delegate method right here,
987
00:49:14,552 --> 00:49:15,651
MapView, viewForAnnotation.
988
00:49:15,653 --> 00:49:18,888
This is very much like self or roll in index path in table
989
00:49:18,890 --> 00:49:22,091
view. Okay? So in there, you're creating a cell which
990
00:49:22,093 --> 00:49:25,361
is a view. Okay, UI table view cell. Here you're creating
991
00:49:25,363 --> 00:49:28,864
an MK annotation view like the Pin annotation view. Okay. So
992
00:49:28,866 --> 00:49:31,600
let's go through this and see how we do it. First we're
993
00:49:31,602 --> 00:49:36,505
gonna dq a reusable one using some identifier but
994
00:49:36,507 --> 00:49:40,876
if that fail, okay and view is nil if we can't dq one
995
00:49:40,878 --> 00:49:44,914
then there's no prototype like there is in the table view so
996
00:49:44,916 --> 00:49:47,717
we have to create the prototype in code. So
997
00:49:47,719 --> 00:49:49,185
here I'm creating the prototype by saying,
998
00:49:49,187 --> 00:49:52,955
view equals MK pin Annotation view, okay? But I'm specifying
999
00:49:52,957 --> 00:49:56,559
that sam reuse identifier that later I might come back and do
1000
00:49:56,561 --> 00:49:59,595
this reuseable with, you see? So that's how they get done.
1001
00:49:59,597 --> 00:50:02,665
And then can't show call out either set to true or false,
1002
00:50:02,667 --> 00:50:05,768
whichever one you want. Okay.
1003
00:50:05,770 --> 00:50:07,570
Then we're gonna whether we created the view or
1004
00:50:07,572 --> 00:50:10,206
not, we're gonna wanna set the view's annotation to be
1005
00:50:10,208 --> 00:50:15,044
the annotation we're creating, this view for. And then,
1006
00:50:15,046 --> 00:50:18,881
we're gonna wanna prepare this view with anything else.
1007
00:50:18,883 --> 00:50:21,817
Like if it's gonna have a left call out accessor view,
1008
00:50:21,819 --> 00:50:22,318
which is an image view,
1009
00:50:22,320 --> 00:50:25,354
this is the time we would add the image view. This might not
1010
00:50:25,356 --> 00:50:28,624
be the time we actually go fetch the image to put there,
1011
00:50:28,626 --> 00:50:31,427
you might wait until didSelectAnnotationView for
1012
00:50:31,429 --> 00:50:34,630
you to do that but we're not gonna wanna put the UI image
1013
00:50:34,632 --> 00:50:37,867
view in there so that it's ready to go in the call out.
1014
00:50:37,869 --> 00:50:41,537
Okay, and then we're gonna return the view then
1015
00:50:41,539 --> 00:50:45,141
the system gonna use this view to show that pin.
1016
00:50:45,143 --> 00:50:47,777
Okay, interesting properties on UIAnnotationView,
1017
00:50:47,779 --> 00:50:49,078
we already talked about the left and
1018
00:50:49,080 --> 00:50:51,514
right CallOutAccessoryViews, which are just views.
1019
00:50:51,516 --> 00:50:55,317
There's also whether it's enabled or not. This image is
1020
00:50:55,319 --> 00:50:58,087
the image of the pin, not the image that's in the callout,
1021
00:50:58,089 --> 00:51:00,656
it's the image of the pin. Also you can
1022
00:51:00,658 --> 00:51:03,359
make it draggable, setting draggable to true. If you do
1023
00:51:03,361 --> 00:51:06,529
that your MKAnnotations, the coordinate property would have
1024
00:51:06,531 --> 00:51:09,465
to be get and set. Obviously if you're gonna drag it around
1025
00:51:09,467 --> 00:51:12,268
you have to be able to set the property as well.
1026
00:51:12,870 --> 00:51:16,605
Okay. This cool property or
1027
00:51:16,607 --> 00:51:19,642
delegate method right here calloutAccessoryControlTapped
1028
00:51:19,644 --> 00:51:23,813
will get sent to your map view delegates If your left or
1029
00:51:23,815 --> 00:51:27,049
right call out accessory is a ui control.
1030
00:51:27,051 --> 00:51:29,852
Basically a ui button okay? So if you put a button
1031
00:51:29,854 --> 00:51:32,455
at your left or right call out accessory when it gets touched
1032
00:51:32,457 --> 00:51:36,459
you're gonna get this call out accessory control tapped
1033
00:51:36,861 --> 00:51:39,695
method which is pretty cool.
1034
00:51:39,697 --> 00:51:42,631
In did select annotation view we talked about the fact
1035
00:51:42,633 --> 00:51:46,268
that this is where you might want to create your image or
1036
00:51:46,270 --> 00:51:50,706
even fire off a thread or q, on a different q, fire off
1037
00:51:50,708 --> 00:51:53,509
a request to go get the image. When it comes back later
1038
00:51:53,511 --> 00:51:57,813
you'll have to load it up into the accessory view. Okay?
1039
00:51:57,815 --> 00:51:59,982
Just like when you're scrolling around in a table
1040
00:51:59,984 --> 00:52:02,685
view, realize that if you're scrolling around on the map
1041
00:52:02,687 --> 00:52:06,455
you might fire off a dispatch async here to go get this
1042
00:52:06,457 --> 00:52:09,758
image, and when it comes back that image is no longer being
1043
00:52:09,760 --> 00:52:12,761
displayed in this call accessory view because it gets
1044
00:52:12,763 --> 00:52:17,566
reused, reused right by other ones. So be careful about
1045
00:52:17,568 --> 00:52:21,537
that. The map you can configure the way it displays.
1046
00:52:21,539 --> 00:52:24,673
This is a var on MKMapView. You can do standard,
1047
00:52:24,675 --> 00:52:27,710
satellite, or hybrid. So standard is like streets and
1048
00:52:27,712 --> 00:52:31,447
things like that, satellite is like Google Earth type thing.
1049
00:52:31,449 --> 00:52:35,251
And then hybrid is a mix, an overlay of those two things.
1050
00:52:35,253 --> 00:52:39,155
You can also show the user's current location. On the map
1051
00:52:39,157 --> 00:52:40,923
just by saying this Bool to true and it will show it.
1052
00:52:40,925 --> 00:52:43,659
I think the users current location is blue or maybe it's
1053
00:52:43,661 --> 00:52:46,128
purple, I don't know. But it will just show in there.
1054
00:52:46,130 --> 00:52:48,864
You can also get the users location as well.
1055
00:52:48,866 --> 00:52:51,500
As MKUserLocation which is just thing that implements
1056
00:52:51,502 --> 00:52:56,472
the MKAnnotation protocol. You can also restrict scrolling.
1057
00:52:56,474 --> 00:52:59,008
Okay, maybe you don't want. The 3D mode.
1058
00:52:59,010 --> 00:53:01,544
If you don't want 3D mode, set pitch enable to false and
1059
00:53:01,546 --> 00:53:06,248
it won't pitch up to show you 3D maps. You can control
1060
00:53:06,250 --> 00:53:08,851
the camera if you do allow 3D you can control
1061
00:53:08,853 --> 00:53:12,254
where the camera is pointing using this, API right here,
1062
00:53:12,256 --> 00:53:16,025
that's kind of fun. You can also say what part of
1063
00:53:16,027 --> 00:53:19,094
the world is being shown with the MP coordinate region which
1064
00:53:19,096 --> 00:53:22,998
is just a latitude longitude and a span okay which is
1065
00:53:23,000 --> 00:53:28,370
number of meters. Okay? So it shows that will show that part
1066
00:53:28,372 --> 00:53:32,208
of the Earth. Actually it's not it's not even meters.
1067
00:53:32,210 --> 00:53:35,044
The span is a delta of latitude and longitude.
1068
00:53:35,046 --> 00:53:36,078
How many degrees of latitude and
1069
00:53:36,080 --> 00:53:38,581
how many degrees of longitude to show.
1070
00:53:39,217 --> 00:53:43,886
Okay? What else we can do here?
1071
00:53:43,888 --> 00:53:46,722
I just want you to note that there's a whole bunch of
1072
00:53:46,724 --> 00:53:49,892
C-like functions, they're swift global functions,
1073
00:53:49,894 --> 00:53:52,494
they're not methods, that you can do to convert
1074
00:53:52,496 --> 00:53:55,064
like from map coordinates to view coordinates and
1075
00:53:55,066 --> 00:53:58,300
things like that. So make sure you take a look at these and
1076
00:53:58,302 --> 00:54:01,270
understand all of these. There's probably about 15 or
1077
00:54:01,272 --> 00:54:03,906
20 of them that will help you do conversions.
1078
00:54:03,908 --> 00:54:08,110
There's also some methods that will do it, as well,
1079
00:54:08,112 --> 00:54:13,415
in MKMapView. There's a cool method in MKMapView called
1080
00:54:13,417 --> 00:54:15,618
didChangeRegionAnimated.
1081
00:54:15,620 --> 00:54:18,087
Okay? So, if you tell the mapview to
1082
00:54:18,089 --> 00:54:21,257
show a different part of the world like New York, okay.
1083
00:54:21,259 --> 00:54:23,559
It's going to animate going there. It's going to
1084
00:54:23,561 --> 00:54:26,695
scroll over there. But if I'm in San Francisco and I say,
1085
00:54:26,697 --> 00:54:29,999
show New York it's gonna go whoo, okay, you're not even
1086
00:54:30,001 --> 00:54:32,534
gonna see it. Nebraska, you'll never see it okay.
1087
00:54:32,536 --> 00:54:35,337
In Nebraska, you can go by so fast. You'll never see it.
1088
00:54:35,339 --> 00:54:36,905
What would be really cool is if you said,
1089
00:54:36,907 --> 00:54:39,408
I'm in San Francisco, show New York, first you said
1090
00:54:39,410 --> 00:54:42,678
show the whole United States then show New York.
1091
00:54:42,680 --> 00:54:45,681
Right then you get this cool animation. San Francisco to
1092
00:54:45,683 --> 00:54:47,249
the whole United States down to New York.
1093
00:54:47,251 --> 00:54:49,318
Wouldn't that be cool? Yes it would. And so
1094
00:54:49,320 --> 00:54:52,621
the way you do that is you first of all set your location
1095
00:54:52,623 --> 00:54:53,489
to the whole United States or
1096
00:54:53,491 --> 00:54:56,358
middle of the United States with a large span and it will
1097
00:54:56,360 --> 00:54:59,695
go up to there and as soon as the animation of that finishes
1098
00:54:59,697 --> 00:55:04,166
you'll get sent this. Okay cuz it's a did change region.
1099
00:55:04,168 --> 00:55:04,533
It finished the change region.
1100
00:55:04,535 --> 00:55:08,537
As soon as it sends you this now you animate to New York.
1101
00:55:08,706 --> 00:55:10,005
So essentially chaining you see.
1102
00:55:10,007 --> 00:55:11,907
And now it will [INAUDIBLE] New York. So this is the way
1103
00:55:11,909 --> 00:55:14,276
you can chain animations to make the animations kind of
1104
00:55:14,278 --> 00:55:18,480
fly around the world nicely. MKLocalSearch I'm not going to
1105
00:55:18,482 --> 00:55:21,750
really talk about it but it's a way that you can put normal
1106
00:55:21,752 --> 00:55:25,321
English language descriptions of places like Ike's, and
1107
00:55:25,323 --> 00:55:27,423
it'll go and search. This is asynchronous,
1108
00:55:27,425 --> 00:55:30,626
cuz it's gonna go out on the internet to look this up. But
1109
00:55:30,628 --> 00:55:32,961
it'll come back and give you a bunch of,
1110
00:55:32,963 --> 00:55:38,734
locations that might match this search. Okay? Okay.
1111
00:55:38,903 --> 00:55:41,470
Similarly you can asked for the directions you can
1112
00:55:41,472 --> 00:55:44,273
say I wanna go from here to here and I'm gonna drive,
1113
00:55:44,275 --> 00:55:46,942
give me directions, and it will give you this thing
1114
00:55:46,944 --> 00:55:49,845
called MKRoute with also have turn by turn directions.
1115
00:55:49,847 --> 00:55:53,982
And also have an MKPolyline which is an object which will
1116
00:55:53,984 --> 00:55:56,652
draw this blue line right here Okay, and
1117
00:55:56,654 --> 00:55:59,888
how do you get this blue line showing on here?
1118
00:55:59,890 --> 00:56:02,624
You're gonna use another feature called overlays, okay.
1119
00:56:02,626 --> 00:56:05,728
Overlays are very much like annotations, but instead of
1120
00:56:05,730 --> 00:56:09,064
you have view for annotation, you have overlay view for
1121
00:56:09,066 --> 00:56:11,734
annotation or renderer for annotation.
1122
00:56:11,736 --> 00:56:15,137
Okay, and this renderer could be something like a polyline
1123
00:56:15,139 --> 00:56:17,506
renderer, and if you have a polyline renderer, and
1124
00:56:17,508 --> 00:56:21,643
you give it an MKPolyline that comes from that root thing,
1125
00:56:21,645 --> 00:56:24,146
you can draw that root, okay?
1126
00:56:24,148 --> 00:56:26,148
You also use overlays for your on own thing,
1127
00:56:26,150 --> 00:56:29,785
if you wanna draw a bounding area around something, or
1128
00:56:29,787 --> 00:56:33,255
you wanna draw your own path of where you went, you can do
1129
00:56:33,257 --> 00:56:36,492
all that with overlays. Okay if you wanna do that in your
1130
00:56:36,494 --> 00:56:38,761
final project, it'll count as not covered in lecture,
1131
00:56:38,763 --> 00:56:39,728
cuz I'm not really covering it,
1132
00:56:39,730 --> 00:56:41,130
I'm just telling you it's there, okay?
1133
00:56:41,132 --> 00:56:42,431
Same thing as local search and
1134
00:56:42,433 --> 00:56:46,268
all these things I've kind of glossed over here, okay?
1135
00:56:46,270 --> 00:56:49,104
These are some of the built-in renderers, polyline,
1136
00:56:49,106 --> 00:56:54,076
also circles, polygons, tiling, etc. Okay,
1137
00:56:54,078 --> 00:56:56,945
so that's it. Now we're gonna start a demo that's gonna last
1138
00:56:56,947 --> 00:57:01,650
across both lectures here, and we're gonna start by just
1139
00:57:01,652 --> 00:57:05,721
doing a simple app that takes some waypoints and throws them
1140
00:57:05,723 --> 00:57:09,291
on a map, so we can see how to do that. Also on Wednesday, in
1141
00:57:09,293 --> 00:57:12,394
addition to finishing up this demo I'm gonna cover the last
1142
00:57:12,396 --> 00:57:13,996
topic of the quarter which is persistence,
1143
00:57:13,998 --> 00:57:15,798
which is basically how to store things in the file
1144
00:57:15,800 --> 00:57:21,537
system, right? Opening files and all that business. Okay,
1145
00:57:21,539 --> 00:57:24,873
so let's go start a new app here.
1146
00:57:24,875 --> 00:57:29,044
We're gonna create a brand new one. We'll create on iOS app,
1147
00:57:29,046 --> 00:57:33,015
Single View, as usual. We'll call this one Trax, okay?
1148
00:57:33,017 --> 00:57:37,052
Cuz it's gonna keep track of where you have been.
1149
00:57:37,054 --> 00:57:40,923
We'll make it a Universal app, so it works on iPhone and
1150
00:57:40,925 --> 00:57:46,595
iPad. There we go, we'll put it in our standard location.
1151
00:57:46,831 --> 00:57:50,265
All right, so here's Trax. In the storyboard right here
1152
00:57:50,267 --> 00:57:54,303
I'm going to, do my standard thing. We're gonna move,
1153
00:57:54,305 --> 00:57:58,373
some of these things off to Supporting Files here.
1154
00:57:58,375 --> 00:57:58,740
Supporting Files.
1155
00:57:58,742 --> 00:58:01,844
We're not gonna be needing to do anything in AppDelegate, so
1156
00:58:01,846 --> 00:58:04,513
I'll move it off to Supporting flies, Files here.
1157
00:58:04,515 --> 00:58:07,883
In my storyboard, I actually don't want my ViewController
1158
00:58:07,885 --> 00:58:10,319
here to be called generic View Controller.
1159
00:58:10,321 --> 00:58:13,856
So I'm gonna rename it to be GPXViewController,
1160
00:58:13,858 --> 00:58:16,492
cuz what we're gonna do is take a GPX file which,
1161
00:58:16,494 --> 00:58:19,194
like I said before, is just a file that contains a bunch
1162
00:58:19,196 --> 00:58:21,597
of GPS coordinates, and we're gonna put it on our map.
1163
00:58:21,599 --> 00:58:26,034
So we're gonna call this our GPXViewController, so
1164
00:58:26,036 --> 00:58:28,537
I'm gonna rename it here as well,
1165
00:58:28,539 --> 00:58:31,206
and also in my storyboard and go here to
1166
00:58:31,208 --> 00:58:35,978
the Identity Inspector change this to GPXViewController.
1167
00:58:35,980 --> 00:58:40,782
Okay, now let's also go ahead and build our UI here.
1168
00:58:40,784 --> 00:58:44,019
Our UI is gonna be all map, all the time. Okay, our entire
1169
00:58:44,021 --> 00:58:48,390
UI is just gonna be a gigantic map. So I'm gonna go down to
1170
00:58:48,392 --> 00:58:50,592
my object palette down here and try and find map,
1171
00:58:50,594 --> 00:58:53,262
actually, I'm gonna search for it by just typing map.
1172
00:58:53,264 --> 00:58:56,265
Here it is, a Map Kit View, and you drag that out and
1173
00:58:56,267 --> 00:59:00,836
put it here. Make it fill the entire MVC scene.
1174
00:59:00,838 --> 00:59:03,705
Of course, we'll do Reset to Suggested Constraints, and
1175
00:59:03,707 --> 00:59:06,441
we'll jump over here to our size inspector and make sure
1176
00:59:06,443 --> 00:59:09,411
it did the right thing. Sure looks like it did. Let's go
1177
00:59:09,413 --> 00:59:12,748
ahead and make an outlet to this from our controller. So
1178
00:59:12,750 --> 00:59:17,052
I'm just gonna Ctrl+drag in here. I'll call it my mapView.
1179
00:59:17,054 --> 00:59:20,756
Okay, you can see that it's a type MKMapView right there. So
1180
00:59:20,758 --> 00:59:25,961
we have our Map View. Let's go ahead and when our mapView
1181
00:59:25,963 --> 00:59:29,197
is set, let's go ahead and configure our mapView what we
1182
00:59:29,199 --> 00:59:35,337
want it to look like. So how about the mapView's, mapType.
1183
00:59:35,806 --> 00:59:38,206
And see it's not doing escape completion here.
1184
00:59:38,208 --> 00:59:40,842
Why not? Import MapKit.
1185
00:59:40,844 --> 00:59:44,012
Okay, just like CloudKit, it's a separate framework.
1186
00:59:44,014 --> 00:59:45,981
We need it, or it's not gonna know any of these types.
1187
00:59:45,983 --> 00:59:49,885
So we'll set our mapType to be Satellite, okay,
1188
00:59:49,887 --> 00:59:54,890
satellite images. Let's set our delegate,
1189
00:59:54,892 --> 00:59:59,361
of course. Nothing works in a mapView without a delegate, so
1190
00:59:59,363 --> 01:00:02,030
we absolutely have to use delegate, and
1191
01:00:02,032 --> 01:00:05,267
that means we'll have to be an MKMapViewDelegate.
1192
01:00:05,269 --> 01:00:09,171
All right, so we have our mapview set up here nicely,
1193
01:00:09,173 --> 01:00:14,876
oops. Okay, so now we need our model.
1194
01:00:14,878 --> 01:00:17,279
What's gonna be our model to this thing?
1195
01:00:17,281 --> 01:00:22,284
It's going to be a gpxURL. Okay, so this is gonna be
1196
01:00:22,286 --> 01:00:26,054
the URL to a GPX file, a file that contains coordinates.
1197
01:00:26,056 --> 01:00:27,823
And we're just gonna open that file,
1198
01:00:27,825 --> 01:00:30,592
look at all the GPX coordinates and display them.
1199
01:00:30,594 --> 01:00:35,097
And in fact, let's go ahead and in our viewDidLoad here
1200
01:00:35,099 --> 01:00:40,369
just load one up. So let's see, gpxURL equals,
1201
01:00:40,371 --> 01:00:45,674
how about this one right here, a string, which is
1202
01:00:45,676 --> 01:00:51,246
http://cs193p.stanford.edu/Va- cation,
1203
01:00:51,248 --> 01:00:55,951
oops, Vacation.gpx. Okay, so that's just a,
1204
01:00:55,953 --> 01:01:01,390
a URL I have around there that we can look at. Now this
1205
01:01:01,392 --> 01:01:05,560
is an http, so we'd better go over to our info.plist,
1206
01:01:05,562 --> 01:01:10,232
right here, and do the thing we always do here where we add
1207
01:01:10,234 --> 01:01:13,535
our App Transport settings right here.
1208
01:01:13,537 --> 01:01:18,507
Let's add Arbitrary Loads allowed, and we will
1209
01:01:18,509 --> 01:01:21,977
say YES. Everyone understand why we're doing that,
1210
01:01:21,979 --> 01:01:26,281
same thing we had to do with smash tag there. All right, so
1211
01:01:26,283 --> 01:01:29,918
we're all set up here, what are we gonna do when this is
1212
01:01:29,920 --> 01:01:35,323
set? Okay, so when our model is set, when this URL is set,
1213
01:01:35,325 --> 01:01:40,529
what are we gonna need to do here to make this work? Well,
1214
01:01:40,531 --> 01:01:43,265
if they set the URL equal to something, so
1215
01:01:43,267 --> 01:01:48,236
they didn't set it to be nil let's say. Then really what
1216
01:01:48,238 --> 01:01:52,841
I need to do is parse this file, this URL somehow. And
1217
01:01:52,843 --> 01:01:56,545
to do that, I'm going to introduce a little class that
1218
01:01:56,547 --> 01:02:00,849
I wrote called GPX, which will parse a GPX file and then give
1219
01:02:00,851 --> 01:02:03,618
you all the waypoints. Okay, so let's go ahead and
1220
01:02:03,620 --> 01:02:08,090
grab that file. It's right here, it's called GPX.swift.
1221
01:02:08,092 --> 01:02:10,492
Copy it in. Take a brief look at this thing,
1222
01:02:10,494 --> 01:02:15,163
what it does. All right, here's GPX, let's go ahead and
1223
01:02:15,165 --> 01:02:18,133
look at it just at its generated interface
1224
01:02:18,135 --> 01:02:23,371
here. No need to look at the code there.
1225
01:02:23,373 --> 01:02:26,208
All right, so here's the GPX, this class. And
1226
01:02:26,210 --> 01:02:30,545
the main thing it has is this, this var waypoints,
1227
01:02:30,547 --> 01:02:34,249
which is a array of GPX.Waypoint.
1228
01:02:34,251 --> 01:02:36,952
Let's go look at the GPX waypoint, that's right here.
1229
01:02:36,954 --> 01:02:40,756
So, the Waypoint is latitude and longitude, of course,
1230
01:02:40,758 --> 01:02:44,659
kind of some information about this waypoint, the date
1231
01:02:44,661 --> 01:02:48,497
the waypoint was captured. And it's also an entry,
1232
01:02:48,499 --> 01:02:51,233
this Waypoint is also an entry, which is this thing.
1233
01:02:51,235 --> 01:02:54,603
The entry can have any number of hyperlinks. So these would
1234
01:02:54,605 --> 01:02:58,240
be like URLs to photos or video that I might have taken
1235
01:02:58,242 --> 01:03:02,611
at this location when I was on my trek or whatever. Also it
1236
01:03:02,613 --> 01:03:06,381
has a name, which is the name of this waypoint, okay? May or
1237
01:03:06,383 --> 01:03:11,686
may not have a name, but if it does this is the name. Okay,
1238
01:03:11,688 --> 01:03:14,623
these links by the way, these GPX links is right here.
1239
01:03:14,625 --> 01:03:18,593
It's basically just URLs, hrefs, here,
1240
01:03:18,595 --> 01:03:21,797
okay. All right, so that's it. That's all this thing does is,
1241
01:03:21,799 --> 01:03:25,333
main thing is this, this array of waypoints, okay? So let's
1242
01:03:25,335 --> 01:03:29,704
go back to our controller here. So we do this GPX.parse.
1243
01:03:29,706 --> 01:03:34,576
Notice that this parse method in GPX is asynchronous.
1244
01:03:34,578 --> 01:03:37,112
It parses that file, and then later it calls you back.
1245
01:03:37,114 --> 01:03:40,682
It's a very nice asynchronous method in that it always calls
1246
01:03:40,684 --> 01:03:42,284
you back on the main queue, okay, so
1247
01:03:42,286 --> 01:03:45,187
we don't have to dispatch async or anything here. And
1248
01:03:45,189 --> 01:03:50,025
the only argument that's given when you do it is a GPX, which
1249
01:03:50,027 --> 01:03:54,596
is an instance of this class, and it has those waypoints.
1250
01:03:54,598 --> 01:03:57,632
So, what are we gonna do with these waypoints when they come
1251
01:03:57,634 --> 01:04:01,937
back? Well, if the, if it was able to parse it, basically if
1252
01:04:01,939 --> 01:04:06,241
this thing came back and it wasn't nil, then we're going
1253
01:04:06,243 --> 01:04:11,546
to add these waypoints to ourself. Waypoints,
1254
01:04:11,548 --> 01:04:14,583
okay, so we're gonna have to add, do this method here.
1255
01:04:14,585 --> 01:04:17,252
Also every time someone said that I'm also gonna clear any
1256
01:04:17,254 --> 01:04:20,522
waypoints that I already have. So I'm gonna implement these
1257
01:04:20,524 --> 01:04:24,292
two methods right here. Okay everybody cool with that? So
1258
01:04:24,294 --> 01:04:27,262
we're just getting those GPS waypoints out of this GPX
1259
01:04:27,264 --> 01:04:29,397
file. So we're gonna implement these two methods. So
1260
01:04:29,399 --> 01:04:34,035
let's do private func clearWaypoints. Okay, so for
1261
01:04:34,037 --> 01:04:37,072
clearWaypoints I just wanna remove all the annotations off
1262
01:04:37,074 --> 01:04:40,375
my map, remember the map just shows annotations? So
1263
01:04:40,377 --> 01:04:40,842
I'm gonna remove them all.
1264
01:04:40,844 --> 01:04:44,713
I'm gonna say mapView removeAnnotations which is
1265
01:04:44,715 --> 01:04:47,816
the mapView's current annotations, okay,
1266
01:04:47,818 --> 01:04:52,187
whatever the mapView currently has. Notice I'm doing this
1267
01:04:52,189 --> 01:04:55,824
MapView question mark here, that's because if this happens
1268
01:04:55,826 --> 01:04:59,828
in like a perform, prepare for segue or something like that,
1269
01:04:59,830 --> 01:05:02,030
then my mapView wouldn't be wired up yet.
1270
01:05:02,032 --> 01:05:04,799
So I don't want this to fail, I just want you to do nothing.
1271
01:05:04,801 --> 01:05:08,436
So that's why I'm doing MapView?. Okay and
1272
01:05:08,438 --> 01:05:13,775
then we have the addWaypoints, private func addWaypoints and
1273
01:05:13,777 --> 01:05:18,146
this one is going to take an array. Waypoints,
1274
01:05:18,148 --> 01:05:23,618
which is an array of these GPX.Waypoint objects. Okay,
1275
01:05:23,620 --> 01:05:25,720
so what do we wanna do in addWaypoints?
1276
01:05:25,722 --> 01:05:28,990
Well, we just want to add them as annotations. So I'm just
1277
01:05:28,992 --> 01:05:33,228
gonna say addAnnotations, these waypoints, right?
1278
01:05:33,230 --> 01:05:37,532
MapView, the other thing I'm gonna do is there's a way in
1279
01:05:37,534 --> 01:05:41,369
the MapView to say, show me all of these annotations.
1280
01:05:41,371 --> 01:05:46,474
You say showAnnotations, showAnnotations, and
1281
01:05:46,476 --> 01:05:47,342
it takes some annotations, and
1282
01:05:47,344 --> 01:05:49,978
I'll just give those same waypoints. And
1283
01:05:49,980 --> 01:05:50,645
I'll say animated true, and
1284
01:05:50,647 --> 01:05:54,582
what this will do is zoom the map to show these annotations,
1285
01:05:54,584 --> 01:06:00,255
okay. Now, a couple errors here. What are these errors?
1286
01:06:00,257 --> 01:06:03,758
Cannot convert value of array of GPS Waypoint to
1287
01:06:03,760 --> 01:06:07,862
expected argument which is array of MKAnnotation.
1288
01:06:07,864 --> 01:06:11,866
Ha! Right I said that the mapView takes MKAnnotations,
1289
01:06:11,868 --> 01:06:15,670
and these are GPS Waypoints. They're not MKAnnotations. But
1290
01:06:15,672 --> 01:06:20,208
we can easily turn a GPX Waypoint into an MKAnnotation
1291
01:06:20,210 --> 01:06:23,011
as long as we get it to implement those three methods,
1292
01:06:23,013 --> 01:06:25,880
right? So how are we gonna do that? Well let's create
1293
01:06:25,882 --> 01:06:29,050
another little file and we're just gonna add protocol
1294
01:06:29,052 --> 01:06:33,121
conformance via an extension which I talked about earlier
1295
01:06:33,123 --> 01:06:35,991
in the quarter but we haven't seen it actually happen. So
1296
01:06:35,993 --> 01:06:36,925
here I'm gonna create a Swift file.
1297
01:06:36,927 --> 01:06:41,096
I'm gonna call it mkgpx because it's MK annotated,
1298
01:06:41,098 --> 01:06:45,000
map kit stuff having to do with this GPX class right
1299
01:06:45,002 --> 01:06:49,938
here. And I'm going to import MapKit, MK, yeah MapKit.
1300
01:06:49,940 --> 01:06:51,740
All right, and what am I gonna do,
1301
01:06:51,742 --> 01:06:55,043
I'm gonna create an extension to GPX.Waypoint, and
1302
01:06:55,045 --> 01:06:58,480
it's going to implement the protocol MKAnnotation.
1303
01:06:58,482 --> 01:07:00,382
That's what this extension is going to do. So
1304
01:07:00,384 --> 01:07:05,453
this is how we can implement a protocol via an extension.
1305
01:07:05,455 --> 01:07:07,288
Okay now all you need to do is implement these things.
1306
01:07:07,290 --> 01:07:12,594
Remember there's coordinates that we have to do which is
1307
01:07:12,596 --> 01:07:17,665
a CLLocationCoordinate2D. We have to implement title, which
1308
01:07:17,667 --> 01:07:20,902
is a String, optional but we have to implement it and
1309
01:07:20,904 --> 01:07:23,805
then there's subtitle which can be any subtitle
1310
01:07:23,807 --> 01:07:27,075
information we want, okay, which is also a String.
1311
01:07:27,077 --> 01:07:29,644
So we just have to implement these three things and
1312
01:07:29,646 --> 01:07:32,113
then we will have successfully,
1313
01:07:32,282 --> 01:07:36,951
added the MKAnnotation conformant to Waypoint.
1314
01:07:36,953 --> 01:07:38,486
So how are we going to implement these things?
1315
01:07:38,488 --> 01:07:41,189
Okay well coordinate this is not coordination this is
1316
01:07:41,191 --> 01:07:44,459
coordinate okay? The coordinate is easy to
1317
01:07:44,461 --> 01:07:47,662
implement because the GPXWaypoint knows latitude and
1318
01:07:47,664 --> 01:07:48,029
So I'm just gonna create a new LocationCoordinate2D okay, and
1319
01:07:48,031 --> 01:07:51,599
longitude.
1320
01:07:51,601 --> 01:07:54,803
it's gonna have latitude which is the latitude,
1321
01:07:54,805 --> 01:07:58,306
it's green there, you see? Because it's a var,
1322
01:07:58,308 --> 01:08:03,244
in this class, and then longitude. Okay, so
1323
01:08:03,246 --> 01:08:05,346
that was easy. How about the title?
1324
01:08:05,348 --> 01:08:07,916
Well, all these GPX Waypoints have that name, so
1325
01:08:07,918 --> 01:08:11,853
I'm just gonna return the name of the GPX Waypoint and
1326
01:08:11,855 --> 01:08:16,357
then they also have this thing called info, which is just
1327
01:08:16,359 --> 01:08:17,992
kind of information about the Waypoint.
1328
01:08:17,994 --> 01:08:22,263
That would be a good subtitle, okay? So just like that I've
1329
01:08:22,265 --> 01:08:25,834
turned GPX Waypoints into MKAnnotations and
1330
01:08:25,836 --> 01:08:28,636
this is how you use the map. You take usually something
1331
01:08:28,638 --> 01:08:30,905
you already have and make it implement this.
1332
01:08:30,907 --> 01:08:33,374
Now you could create a new class that all it does
1333
01:08:33,376 --> 01:08:35,710
is implement MKAnnotation that's fine too, but
1334
01:08:35,712 --> 01:08:38,480
generally, usually have something lying around that
1335
01:08:38,482 --> 01:08:40,248
you can turn into an MKAnnotation. And
1336
01:08:40,250 --> 01:08:44,419
notice as soon as I do that, no more errors here, okay,
1337
01:08:44,421 --> 01:08:48,456
because this is now an array of MKAnnotation, okay,
1338
01:08:48,458 --> 01:08:53,027
all right. So let's go ahead and run this,
1339
01:08:53,029 --> 01:08:56,698
see what happens, lets go here, iPhone6, run,
1340
01:08:56,700 --> 01:08:58,967
see if it can open this GPX file up and
1341
01:08:58,969 --> 01:09:05,874
show us these waypoints. Up, sure enough there it is,
1342
01:09:05,876 --> 01:09:08,510
and it even did that showAnnotations right here,
1343
01:09:08,512 --> 01:09:10,378
okay. That zoomed to where it is so
1344
01:09:10,380 --> 01:09:11,146
it's showing all the annotations
1345
01:09:11,148 --> 01:09:13,915
it found in that GPX file. Let's see if we can see where
1346
01:09:13,917 --> 01:09:19,387
we are here. Zoom out. Looks like we are in what, what is,
1347
01:09:19,389 --> 01:09:25,960
anyone know where that is? Recognize that coastline?
1348
01:09:25,962 --> 01:09:29,497
Canada! Yeah, there's the United States, here's Canada.
1349
01:09:29,499 --> 01:09:32,333
Okay? So here we are, we got these things in Canada. Now,
1350
01:09:32,335 --> 01:09:35,170
if we click on some of these. Let's click on some,
1351
01:09:35,172 --> 01:09:38,540
see what happens. So I click, notice we get the name and
1352
01:09:38,542 --> 01:09:42,544
the info right. The title and the subtitle got it.
1353
01:09:42,546 --> 01:09:47,615
Now I happen to know that this GPX file has URLs of photos
1354
01:09:47,617 --> 01:09:52,020
that were taken at all of these locations, okay?
1355
01:09:52,022 --> 01:09:55,056
So the next thing we'd like to do is see those. Okay and
1356
01:09:55,058 --> 01:09:58,226
what I'm gonna try to do is put them as a thumbnail image
1357
01:09:58,228 --> 01:10:02,297
inside my left accessory view of these call outs.
1358
01:10:02,299 --> 01:10:06,534
Now to do that we're going to need to override or implement
1359
01:10:06,536 --> 01:10:09,270
that method viewForAnnotation. Notice we didn't implement
1360
01:10:09,272 --> 01:10:11,940
viewForAnnotation which is like self erode index path,
1361
01:10:11,942 --> 01:10:14,642
and when you don't you get this really simple call
1362
01:10:14,644 --> 01:10:16,611
out that has no left or right accessory view,
1363
01:10:16,613 --> 01:10:18,479
just has the title or subtitle, okay.
1364
01:10:18,481 --> 01:10:21,382
But we need to implement it so we can put a left accessory
1365
01:10:21,384 --> 01:10:23,785
view which is going to be a button. I'm gonna have it be a
1366
01:10:23,787 --> 01:10:25,920
button because I'm gonna want to be able to click on it in
1367
01:10:25,922 --> 01:10:29,824
and segue to show me the image later in the demo probably
1368
01:10:29,826 --> 01:10:33,261
next lecture. All right, so how are we gonna do this?
1369
01:10:33,263 --> 01:10:36,264
Well we have to implement that method viewForAnnotations.
1370
01:10:36,266 --> 01:10:38,566
So let's see if we can find it. There it is, okay.
1371
01:10:38,568 --> 01:10:42,904
MKMap viewForAnnotation, okay? It's asking us to provide
1372
01:10:42,906 --> 01:10:47,141
an annotation view for this annotation. So first, I'm
1373
01:10:47,143 --> 01:10:49,644
going to see if I can get it, and I'm actually gonna do
1374
01:10:49,646 --> 01:10:53,414
something kind of interesting here. I'm going to type this.
1375
01:10:53,416 --> 01:10:56,084
Now, normally you would say why are you putting a type
1376
01:10:56,086 --> 01:10:58,886
here. Because you could just say view equals something and
1377
01:10:58,888 --> 01:11:01,489
it would automatically infer the type. Well the reason is
1378
01:11:01,491 --> 01:11:05,727
cuz I want it to be implicitly unwrapped. Okay if I didn't
1379
01:11:05,729 --> 01:11:08,396
put this here, okay, then it would be an optional
1380
01:11:08,398 --> 01:11:10,965
because the thing I'm going to call returns an optional, but
1381
01:11:10,967 --> 01:11:12,767
I want it implicitly unwrapped so
1382
01:11:12,769 --> 01:11:14,035
the rest of my method method looks like.
1383
01:11:14,037 --> 01:11:16,838
The code and the rest of my method. Okay so
1384
01:11:16,840 --> 01:11:20,174
I'm gonna do here. I'm gonna ask the MapView to dequeue
1385
01:11:20,176 --> 01:11:22,710
a reusable annotationView with an identifier.
1386
01:11:22,712 --> 01:11:24,779
Now to be nice I have some constants, so
1387
01:11:24,781 --> 01:11:28,182
let's put them on the bottom here. Okay so
1388
01:11:28,184 --> 01:11:32,120
here's my constants that I have and I have a constant for
1389
01:11:32,122 --> 01:11:36,357
the reuse identifier here. Call it waypoint and say
1390
01:11:36,359 --> 01:11:42,297
Constants.AnnotationViewReuse- Identifier.
1391
01:11:42,299 --> 01:11:43,498
Okay, so that's de-queueing it.
1392
01:11:43,500 --> 01:11:46,934
Now what if this is the first pin it's ever doing? It's not
1393
01:11:46,936 --> 01:11:49,871
going to be able to de-queue. There's no pins to reuse.
1394
01:11:49,873 --> 01:11:51,139
There's none that were used and
1395
01:11:51,141 --> 01:11:53,841
are now left over. So now we're gonna have to say if
1396
01:11:53,843 --> 01:11:57,078
the view Equals nil we have to create this thing because
1397
01:11:57,080 --> 01:12:00,081
there's no prototypes like table view. So I'm going
1398
01:12:00,083 --> 01:12:04,185
to create the thing by saying I want an MKPinAnnotationView.
1399
01:12:04,187 --> 01:12:06,954
And the arguments to initialize here
1400
01:12:06,956 --> 01:12:10,158
are the annotation which is this annotation right here.
1401
01:12:10,160 --> 01:12:13,394
That's the annotation we're creating a view for. And
1402
01:12:13,396 --> 01:12:15,630
we wanna use the same ReuseIdentifier right here,
1403
01:12:15,632 --> 01:12:19,367
because in future, we want this one that we're creating
1404
01:12:19,369 --> 01:12:23,004
to be in the reuse queue, okay? And
1405
01:12:23,006 --> 01:12:26,874
I'm also gonna say that we can show callouts.
1406
01:12:27,210 --> 01:12:28,843
Okay, because by default, I believe,
1407
01:12:28,845 --> 01:12:30,611
if you create an MKPinAnnotationView,
1408
01:12:30,613 --> 01:12:32,447
it won't show the callouts when you click on it.
1409
01:12:32,449 --> 01:12:33,948
So I wanna make sure it does show the callouts,
1410
01:12:33,950 --> 01:12:36,684
cuz that's where my left accessory view is gonna be.
1411
01:12:36,686 --> 01:12:40,154
Otherwise, if I was able to dequeue one here then I need
1412
01:12:40,156 --> 01:12:45,259
to change the annotation in it to be this annotation here.
1413
01:12:46,296 --> 01:12:48,062
Okay? All right,
1414
01:12:48,064 --> 01:12:51,032
now we've got this view, this is basically exactly what's
1415
01:12:51,034 --> 01:12:53,568
happening if we don't implement this method. Okay?
1416
01:12:53,570 --> 01:12:56,471
And in fact, if I run this, if I just return this view and
1417
01:12:56,473 --> 01:12:58,840
run it, you're gonna see it looks exactly the same,
1418
01:12:58,842 --> 01:13:03,911
okay the system basically will do exactly this for you. See?
1419
01:13:03,913 --> 01:13:07,515
If you don't implement it okay? So it's doing exactly
1420
01:13:07,517 --> 01:13:12,353
the same thing. So now,let's add this left annotation view
1421
01:13:12,355 --> 01:13:16,858
which wants to be a ui button. So how are we gonna do that?
1422
01:13:16,860 --> 01:13:20,795
Okay well, let's just first of all let's set the left
1423
01:13:20,797 --> 01:13:24,899
leftCalloutAccessoryView to be nil. I'm just going to put
1424
01:13:24,901 --> 01:13:29,570
this little button in there if the GPS point has a photo.
1425
01:13:29,572 --> 01:13:31,305
If it doesn't have a thumbnail image,
1426
01:13:31,307 --> 01:13:32,607
I don't want that button to
1427
01:13:32,609 --> 01:13:34,509
be there empty. So, first of all,
1428
01:13:34,511 --> 01:13:35,376
I'm going to clear it out.
1429
01:13:35,378 --> 01:13:39,514
Then I'm going to say if I can get the waypoint, which I
1430
01:13:39,516 --> 01:13:42,683
should be able to because it's just the annotation
1431
01:13:42,685 --> 01:13:47,755
As a GPX waypoint, right? That's what the waypoint is.
1432
01:13:47,757 --> 01:13:50,658
This annotation, all of our annotations are that. So
1433
01:13:50,660 --> 01:13:51,826
as long I'm able to get that waypoint,
1434
01:13:51,828 --> 01:13:55,029
which I should be able to, then if the waypoint has
1435
01:13:55,031 --> 01:14:01,636
a thumbnail URL, if that doesn't equal
1436
01:14:01,638 --> 01:14:05,440
nil Now this is something I'm gonna have to implement okay.
1437
01:14:05,442 --> 01:14:06,941
Because it doesn't have that method, so
1438
01:14:06,943 --> 01:14:08,543
I'm going to have to implement that method.
1439
01:14:08,545 --> 01:14:11,512
Then, I'm gonna set my left column so AccessoryView
1440
01:14:11,514 --> 01:14:15,783
to be a UI button. Whose frame is this constant.
1441
01:14:15,785 --> 01:14:18,453
Unfortunately I have to do a constant here for
1442
01:14:18,455 --> 01:14:21,956
this and I'll show that in a second. The constant for
1443
01:14:21,958 --> 01:14:25,593
this is 59 by 59. This is one of the few cases
1444
01:14:25,595 --> 01:14:28,329
in IOS where you're gonna use a magic number. But
1445
01:14:28,331 --> 01:14:30,498
unfortunately, they're just doesn't seem to be anyway,
1446
01:14:30,500 --> 01:14:34,135
at least that I've ever found, to ask the map views and
1447
01:14:34,137 --> 01:14:38,239
notation view how big is your call-out so that I can make
1448
01:14:38,241 --> 01:14:41,375
the thing the right size. So, I just Know that 59 by
1449
01:14:41,377 --> 01:14:43,978
59 works. I hope Apple doesn't change the size of that
1450
01:14:43,980 --> 01:14:46,380
callout in the future, cuz it might break this code.
1451
01:14:46,382 --> 01:14:47,715
That's why you never want magic numbers,
1452
01:14:47,717 --> 01:14:50,985
but unfortunately, we're kind of stuck with one here.
1453
01:14:50,987 --> 01:14:53,488
So, we have this error because thumbnailURL is not
1454
01:14:53,490 --> 01:14:56,557
implemented. So, how are we gonna implement thumbnailURL?
1455
01:14:56,559 --> 01:14:59,827
Well, I told you that every GPX waypoint can have
1456
01:14:59,829 --> 01:15:02,630
a number of links associated with one of them,
1457
01:15:02,632 --> 01:15:04,632
one of my view thumbnailURL. So
1458
01:15:04,634 --> 01:15:08,803
I'm gonna go back to my MKGPX right here and
1459
01:15:08,805 --> 01:15:11,806
I'm gonna add this var thumbnailURL, which
1460
01:15:11,808 --> 01:15:15,776
is gonna be an NSURL. And this is gonna be optional because,
1461
01:15:15,778 --> 01:15:18,546
it might be that it doesn't have one. Which is fine,
1462
01:15:18,548 --> 01:15:21,215
now I just want to show this little side button. And
1463
01:15:21,217 --> 01:15:24,352
to implement this, I'm gonna implement a little method
1464
01:15:24,354 --> 01:15:28,422
here, a private func, which goes through the links and
1465
01:15:28,424 --> 01:15:31,425
tries to find one of the type thumbnail. So it's gonna be
1466
01:15:31,427 --> 01:15:36,464
called getImageURLofType. It's gonna take a certain type,
1467
01:15:36,466 --> 01:15:39,367
which is just a string like thumbnail or something like
1468
01:15:39,369 --> 01:15:45,640
that. And it's going to return an NSURL possibly if
1469
01:15:45,642 --> 01:15:46,474
you can find such a thing, and
1470
01:15:46,476 --> 01:15:51,345
I'm just gonna go through the links in the GPXWayPoint. And
1471
01:15:51,347 --> 01:15:55,082
if the links type equals the type am asking for then am
1472
01:15:55,084 --> 01:15:58,619
just gonna return the links URL, and if I can't do any of
1473
01:15:58,621 --> 01:16:02,924
that I'll return nil. Okay? So this gets an ImageURLofType,
1474
01:16:02,926 --> 01:16:07,662
so here I'm gonna get the ImageURLofTyp.
1475
01:16:07,664 --> 01:16:10,231
So if it's able to get a thumbnail image, woo hoo,
1476
01:16:10,233 --> 01:16:13,568
we're good to go, otherwise it'll be returning nil here.
1477
01:16:13,570 --> 01:16:17,738
While I'm here I'm gonna have another one called imageURL
1478
01:16:17,740 --> 01:16:20,708
which gets the URL of the image large.
1479
01:16:20,710 --> 01:16:23,578
Okay, not a thumbnail of it, but a big, large image of it.
1480
01:16:23,580 --> 01:16:28,316
And so, that's the type large. Okay, so these are just types
1481
01:16:28,318 --> 01:16:33,321
associated with that link in the GPX file, okay? So
1482
01:16:33,323 --> 01:16:38,659
now we have these, we can go back here, and do this now.
1483
01:16:38,661 --> 01:16:40,428
Notice that we, Sorry.
1484
01:16:40,430 --> 01:16:43,364
[LAUGH] Create this leftCalloutAccessoryView here.
1485
01:16:43,366 --> 01:16:44,732
I'm actually gonna give this some space, but
1486
01:16:44,734 --> 01:16:47,401
we don't actually set the button's image. And
1487
01:16:47,403 --> 01:16:51,305
I'm not gonna set that image until the user clicks on me.
1488
01:16:51,307 --> 01:16:54,675
Okay? Soon as they click on my pin annotation view,
1489
01:16:54,677 --> 01:16:55,376
it makes our callout accessory
1490
01:16:55,378 --> 01:16:57,345
is gonna come up. Then I'm gonna go get the image.
1491
01:16:57,347 --> 01:16:58,646
Cuz getting the image is gonna be expensive.
1492
01:16:58,648 --> 01:17:01,616
Cuz I'm gonna go get it off the internet somewhere. Okay?
1493
01:17:01,618 --> 01:17:06,420
So let's do that and we do that in this method here
1494
01:17:06,422 --> 01:17:12,026
called did select annotation view.
1495
01:17:12,028 --> 01:17:14,895
Okay, so mapview delegate method here tells us when this
1496
01:17:14,897 --> 01:17:18,065
annotation view was selected. And so I'm gonna have to
1497
01:17:18,067 --> 01:17:21,469
extract some things here, like I need to get that thumbnail
1498
01:17:21,471 --> 01:17:24,972
image button from the left accessory view. That's just my
1499
01:17:24,974 --> 01:17:28,776
left call out accessory view and it better be a ui button.
1500
01:17:28,778 --> 01:17:31,112
So I'll that's what I'm doing if let here.
1501
01:17:31,114 --> 01:17:36,984
I'm also going to get the url which is the gpx waypoint
1502
01:17:36,986 --> 01:17:42,690
which is the annotation as a gpx waypoint okay?
1503
01:17:42,692 --> 01:17:47,028
It's it's thumbnail URL that method we just implemented.
1504
01:17:47,030 --> 01:17:49,764
Okay, I also need to get the image data.
1505
01:17:49,766 --> 01:17:54,835
Now I'm gonna be a really bad man here and I'm going
1506
01:17:54,837 --> 01:18:00,708
to do this on the main queue okay. Blocks main queue.
1507
01:18:00,710 --> 01:18:03,944
You would never do this in your final projects for
1508
01:18:03,946 --> 01:18:09,050
example okay. And now, I'm going to get the UI image that
1509
01:18:09,052 --> 01:18:18,592
corresponds to that data. All right?
1510
01:18:18,594 --> 01:18:22,596
So now I've run the gauntlet of all of these if lets and
1511
01:18:22,598 --> 01:18:24,899
I have the image and I have the thumbnail image button,
1512
01:18:24,901 --> 01:18:28,602
so I'm just gonna say thumbnail image button.
1513
01:18:28,604 --> 01:18:33,274
SetImage to be that image for the State. Remember
1514
01:18:33,276 --> 01:18:35,376
buttons have different states like highlight and state and
1515
01:18:35,378 --> 01:18:37,645
whatever so I don't want the normal state which is kinda
1516
01:18:37,647 --> 01:18:42,817
the default state, okay. So let's go and run that.
1517
01:18:47,690 --> 01:18:52,126
Right, here we go, a click on it and sure enough now we're
1518
01:18:52,128 --> 01:18:56,363
getting this thumbnail in here To that, okay? So
1519
01:18:56,365 --> 01:18:57,131
that's all we have time for today.
1520
01:18:57,133 --> 01:18:59,667
What we're gonna do next time is I'm gonna click on this
1521
01:18:59,669 --> 01:19:03,671
button, and we're gonna Segway to the image view controller
1522
01:19:03,673 --> 01:19:05,806
we have in Kissimmee. It's gonna Segway and
1523
01:19:05,808 --> 01:19:09,777
show us the full size image, the large image of that thing.
1524
01:19:09,779 --> 01:19:12,646
So once you have segue. Then, we'll go on and do some more
1525
01:19:12,648 --> 01:19:17,251
stuff to show off some other little features. All right?
1526
01:19:17,253 --> 01:19:20,087
So, see you next time. >> For
1527
01:19:20,089 --> 01:19:20,120
more, please visit us at stanford.edu.
================================================
FILE: subtitles/18. Persistence.srt
================================================
1
00:00:00,001 --> 00:00:03,469
[MUSIC]
2
00:00:03,471 --> 00:00:07,806
Stanford University. >> Okay, well,
3
00:00:07,808 --> 00:00:12,711
welcome to Stanford CSI93P Spring of 2016.
4
00:00:12,713 --> 00:00:14,947
This is our last normal lecture.
5
00:00:14,949 --> 00:00:18,217
The only lecture we have left is our alternate final, next
6
00:00:18,219 --> 00:00:23,088
week. Today we are going to have a demo of all the stuff I
7
00:00:23,090 --> 00:00:27,760
talked about on Monday. So, mostly that's segues.
8
00:00:27,762 --> 00:00:33,298
Including ma, modal segue, popover segue, unwind segue.
9
00:00:33,300 --> 00:00:35,868
And even we'll talk about the adaptive presentation,
10
00:00:35,870 --> 00:00:39,671
how we adapt to horizontally compact, all that stuff. And
11
00:00:39,673 --> 00:00:40,672
I'll throw a little bit of bonus in there.
12
00:00:40,674 --> 00:00:43,442
We'll talk a little about visual effects, like Blur.
13
00:00:43,444 --> 00:00:47,513
You'll see a lot of Blur in the UI occasionally and
14
00:00:47,515 --> 00:00:50,682
I'll show you how to basically do that. And
15
00:00:50,684 --> 00:00:53,952
I think that's gonna take most of the time but
16
00:00:53,954 --> 00:00:55,854
if I have any time left like maybe five or
17
00:00:55,856 --> 00:00:58,590
ten minutes left I'll try to do a quick tour through
18
00:00:58,592 --> 00:01:01,393
persistence because creating files in the file system,
19
00:01:01,395 --> 00:01:03,962
stuff like that is something a lot of people are gonna wanna
20
00:01:03,964 --> 00:01:06,031
do in their final project. So I'll try and cover that. No
21
00:01:06,033 --> 00:01:08,901
demo for that unfortunately, but at least maybe I can get
22
00:01:08,903 --> 00:01:14,006
through the slides if the demo goes on a good pace, okay?
23
00:01:14,008 --> 00:01:17,443
So here's our demo those all the things we're gonna cover.
24
00:01:17,445 --> 00:01:20,345
So it's quite a quick demo, it, it, or
25
00:01:20,347 --> 00:01:24,850
quite a involved demo. It's gonna be an extension of Trax,
26
00:01:24,852 --> 00:01:27,920
okay, the thing that we left off with last time.
27
00:01:27,922 --> 00:01:29,655
I'll run that really quick just to remind yourself,
28
00:01:29,657 --> 00:01:34,693
in the last two days, what it did. So, remember that it went
29
00:01:34,695 --> 00:01:37,162
out on the Internet, it grabbed a GPX file, which is
30
00:01:37,164 --> 00:01:40,099
just basically a file that contains a bunch of waypoints
31
00:01:40,101 --> 00:01:43,035
and possibly hyperlinks attached to the waypoints.
32
00:01:43,037 --> 00:01:44,670
And it puts them on the map here so
33
00:01:44,672 --> 00:01:48,207
we can scroll around and zoom in and out on them. And
34
00:01:48,209 --> 00:01:49,675
we made it so that if we clicked on them,
35
00:01:49,677 --> 00:01:52,678
the callout had a left accessory callout
36
00:01:52,680 --> 00:01:55,114
view right here, this left accessory callout view.
37
00:01:55,116 --> 00:01:59,785
And put a thumbnail that it found in the GPX file for
38
00:01:59,787 --> 00:02:03,455
each waypoint, okay? So now, what we'll do today,
39
00:02:03,457 --> 00:02:06,024
the next thing we're gonna do is learn how to segue from
40
00:02:06,026 --> 00:02:08,660
a map, because it's a little unusual trying to segue from
41
00:02:08,662 --> 00:02:12,131
a map because when you look at it in the storyboard it's
42
00:02:12,133 --> 00:02:13,899
just blank, this big blank map view,
43
00:02:13,901 --> 00:02:15,868
so that you can't, it's not like a table view where you
44
00:02:15,870 --> 00:02:18,670
got a row you can Ctrl+drag from or something. So
45
00:02:18,672 --> 00:02:21,540
we're gonna have to do this segueing from a map in code.
46
00:02:21,542 --> 00:02:23,809
And what we're gonna do is make it so that when we
47
00:02:23,811 --> 00:02:27,079
click on this little thumbnail button right here, it segues,
48
00:02:27,081 --> 00:02:31,083
okay. And puts an image view controller that will show you
49
00:02:31,085 --> 00:02:36,088
the full large image of this. And we're gonna get the UI for
50
00:02:36,090 --> 00:02:36,455
doing that imagery control,
51
00:02:36,457 --> 00:02:38,357
we're just gonna steal that right out of Cassini. I mean,
52
00:02:38,359 --> 00:02:41,026
you're gonna see the advantage of building these MVCs that
53
00:02:41,028 --> 00:02:44,530
have very clear public models. It makes them quite reuseable.
54
00:02:44,532 --> 00:02:47,833
So we can reuse that MVC we made in Cassini with
55
00:02:47,835 --> 00:02:50,235
absolutely no changes. Gonna just drag it in and
56
00:02:50,237 --> 00:02:53,205
set up its public API and it'll work perfectly. So
57
00:02:53,207 --> 00:02:56,441
let's go do that right now, let's go over to Cassini.
58
00:02:56,443 --> 00:03:02,147
And where is Cassini? It's here in Developer > Cassini.
59
00:03:02,149 --> 00:03:04,683
We open Cassi, Cassini here. So remember, you'll
60
00:03:04,685 --> 00:03:07,853
remember Cassini here. And we, it's the one that showed the,
61
00:03:07,855 --> 00:03:10,155
the image of Cassini, what it was doing. So
62
00:03:10,157 --> 00:03:12,457
I'm just gonna grab this ImageViewController,
63
00:03:12,459 --> 00:03:15,127
right here, this class ImageViewController. And
64
00:03:15,129 --> 00:03:18,664
I'm gonna pick it up and drag it into my app over here and
65
00:03:18,666 --> 00:03:22,034
I'm gonna copy it in, okay? But I can do actually more
66
00:03:22,036 --> 00:03:24,169
than that, more than just grabbing that MVC,
67
00:03:24,171 --> 00:03:26,738
I can actually go to the storyboard and find the place
68
00:03:26,740 --> 00:03:29,441
in the storyboard, down here actually, where Casini is
69
00:03:29,443 --> 00:03:33,512
using the ImageViewController. And I can copy that scene from
70
00:03:33,514 --> 00:03:39,184
Casini and then bring it on over to our storyboard here.
71
00:03:39,186 --> 00:03:43,589
We'll zoom out so you can see this. Okay, and we can paste.
72
00:03:43,591 --> 00:03:46,024
And that actually dropped an ImageViewController there,
73
00:03:46,026 --> 00:03:49,328
with the scroll view already in it and wired up. And
74
00:03:49,330 --> 00:03:52,364
it has the proper identity, right, ImageViewController, so
75
00:03:52,366 --> 00:03:55,367
it's all really nice. So don't forget that, in storyboards
76
00:03:55,369 --> 00:03:58,170
you can copy and paste scenes between storyboards and
77
00:03:58,172 --> 00:03:58,604
it'll bring it all.
78
00:03:58,606 --> 00:04:00,439
And it brings it all by name, remember, so
79
00:04:00,441 --> 00:04:02,441
you have to have the classes with the same name or
80
00:04:02,443 --> 00:04:07,012
outlets with the same name for it all to work. Okay, so let's
81
00:04:07,014 --> 00:04:10,649
go ahead and build the rest of our UI that we have here.
82
00:04:10,651 --> 00:04:14,186
We want to segue from this map view over to this scroll
83
00:04:14,188 --> 00:04:17,322
view, but we don't really anything to Ctrl+drag from
84
00:04:17,324 --> 00:04:20,092
here cuz of the callouts and things like that are all come
85
00:04:20,094 --> 00:04:22,995
up from programmatically. So we're gonna do a manual
86
00:04:22,997 --> 00:04:25,631
segue, which we've already seen and the way we do that is
87
00:04:25,633 --> 00:04:28,433
we Ctrl+drag from the View Controller itself just to
88
00:04:28,435 --> 00:04:31,370
create a segue with a certain identifier, okay, between
89
00:04:31,372 --> 00:04:36,074
the two MVCs. So let's just get these both on screen here.
90
00:04:36,076 --> 00:04:39,578
And I'm gonna Ctrl+drag from here over to here.
91
00:04:39,580 --> 00:04:41,780
We're gonna put this inside the navigation controller.
92
00:04:41,782 --> 00:04:45,083
So this is gonna be a show segue right here.
93
00:04:45,085 --> 00:04:48,620
And we'll give it a name. This is gonna show an image, so
94
00:04:48,622 --> 00:04:51,957
we'll call this Show Image segue. Let's be sure to go
95
00:04:51,959 --> 00:04:57,462
ahead and embed this in a Navigation Controller. Okay,
96
00:04:57,464 --> 00:05:00,899
oops, so let's zoom out and take a look at our UI. So
97
00:05:00,901 --> 00:05:03,735
here's our UI. We could even do some other things like here
98
00:05:03,737 --> 00:05:06,505
I'd maybe this, we wanna give it a title, we'll call it
99
00:05:06,507 --> 00:05:09,541
Trax, or something like that. And this title we'll set
100
00:05:09,543 --> 00:05:11,943
programmatically depending on which waypoint we're
101
00:05:11,945 --> 00:05:15,447
looking at, we'll put whatever the name of that waypoint is
102
00:05:15,449 --> 00:05:15,714
so we'll do that in the code. All right, so how are we gonna
103
00:05:15,716 --> 00:05:20,052
in at the top,
104
00:05:20,054 --> 00:05:23,455
fire off this segue right here from the code?
105
00:05:23,457 --> 00:05:24,823
Where's a good place to do it in the code? Well,
106
00:05:24,825 --> 00:05:29,728
we know we wanna do it when we click on that left accessory
107
00:05:29,730 --> 00:05:32,764
view button right there. And if you'll remember from
108
00:05:32,766 --> 00:05:36,702
lecture, there's a really cool map view, MKMapViewDelegate
109
00:05:36,704 --> 00:05:39,571
method, I'm just back here in GPXViewController
110
00:05:39,573 --> 00:05:42,174
here. So this is a really cool MapViewDelegate method
111
00:05:42,176 --> 00:05:45,844
that will get called when an accessory view is tapped on
112
00:05:45,846 --> 00:05:48,513
and that accessory view is a UI control like
113
00:05:48,515 --> 00:05:51,249
a UI button, for example. All right, so let's put that in
114
00:05:51,251 --> 00:05:54,986
here. That thing is called a callout accessory, there it
115
00:05:54,988 --> 00:06:00,258
is, MapView, annotationView, calloutAccessory view Tapped,
116
00:06:00,260 --> 00:06:02,961
okay? Let's give ourselves a lot more room here.
117
00:06:02,963 --> 00:06:06,331
This is this method. And all we need to do here is look at
118
00:06:06,333 --> 00:06:09,368
the control that was tapped, and see if it's our left
119
00:06:09,370 --> 00:06:13,105
AccessoryView, okay? So if the control == our views,
120
00:06:13,107 --> 00:06:17,609
leftCalloutAccessoryView, okay, then we know someone
121
00:06:17,611 --> 00:06:21,580
tapped on that left side. And we wanna do this segue, and
122
00:06:21,582 --> 00:06:24,383
we know how to perform a segue from clo, from code.
123
00:06:24,385 --> 00:06:27,686
We just do performSegueWithIdentifier.
124
00:06:27,688 --> 00:06:30,288
Okay, the identifier is that "Show Image", I actually
125
00:06:30,290 --> 00:06:32,691
created a constant for that right here, okay just so
126
00:06:32,693 --> 00:06:37,863
my code would be nice. So constants.ShowImageSegue.
127
00:06:37,865 --> 00:06:40,766
And what about the sender? Well, when this segue happens,
128
00:06:40,768 --> 00:06:44,236
we're gonna need to know which of our many waypoints we wanna
129
00:06:44,238 --> 00:06:47,973
show the image of, okay. So we need to pass a sender on
130
00:06:47,975 --> 00:06:50,809
to prepareForSegue that can identify it.
131
00:06:50,811 --> 00:06:53,945
And a simple thing to do here, okay, this argument
132
00:06:53,947 --> 00:06:57,282
that we get right here is the annotationView. Okay,
133
00:06:57,284 --> 00:07:01,286
that was, that this callout has a left accessory view
134
00:07:01,288 --> 00:07:02,554
for, so we'll just pass that along.
135
00:07:02,556 --> 00:07:05,090
So we'll just pass that as the sender, which makes sense,
136
00:07:05,092 --> 00:07:05,991
the annotationView is the sender.
137
00:07:05,993 --> 00:07:08,727
And from the annotationView, we're gonna be able to get
138
00:07:08,729 --> 00:07:11,463
the waypoint that we want. Okay, so that couldn't
139
00:07:11,465 --> 00:07:15,300
be simpler right there. Now what about the navigation? So
140
00:07:15,302 --> 00:07:18,103
let's put in some navigation here, the prepareForSegue.
141
00:07:18,105 --> 00:07:20,372
Because any segue needs to be prepared. In this case,
142
00:07:20,374 --> 00:07:23,608
we're going to have to prepare that ImageViewController with
143
00:07:23,610 --> 00:07:25,610
its public API, which, if you'll remember,
144
00:07:25,612 --> 00:07:29,247
is an image URL. We have to give it the URL of an image,
145
00:07:29,249 --> 00:07:32,150
okay? So prepareForSegue, let's do that,
146
00:07:32,152 --> 00:07:36,288
prepareForSegue. And what do we need for prepareForSegue?
147
00:07:36,290 --> 00:07:38,623
Well, first of all, let's collect some information here,
148
00:07:38,625 --> 00:07:40,525
like, let's get the destinationViewController.
149
00:07:40,527 --> 00:07:43,762
That's the segue's destinationViewController.
150
00:07:43,764 --> 00:07:47,165
And actually, I'm gonna do that contentViewController
151
00:07:47,167 --> 00:07:50,802
trick that we did where we have a little extension,
152
00:07:50,804 --> 00:07:52,437
contentViewController here,
153
00:07:52,439 --> 00:07:54,739
a little extension to UIViewController.
154
00:07:54,741 --> 00:07:57,108
So that if the thing is in a Navigation Controller,
155
00:07:57,110 --> 00:07:58,443
we return the visibleViewController,
156
00:07:58,445 --> 00:08:01,112
otherwise we just return self. You remember this from From
157
00:08:01,114 --> 00:08:03,648
before, this is so that if we're in a split view and
158
00:08:03,650 --> 00:08:05,717
we happen to put the detail, for example,
159
00:08:05,719 --> 00:08:06,985
inside a navigation controller,
160
00:08:06,987 --> 00:08:09,955
we can get the thing that's inside. All right, so
161
00:08:09,957 --> 00:08:13,625
we'll do the content view controller. Here, so
162
00:08:13,627 --> 00:08:17,195
that's the destination of, this, segue.
163
00:08:17,197 --> 00:08:19,931
Let's get the annotation view, which is the sender,
164
00:08:19,933 --> 00:08:23,301
okay this sender any object, should be an annotation view,
165
00:08:23,303 --> 00:08:24,469
so let's go ahead and get that.
166
00:08:24,471 --> 00:08:29,541
By saying the sender as an MKAnnotationView. Okay.
167
00:08:29,543 --> 00:08:33,078
Let's also get the waypoint that we're talking about here.
168
00:08:33,080 --> 00:08:35,981
Okay, all these segues are gonna be segueing from
169
00:08:35,983 --> 00:08:38,517
something that has a waypoint. So what is that?
170
00:08:38,519 --> 00:08:43,355
That's gonna be the annotationViews annotation.
171
00:08:43,557 --> 00:08:45,924
And it has to be a gpx waypoint, which they all
172
00:08:45,926 --> 00:08:50,028
should be. All the annotations that we add to our map view
173
00:08:50,030 --> 00:08:53,665
implement the MK protocol, so they're all gpx waypoint.
174
00:08:53,667 --> 00:08:55,667
So this should always come out true.
175
00:08:55,669 --> 00:08:58,537
So we kinda got these little things that we need.
176
00:08:58,539 --> 00:09:03,241
So now lets just say if the segway identifier equals
177
00:09:03,243 --> 00:09:08,079
the Constants.ShowSegue, ShowImageSegue, okay?
178
00:09:08,081 --> 00:09:11,850
So we know we're going the show image. Then if we can get
179
00:09:11,852 --> 00:09:14,886
an ImageViewController as the destination, so
180
00:09:14,888 --> 00:09:18,290
if the destination is an ImageViewController,
181
00:09:18,525 --> 00:09:23,028
then we can just load it up by saying it's imageURL equals
182
00:09:23,030 --> 00:09:24,829
the waypoints.
183
00:09:25,766 --> 00:09:29,301
Image URL, right remember image URL,
184
00:09:29,303 --> 00:09:32,671
we added that when we put this little extension on here that
185
00:09:32,673 --> 00:09:36,107
did the thumbnail URL we also added the image URL. Its just
186
00:09:36,109 --> 00:09:40,045
gonna look in the gpx file information to find an image
187
00:09:40,047 --> 00:09:44,282
url that's got the type large verse the type thumbnail.
188
00:09:44,284 --> 00:09:46,885
Okay? Cuz we obviously wanna show the large image,
189
00:09:46,887 --> 00:09:50,522
when we segue to it. All right? All right, so
190
00:09:50,524 --> 00:09:52,290
that's good. While we're here, let's go ahead and
191
00:09:52,292 --> 00:09:57,662
set the title of this, thing to be the waypoint's name. So
192
00:09:57,664 --> 00:10:00,031
that'll give us a nice title at the top,
193
00:10:00,033 --> 00:10:06,571
of our MVC. Okay? Makes sense? All right so
194
00:10:06,573 --> 00:10:11,476
let's go to run see what this does. See if it works for us?
195
00:10:17,017 --> 00:10:20,218
All right, so here is all of our little waypoints here. So
196
00:10:20,220 --> 00:10:24,422
let's click on waypoint, like this one, still working okay,
197
00:10:24,424 --> 00:10:26,524
we got the picture in here but now hopefully if we click on
198
00:10:26,526 --> 00:10:30,729
this, we'll seque. Sure enough here we go. My image view
199
00:10:30,731 --> 00:10:32,130
controller is not quite as nice as the one you did in
200
00:10:32,132 --> 00:10:35,233
your homework in terms of the white space management but
201
00:10:35,235 --> 00:10:37,636
it works. Notice it's got the title up here as well.
202
00:10:37,638 --> 00:10:39,471
The title of this thing, Panorama,
203
00:10:39,473 --> 00:10:45,377
got passed along there. Okay, so that was pretty easy.
204
00:10:45,746 --> 00:10:47,679
The next thing we're gonna do is make it so
205
00:10:47,681 --> 00:10:51,816
that in our map here, we can add a waypoint.
206
00:10:51,818 --> 00:10:54,719
Now, this is a demo, I'm not gonna go so far as to actually
207
00:10:54,721 --> 00:10:58,356
rewrite the gpx file to add this waypoint to it, but I'm
208
00:10:58,358 --> 00:11:02,260
gonna show you how to at least add the waypoint to the map.
209
00:11:02,262 --> 00:11:05,563
Now, the UI I'm gonna use here is long press.
210
00:11:05,565 --> 00:11:08,166
I don't know if you've seen a long press gesture, but
211
00:11:08,168 --> 00:11:10,368
basically if you just press with your finger and
212
00:11:10,370 --> 00:11:11,169
you hold it down long enough,
213
00:11:11,171 --> 00:11:14,839
it'll start looking like a long press, to iOS.
214
00:11:14,841 --> 00:11:17,442
And when you long press, I'm gonna drop another pin there.
215
00:11:17,444 --> 00:11:21,780
A new pin. It's gonna be a new we, new waypoint. All right so
216
00:11:21,782 --> 00:11:23,348
let's put that in our UI first,
217
00:11:23,350 --> 00:11:26,851
let's go, back here to our story board and
218
00:11:26,853 --> 00:11:30,221
we're just gonna add a long press gesture to this map, so
219
00:11:30,223 --> 00:11:34,092
i'm gonna go over here down to this, I'll even search for it
220
00:11:34,094 --> 00:11:36,594
and make it even easier, long press. Here it is right here,
221
00:11:36,596 --> 00:11:39,497
so I'm gonna add this long press the map view is the one
222
00:11:39,499 --> 00:11:44,102
that is gonna recognize it right there, to it.
223
00:11:44,104 --> 00:11:48,373
And now we're go ahead and make this long press send
224
00:11:48,375 --> 00:11:51,009
action to our con, view controller. So let's get them
225
00:11:51,011 --> 00:11:55,780
on the screen at the same time here. All right go automatic.
226
00:11:56,049 --> 00:12:01,119
Okay we'll put it let's put it right down here. Okay? So
227
00:12:01,121 --> 00:12:03,755
I'm just gonna Ctrl+drag from the long press gesture
228
00:12:03,757 --> 00:12:07,792
into here. Its going to be an action, we will call this Add
229
00:12:07,794 --> 00:12:10,829
wayPoint because that is what this gesture is going to do,
230
00:12:10,831 --> 00:12:15,166
its going to add a waypoint. OK, lets go ahead and
231
00:12:15,168 --> 00:12:19,871
go back here. All right, so we have got Add waypoint right
232
00:12:19,873 --> 00:12:22,674
here. What do we need to do when we add a waypoint?
233
00:12:22,676 --> 00:12:27,712
Well, first we're going to do it as soon as we
234
00:12:27,714 --> 00:12:32,484
recognize the long press. So soon as it
235
00:12:32,486 --> 00:12:35,019
starts recognizing this long press, we're going to do it.
236
00:12:35,021 --> 00:12:37,622
Now a long press is actually a continuous gesture.
237
00:12:37,624 --> 00:12:39,157
If you hold the long town press down,
238
00:12:39,159 --> 00:12:41,693
it'll keep firing. Okay fire over and over and
239
00:12:41,695 --> 00:12:43,027
over. We don't want that we don't want to add
240
00:12:43,029 --> 00:12:44,662
a whole bunch of waypoints we just want the first one.
241
00:12:44,664 --> 00:12:46,898
So as soon as we recognize that it's a long press
242
00:12:46,900 --> 00:12:48,900
we're gonna drop the waypoint and we don't care what happens
243
00:12:48,902 --> 00:12:52,637
with the press after that we're ignoring. Okay?
244
00:12:52,639 --> 00:12:54,472
So we've got to get the coordinate
245
00:12:54,474 --> 00:12:57,408
okay that CL coordinate 2d right the latitude and
246
00:12:57,410 --> 00:13:01,246
longitude of this thing that's dropped. And to do that,
247
00:13:01,248 --> 00:13:03,648
we're gonna have to take the coordinate from the gesture,
248
00:13:03,650 --> 00:13:06,751
which is a view coordinate, and turn it into longitude and
249
00:13:06,753 --> 00:13:09,621
latitude. Well luckily Mapview knows how to do that,
250
00:13:09,623 --> 00:13:13,024
Mapview has a method called convertPoint,
251
00:13:13,026 --> 00:13:14,793
two coordinate from view, you see it right there,
252
00:13:14,795 --> 00:13:17,762
the first one, Convert point to coordinate from view. So
253
00:13:17,764 --> 00:13:22,767
the point it wants to convert is the gesture recognizers
254
00:13:22,769 --> 00:13:27,172
location in view, okay. And what view?
255
00:13:27,174 --> 00:13:28,373
The map view itself. So
256
00:13:28,375 --> 00:13:30,708
we're gonna get the location to gesture in the map view and
257
00:13:30,710 --> 00:13:34,712
we're going to turn it into a coordinate and the coordinates
258
00:13:34,714 --> 00:13:40,084
we're, converting from is the map view also. Okay.
259
00:13:40,086 --> 00:13:43,454
So now we've got the latitude and longitude that the long
260
00:13:43,456 --> 00:13:46,457
press happened at. So now let's create a wave point, and
261
00:13:46,459 --> 00:13:50,628
we're going to do that by just saying GPX.wavepoint, latitude
262
00:13:50,630 --> 00:13:53,832
and longitude. Right? So the latitude is the coordinates
263
00:13:53,834 --> 00:13:59,304
latitude, and longitude is the coordinates longitude. Right?
264
00:13:59,306 --> 00:13:59,704
This coordinate right here,
265
00:13:59,706 --> 00:14:02,807
that's this coordinate we just created. All right. So we got
266
00:14:02,809 --> 00:14:06,211
a waypoint and we can give the waypoint a name, let's give it
267
00:14:06,213 --> 00:14:08,847
a default name. We'll call it Dropped since we just dropped
268
00:14:08,849 --> 00:14:12,417
this waypoint onto of the map. Now let's go and add that as
269
00:14:12,419 --> 00:14:17,355
an annotation, to the map, and we know that this is legal for
270
00:14:17,357 --> 00:14:21,025
us to do because addAnnotation takes an MKAnnotation and
271
00:14:21,027 --> 00:14:22,160
we know that GPS. Way,
272
00:14:22,162 --> 00:14:25,230
GPX.Waypoint implements the MKAnnotation protocol, so
273
00:14:25,232 --> 00:14:29,868
this is perfectly legal for us to do, right? All right, so
274
00:14:29,870 --> 00:14:37,542
let's go take a look see if this works. All right,
275
00:14:37,544 --> 00:14:40,645
so here we are. I'm just going to let's say right up here is
276
00:14:40,647 --> 00:14:43,514
a little airport here, so let's hold it down and
277
00:14:43,516 --> 00:14:45,216
drop and sure enough, it dropped a pin there, okay.
278
00:14:45,218 --> 00:14:48,219
We drop another one over here, another one over here. Now,
279
00:14:48,221 --> 00:14:51,923
if I click on this, I'm just gonna get dropped. Okay?
280
00:14:51,925 --> 00:14:54,425
Also, what if I put this in the wrong spot and
281
00:14:54,427 --> 00:14:57,061
I wanted to pick it up? 'Kay, there's no way to pick it up
282
00:14:57,063 --> 00:15:02,100
and move it and that's because these pins are not "dragable."
283
00:15:02,102 --> 00:15:04,669
And if you remember from the, la a, slides,
284
00:15:04,671 --> 00:15:07,438
to make a pin dragable you have to take that coordinate,
285
00:15:07,440 --> 00:15:10,608
ma, var, you know we have this coordinate var that we added
286
00:15:10,610 --> 00:15:13,912
when we were implementing MKannotation we added this.
287
00:15:13,914 --> 00:15:16,948
This has to be read and write. To make these things
288
00:15:16,950 --> 00:15:19,183
obviously dragable if they're going to be dragable when they
289
00:15:19,185 --> 00:15:21,653
drop into a new place we need to set the new coordinates.
290
00:15:21,655 --> 00:15:24,722
So we need to have a editable or
291
00:15:24,724 --> 00:15:27,225
one of these that has a get and a set. So
292
00:15:27,227 --> 00:15:30,995
I'm actually going to do that by creating a subclass of
293
00:15:30,997 --> 00:15:36,601
GPX Waypoint called editable ed it a ble waypoint,
294
00:15:36,603 --> 00:15:40,905
okay. It's just gonna sub class GPX.waypoint and
295
00:15:40,907 --> 00:15:43,508
in that sub class I'm gonna make coordinate.
296
00:15:43,510 --> 00:15:48,680
B read right VAR. So coordinate, CL location,
297
00:15:48,682 --> 00:15:50,882
coordinate 2D is gonna have a get and
298
00:15:50,884 --> 00:15:54,152
it's also gonna have the set. Now for the get, I'm just
299
00:15:54,154 --> 00:15:58,022
gonna do super. coordinate. Okay? We should note
300
00:15:58,024 --> 00:16:02,527
that we are overriding this bar from gpx waypoint, okay.
301
00:16:02,529 --> 00:16:05,830
And for the set, we've got this new value which is
302
00:16:05,832 --> 00:16:09,400
a coordinate and I need to put that into the GPXs data
303
00:16:09,402 --> 00:16:13,137
structure which is latitude and longitude right?
304
00:16:13,139 --> 00:16:15,807
So, I'm just gonna say here that,
305
00:16:15,809 --> 00:16:19,978
our latitude equals the new value's latitude and
306
00:16:19,980 --> 00:16:24,082
our longitude equals the new value's longitude.
307
00:16:24,084 --> 00:16:27,585
Okay, so we're setting the latitude and the longitude in
308
00:16:27,587 --> 00:16:31,622
the GPX waypoint from this new coordinate value that we got.
309
00:16:31,624 --> 00:16:35,526
Okay. So, we'll do that. Everybody get that.
310
00:16:35,528 --> 00:16:36,627
Everyone understand what I what I did there?
311
00:16:36,629 --> 00:16:38,930
So, I have this new class now, EditableWaypoint.
312
00:16:38,932 --> 00:16:41,165
Yeah, I probably could have done that here,
313
00:16:41,167 --> 00:16:44,002
put in the extension but it's actually nice as you're
314
00:16:44,004 --> 00:16:46,771
gonna see to have a whole class that we know that this
315
00:16:46,773 --> 00:16:49,140
is an editable waypoint by looking at its class,
316
00:16:49,142 --> 00:16:53,745
actually. All right? Okay, so let's go back now to here, and
317
00:16:53,747 --> 00:16:57,148
instead of creating a GPX waypoint here now when we add
318
00:16:57,150 --> 00:17:02,086
waypoint, I'm gonna create an editable waypoint. Okay.
319
00:17:02,088 --> 00:17:04,389
So that, so that it can be moved around.
320
00:17:04,391 --> 00:17:07,892
Now, the other thing I need to do to make it an draggable
321
00:17:07,894 --> 00:17:11,629
is to go here to where I create my annotation view for
322
00:17:11,631 --> 00:17:13,131
annotation which remember,
323
00:17:13,133 --> 00:17:15,900
is kind of self row index path for map view.
324
00:17:15,902 --> 00:17:17,602
Here's where I create the left call it accessory.
325
00:17:17,604 --> 00:17:21,806
I also need to say whether it's draggable. Okay?
326
00:17:21,808 --> 00:17:25,643
And that's just going to be, whether or not the annotation
327
00:17:25,645 --> 00:17:29,647
that we passed on here is an editable waypoint. Okay?
328
00:17:29,649 --> 00:17:31,783
If annotation is of class editable waypoint,
329
00:17:31,785 --> 00:17:34,352
then we know it's editable, and so it's dragable.
330
00:17:34,354 --> 00:17:37,188
See how giving that in a separate class was kind of
331
00:17:37,190 --> 00:17:39,724
cool? Makes our code read kind of nice there.
332
00:17:39,726 --> 00:17:42,527
All right, so we should be able to drag this around, so
333
00:17:42,529 --> 00:17:49,801
let's see if it works. All right,
334
00:17:49,803 --> 00:17:51,836
so you going to drop one right here, here it is,
335
00:17:51,838 --> 00:17:54,338
we can see this is dropped. Can we pick it up and drag it?
336
00:17:54,340 --> 00:17:59,377
Let's see. Yes, we can put it somewhere else pick it
337
00:17:59,379 --> 00:18:02,947
up again put it over here. Okay so
338
00:18:02,949 --> 00:18:06,050
that's kind of cool. So, now what are we gonna do? Well,
339
00:18:06,052 --> 00:18:09,353
it's kind of annoying that this just says dropped, okay.
340
00:18:09,355 --> 00:18:10,788
We don't want it to say dropped,
341
00:18:10,790 --> 00:18:11,923
we wanna actually edit it and so,
342
00:18:11,925 --> 00:18:13,891
you know if I put it over here on top of this little
343
00:18:13,893 --> 00:18:17,328
airport right here, I probably wanna put airport in there.
344
00:18:17,330 --> 00:18:20,431
Okay? And so how are we gonna do that? Well,
345
00:18:20,433 --> 00:18:23,334
this is a good opportunity to show you a modal segue.
346
00:18:23,336 --> 00:18:27,138
Okay, cuz what we're gonna do is we're gonna create a little
347
00:18:27,140 --> 00:18:29,807
MVC to edit the name and the description there of
348
00:18:29,809 --> 00:18:33,244
the waypoint and we're going to modally present that MVC,
349
00:18:33,246 --> 00:18:37,448
okay? So, how we're gonna do that? Pretty straightforward.
350
00:18:37,450 --> 00:18:40,985
Let's start here by figuring out how we're gonna get
351
00:18:40,987 --> 00:18:42,120
that modal MVC to appear and
352
00:18:42,122 --> 00:18:44,956
what I'm gonna do is you see how I'm using over here
353
00:18:44,958 --> 00:18:48,326
at the left accessory view. Now, I'm gonna use the right
354
00:18:48,328 --> 00:18:50,928
accessory view on these and I'm gonna put a detail
355
00:18:50,930 --> 00:18:54,132
disclosure button in there. That's the little blue kinda
356
00:18:54,134 --> 00:18:57,101
looks like a blue round circle with a carrot in it. So,
357
00:18:57,103 --> 00:18:59,804
I'm gonna put that kind of button in here and only on
358
00:18:59,806 --> 00:19:02,173
that editable way points will I put it there. And
359
00:19:02,175 --> 00:19:05,309
when you click on that, it's going to bring up this modal
360
00:19:05,311 --> 00:19:09,714
panel that lets you edit the name and info. Okay. So, let's
361
00:19:09,716 --> 00:19:15,219
go ahead and do that, let's change our UI to make our UI,
362
00:19:15,221 --> 00:19:17,822
that's how we gonna do that.
363
00:19:17,824 --> 00:19:20,358
We're gonna have to add a RightCalloutAccessoryView.
364
00:19:20,360 --> 00:19:22,760
Just like we added a leftCalloutAccessoryView here,
365
00:19:22,762 --> 00:19:25,329
so I'm gonna do the same thing where I clear it out first
366
00:19:25,331 --> 00:19:27,698
because if it's not at an editable waypoint we don't
367
00:19:27,700 --> 00:19:30,301
want it in there. And remember we're reusing these things,
368
00:19:30,303 --> 00:19:33,538
right? DQ reusable. So, we have to clear it out from
369
00:19:33,540 --> 00:19:37,141
previous uses. And so, here I'm just gonna say,
370
00:19:37,143 --> 00:19:41,479
if the waypoint is an EditableWaypoint,then I need
371
00:19:41,481 --> 00:19:46,017
to add this rightCallout AccessoryView which is just
372
00:19:46,019 --> 00:19:51,022
going to be a UIButton(type: .DetailDisclosure).
373
00:19:51,024 --> 00:19:51,923
Okay, so I'm just going to put it on.
374
00:19:51,925 --> 00:19:54,792
This is only going to appear on editable ones which is
375
00:19:54,794 --> 00:19:58,196
exactly what we want. Now, how do we make it so that it's
376
00:19:58,198 --> 00:20:01,866
going to Do this modal segue whenever we're gonna do it.
377
00:20:01,868 --> 00:20:03,801
We're gonna do the exact same way that we did for
378
00:20:03,803 --> 00:20:06,637
the left call-out accessory, which is what we're gonna do
379
00:20:06,639 --> 00:20:08,639
it in this call-out accessory control tapped.
380
00:20:08,641 --> 00:20:11,375
Okay, cuz now this the right side one. So, I'm gonna say
381
00:20:11,377 --> 00:20:15,813
else if the control equals the right call-out accessory view.
382
00:20:15,815 --> 00:20:21,552
Okay? Then, I need to perform a segue, the different one.
383
00:20:22,121 --> 00:20:25,756
This one's gonna be called, let's see what I call down
384
00:20:25,758 --> 00:20:28,759
here in the constance. I call it edit way points,
385
00:20:28,761 --> 00:20:32,830
the Edit user way points segway, right? Edit user way
386
00:20:32,832 --> 00:20:36,000
points and the sender still gonna be the view because we
387
00:20:36,002 --> 00:20:39,003
still wanna know which way point we're editing, okay? And
388
00:20:39,005 --> 00:20:42,540
of course, we have to fix or prepare for segway to do that
389
00:20:42,542 --> 00:20:47,311
as well. Let's take a time out from doing our prepare for
390
00:20:47,313 --> 00:20:51,315
segue and go into our storyboard and add that new
391
00:20:51,317 --> 00:20:54,552
MVC. All right, so here we are, I'm going to make this
392
00:20:54,554 --> 00:20:56,053
a little zoomed out a little bit. So,
393
00:20:56,055 --> 00:20:58,823
I'm going to put this new view controller just by dragging
394
00:20:58,825 --> 00:21:01,158
out an empty view controller on to the top up here.
395
00:21:01,160 --> 00:21:02,226
This is where we're going to put this thing.
396
00:21:02,228 --> 00:21:05,896
It's of course, going to need it's own Custom Class so lets
397
00:21:05,898 --> 00:21:10,635
go file New. Create a subclass of UI View Controller. Okay.
398
00:21:10,637 --> 00:21:13,371
We'll call this our Edit Waypoint View Controller
399
00:21:13,373 --> 00:21:16,674
because that's what it does. Edit a waypoint.
400
00:21:16,676 --> 00:21:19,343
I'll put it same place as everything as usual.
401
00:21:19,345 --> 00:21:22,980
Here it is, we'll go ahead and get rid of all this stuff.
402
00:21:23,950 --> 00:21:26,484
Yeah, so that's clean. Let's go ahead and build the ui of
403
00:21:26,486 --> 00:21:30,321
it, okay what's the ui of this thing gonna look like here?
404
00:21:30,323 --> 00:21:32,290
Well, it's gonna be a pretty simple UI,
405
00:21:32,292 --> 00:21:33,591
it's gonna have two fields, one for
406
00:21:33,593 --> 00:21:36,527
the name of the waypoint and one for like the info, right,
407
00:21:36,529 --> 00:21:38,429
the little description of the waypoint. So,
408
00:21:38,431 --> 00:21:41,565
that's gonna be easy to build, let's just go down here and
409
00:21:41,567 --> 00:21:44,068
grab a text field or two, there's one text field,
410
00:21:44,070 --> 00:21:47,471
there's another one, let's grab a couple labels okay for
411
00:21:47,473 --> 00:21:50,141
our name and description. So, all right so
412
00:21:50,143 --> 00:21:51,442
this is the name of the waypoint.
413
00:21:51,444 --> 00:21:56,347
This is the description of it which is called the info field
414
00:21:56,349 --> 00:22:00,084
in the GPX structure. So, we'll right align these
415
00:22:00,086 --> 00:22:02,887
because they're gonna be names. We'll go ahead and
416
00:22:02,889 --> 00:22:06,724
put these in a stack view here. So, let's embed these in
417
00:22:06,726 --> 00:22:08,893
a stack view. Hopefully it'll figure out it's horizontal.
418
00:22:08,895 --> 00:22:11,495
It does. We'll put a little spacing in there,
419
00:22:11,497 --> 00:22:13,264
maybe eight or something like that.
420
00:22:13,266 --> 00:22:16,534
Same thing here, let's put those in. Okay.
421
00:22:16,536 --> 00:22:22,973
Also, eight. Now, let's put these in vertical stack view.
422
00:22:26,079 --> 00:22:29,113
Okay, maybe we'll also do 8 here as well.
423
00:22:29,115 --> 00:22:32,783
Okay. Now, these aren't quite looking like what we want,
424
00:22:32,785 --> 00:22:35,152
this, this kind of, this is in the center.
425
00:22:35,154 --> 00:22:37,421
We don't really want center alignment here,
426
00:22:37,423 --> 00:22:39,490
we'd like it to fill the entire space. Also,
427
00:22:39,492 --> 00:22:42,560
we'd really want this and this lined up, which really means
428
00:22:42,562 --> 00:22:45,296
we want Name and Description to be the same width. So,
429
00:22:45,298 --> 00:22:48,966
I'm actually going to control drag between these two and
430
00:22:48,968 --> 00:22:52,069
make them be equal widths, okay? Make name and
431
00:22:52,071 --> 00:22:55,973
description be equal widths and also it's gonna,
432
00:22:55,975 --> 00:22:59,477
let me take it back in a second here I think.
433
00:22:59,479 --> 00:23:00,911
Let's also put it in position.
434
00:23:00,913 --> 00:23:05,282
We're gonna put this leading edge, we're gonna put this
435
00:23:05,284 --> 00:23:09,253
the top edge, we'll put this to the trailing edge and
436
00:23:09,255 --> 00:23:13,924
we'll put this to the bottom edge. Okay? Now, where we
437
00:23:13,926 --> 00:23:17,995
put these is a little weird. Okay, this one is our standard
438
00:23:17,997 --> 00:23:21,899
thing here where we want this to be zero or standard value.
439
00:23:21,901 --> 00:23:23,701
So, there's no standard value so we'll make it zero.
440
00:23:23,703 --> 00:23:27,138
That makes it nice and wide. That's good. This one right
441
00:23:27,140 --> 00:23:30,374
here, I actually don't want this one to be zero cuz
442
00:23:30,376 --> 00:23:33,244
I really want name and description to sit at the top.
443
00:23:33,246 --> 00:23:35,746
So, you might argue I don't even need a constraint on
444
00:23:35,748 --> 00:23:38,716
the bottom here, but here's the thing when I put this
445
00:23:38,718 --> 00:23:41,952
on screen, okay the size is gonna change I always wanna
446
00:23:41,954 --> 00:23:45,122
make sure there's at least the standard issue thing from
447
00:23:45,124 --> 00:23:47,391
the bottom. Get a standard space from the bottom and
448
00:23:47,393 --> 00:23:49,527
you'll see why that is a little bit later. So,
449
00:23:49,529 --> 00:23:53,197
I'm actually have the constant here be greater than or
450
00:23:53,199 --> 00:23:54,932
equal to the standard value. Okay,
451
00:23:54,934 --> 00:23:57,535
so just remember that I put a constraint on the bottom to
452
00:23:57,537 --> 00:24:00,971
make sure it's greater than or equal to the standard value.
453
00:24:00,973 --> 00:24:03,641
We we'll see that later. So,
454
00:24:03,643 --> 00:24:08,078
equal widths. Thought I had done that. Okay, anyway, so
455
00:24:08,080 --> 00:24:12,850
this looks pretty good for our UI, okay? Using the space,
456
00:24:12,852 --> 00:24:15,853
etc. It's kind of lining things up nicely. So
457
00:24:15,855 --> 00:24:18,456
let's go ahead and create a couple of outlets to pl,
458
00:24:18,458 --> 00:24:23,494
plug into these two things, all right. Let's
459
00:24:23,496 --> 00:24:26,730
make sure we set the right class for this. We don't want
460
00:24:26,732 --> 00:24:29,567
to be in GPS Waypoint view controller, we wanted to be in
461
00:24:29,569 --> 00:24:32,536
Edit Waypoint view controller. That's why we're not getting
462
00:24:32,538 --> 00:24:36,240
the right class here when we try to do our list.
463
00:24:36,242 --> 00:24:37,575
Let us drag our outlay here.
464
00:24:37,577 --> 00:24:41,979
This outlay is going to be NameTextField. And
465
00:24:41,981 --> 00:24:47,017
this one is going to be, call this the infoTextField.
466
00:24:47,854 --> 00:24:53,457
All right? And so we kinda set up our UI here how we want it.
467
00:24:53,459 --> 00:24:58,128
What do we wanna do here in terms of a let's
468
00:24:58,130 --> 00:25:01,932
make this over here. Let's look at our edit wave.
469
00:25:01,934 --> 00:25:05,402
Okay, what do we want to do in terms of a public model here,
470
00:25:05,404 --> 00:25:05,703
so that people can set it?
471
00:25:05,705 --> 00:25:09,507
Well, that's obvious, we want the a- waypoint to edit. 'Kay?
472
00:25:09,509 --> 00:25:14,478
GPX, sorry, editable waypoint. Let it be optional.
473
00:25:14,480 --> 00:25:17,214
So that's the public API. If someone sets this waypoint
474
00:25:17,216 --> 00:25:20,017
then obviously we're going to have our UI show it. And when
475
00:25:20,019 --> 00:25:23,487
the UI is changing, it's going to be changing this way point.
476
00:25:23,489 --> 00:25:26,524
So, when it gets set, we're gonna have to update your,
477
00:25:26,526 --> 00:25:29,693
our UI. So we'll call updateUI and make a method that does
478
00:25:29,695 --> 00:25:34,765
that. Probably here when these well we'll talk about
479
00:25:34,767 --> 00:25:40,204
that in a second, so let's get our private funk update ui.
480
00:25:40,640 --> 00:25:41,939
So what do we do to update the ui?
481
00:25:41,941 --> 00:25:47,478
I'm just gonna have the name, text fields, text equal
482
00:25:47,480 --> 00:25:52,116
the waypoint to edit name.
483
00:25:52,818 --> 00:25:57,187
And similarly the info text field Text equals
484
00:25:57,189 --> 00:26:02,226
the Way PointTo Edit info. Okay. So
485
00:26:02,228 --> 00:26:05,996
that's updating that. Probably also we should View Did Load.
486
00:26:05,998 --> 00:26:12,403
And View Did Load will also update our UI.
487
00:26:12,405 --> 00:26:14,805
That way when we're sequeway to it or
488
00:26:14,807 --> 00:26:18,442
something if we set this before our outlets are set,
489
00:26:18,444 --> 00:26:23,714
later on or. Now this is great for transferring information
490
00:26:23,716 --> 00:26:26,183
from the waypoint to our text fields.
491
00:26:26,185 --> 00:26:28,986
What about the other way? We edit in the text fields,
492
00:26:28,988 --> 00:26:31,956
we have to edit our waypoint. So how are we going to that?
493
00:26:31,958 --> 00:26:33,591
Well, there's actually a number of ways
494
00:26:33,593 --> 00:26:34,558
to do that. I already showed you
495
00:26:34,560 --> 00:26:36,961
in textfield how to do that with delegate messages.
496
00:26:36,963 --> 00:26:40,064
I'm gonna show you how to do it now with radio stations.
497
00:26:40,066 --> 00:26:43,000
Okay? Text fields actually broadcast on a radio station,
498
00:26:43,002 --> 00:26:46,236
and one of the things they'll broadcast is my text changed.
499
00:26:46,238 --> 00:26:48,172
So you can listen to that radio station, and
500
00:26:48,174 --> 00:26:51,108
every time the text changes you can update whatever
501
00:26:51,110 --> 00:26:55,946
thing you want to update. Now, before we do that, we
502
00:26:55,948 --> 00:26:58,616
also need some things from the text fields delegate, however.
503
00:26:58,618 --> 00:27:01,218
Like, I'd like to control the keyboard a little bit.
504
00:27:01,220 --> 00:27:03,954
So, every time you press return in the keyboard,
505
00:27:03,956 --> 00:27:05,255
I'd like to remove the keyboard,
506
00:27:05,257 --> 00:27:07,858
just to clear it out of your way. So, how may I do that?
507
00:27:07,860 --> 00:27:13,831
I'm gonna make sure that I am the main text fields delegate.
508
00:27:15,301 --> 00:27:15,933
Okay so let's go ahead and
509
00:27:15,935 --> 00:27:19,903
make sure we are a UI text field delegate.
510
00:27:20,673 --> 00:27:25,509
Do the same thing with the info text field here.
511
00:27:25,511 --> 00:27:29,213
All right, so now with the delegate.
512
00:27:29,215 --> 00:27:32,016
What are we going to do here on that famous text
513
00:27:32,018 --> 00:27:35,586
field should return target method right here.
514
00:27:35,588 --> 00:27:39,857
I'm just gonna say text field, resign your first responder.
515
00:27:39,859 --> 00:27:43,360
'Kay. And I'll return true,
516
00:27:43,362 --> 00:27:45,229
'cause it can do whatever it wants there.
517
00:27:45,231 --> 00:27:47,064
We don't have, you're not gonna have any action setups.
518
00:27:47,066 --> 00:27:49,433
That's not gonna matter anyway. And, in fact,
519
00:27:49,435 --> 00:27:52,469
another thing I'd like is when this NDC first appears,
520
00:27:52,471 --> 00:27:55,606
I'd like to keyboard to appear. Okay I wanted it to
521
00:27:55,608 --> 00:27:58,842
come up don't want to make the person touch in there.
522
00:27:58,844 --> 00:28:03,547
So I'm gonna say let's do the name text field. Become
523
00:28:03,549 --> 00:28:07,851
first response responder. Okay so that means when we reload,
524
00:28:07,853 --> 00:28:09,620
we're gonna become the first responders.
525
00:28:09,622 --> 00:28:11,121
Question. >> Sir,
526
00:28:11,123 --> 00:28:12,523
did you say that the textfield should
527
00:28:12,525 --> 00:28:15,025
return because this one said the textfieldShouldReturn?
528
00:28:15,027 --> 00:28:15,359
>> Good catch, okay so
529
00:28:15,361 --> 00:28:17,161
this is not supposed to be textfieldShouldClear,
530
00:28:17,163 --> 00:28:18,395
this should be textfieldShouldReturn.
531
00:28:18,397 --> 00:28:23,100
Okay, good catch, that's why you guys are here to catch
532
00:28:23,102 --> 00:28:27,671
these mistakes. Yeah, so there we go, textfieldShouldReturn.
533
00:28:28,741 --> 00:28:31,809
All right, so that's gonna manage our keyboard, but
534
00:28:31,811 --> 00:28:35,345
now what about those radio stations? So let's go ahead,
535
00:28:35,347 --> 00:28:39,650
and when, our view appears on screen, okay?
536
00:28:39,652 --> 00:28:40,184
When we get viewDisappear,
537
00:28:40,186 --> 00:28:42,386
we're gonna start listening to those radio stations. And
538
00:28:42,388 --> 00:28:45,856
when we get viewWillDisappear, then we'll stop listening, all
539
00:28:45,858 --> 00:28:50,594
right? So viewWillappear, or else you can do viewDidappear.
540
00:28:50,596 --> 00:29:00,704
Super.viewDidAppear(animated).
541
00:29:00,706 --> 00:29:04,908
All right. So we got viewDidAppear,
542
00:29:04,910 --> 00:29:09,279
and so I am just going to say listenToTextFields Okay,
543
00:29:09,281 --> 00:29:17,321
we'll make a little private func that does that. Okay,
544
00:29:17,323 --> 00:29:20,257
so how we gonna listen to the text fields? Let's go ahead
545
00:29:20,259 --> 00:29:24,361
and get ourselves the center, okay, the NSNotification,
546
00:29:24,363 --> 00:29:29,633
NotificationCenter.defaultCen- ter. And
547
00:29:29,635 --> 00:29:31,802
let's get the q that we want, we're going to do all of this
548
00:29:31,804 --> 00:29:36,774
on the main Q. So ns operation q dot main q. All right now
549
00:29:36,776 --> 00:29:39,810
that we have those two things, two little variables here,
550
00:29:39,812 --> 00:29:43,046
I'm going to ask the center to listen to this radio station.
551
00:29:43,048 --> 00:29:47,584
So I'm going to add observer for name. Return here, so
552
00:29:47,586 --> 00:29:50,854
you can see this again, this is what we did last time.
553
00:29:50,856 --> 00:29:54,758
All right, what's the name of the radio station?
554
00:29:54,760 --> 00:29:59,797
It's UITextFieldTextDidChange, you can see it's got a couple
555
00:29:59,799 --> 00:30:03,367
of other radio stations there too for begin end editing, but
556
00:30:03,369 --> 00:30:05,969
I'm gonna get the one where every time the text change.
557
00:30:05,971 --> 00:30:08,338
Every single character It's going to update.
558
00:30:08,340 --> 00:30:12,276
The object here okay this one I'm gonna be listening to
559
00:30:12,278 --> 00:30:13,911
the Name text field. Okay, and
560
00:30:13,913 --> 00:30:18,882
we'll do it on the main queue. And here's the block.
561
00:30:18,884 --> 00:30:23,387
So what's our block going to do here? We actually don't
562
00:30:23,389 --> 00:30:25,322
even need to look at the notification, because we know
563
00:30:25,324 --> 00:30:28,325
this is the Name text field right here this change. So
564
00:30:28,327 --> 00:30:32,563
I'm just gonna say here, if I can let waypoint equal our
565
00:30:32,565 --> 00:30:36,667
waypointToEdit, so if our waypointToEdit is not nil,
566
00:30:36,669 --> 00:30:38,101
basically, then let's go ahead and
567
00:30:38,103 --> 00:30:43,874
have the waypoint's name equal the nameTextFields.text,
568
00:30:43,876 --> 00:30:48,412
all right? So every time this nameTextField changes,
569
00:30:48,414 --> 00:30:52,282
we're going to update, our name. Okay. And
570
00:30:52,284 --> 00:30:56,320
this needs to be self, because we're inside of closure here.
571
00:30:56,322 --> 00:31:00,424
Okay, we'll do the exact same thing for the info text field.
572
00:31:00,426 --> 00:31:03,961
One thing we could have done by the way, is have the object
573
00:31:03,963 --> 00:31:07,464
we're listening to be nil and get all UI text and
574
00:31:07,466 --> 00:31:10,167
then look at the text field that is sending it to us and
575
00:31:10,169 --> 00:31:13,170
find out which one it is. But, what if we had other text
576
00:31:13,172 --> 00:31:15,505
fields, other places in our UI that were listening, and
577
00:31:15,507 --> 00:31:18,075
maybe they weren't good and that they kept listening even
578
00:31:18,077 --> 00:31:21,645
when they were off screen, and all that getting all these.
579
00:31:21,647 --> 00:31:24,381
This is a little bit more code in terms of alliance
580
00:31:24,383 --> 00:31:26,750
right here, but it makes these closers really,
581
00:31:26,752 --> 00:31:30,320
really, really simple. Okay? So, this is gonna be
582
00:31:30,322 --> 00:31:35,292
the inform text field, update, now in viewDidAppear here,
583
00:31:35,294 --> 00:31:41,164
we're listing the textfields, in viewWillDisappear,
584
00:31:43,302 --> 00:31:47,204
We need to stop listening to these, okay so how do we do
585
00:31:47,206 --> 00:31:50,874
that, okay we just ask the notification center.
586
00:31:50,876 --> 00:31:55,412
DefaultCenter() to remove the Observer,
587
00:31:55,414 --> 00:31:58,615
okay, that is this guy right here.
588
00:31:58,617 --> 00:32:01,551
And same thing, we'll have to do another one for this.
589
00:32:01,553 --> 00:32:04,821
So this is where we have to use the return value of this
590
00:32:04,823 --> 00:32:07,891
addObserverForName. It actually returns something.
591
00:32:07,893 --> 00:32:09,793
We'll call this one ntfObserver.
592
00:32:09,795 --> 00:32:12,829
And this is just basically a cookie that represents
593
00:32:12,831 --> 00:32:14,298
this radio station going here. So
594
00:32:14,300 --> 00:32:18,201
we'll need a couple of private vars here. ntfObserver.
595
00:32:18,203 --> 00:32:21,505
These things are of type NSObjectProtocol. Which
596
00:32:21,507 --> 00:32:25,008
basically just means they're Objective-C compatible things,
597
00:32:25,010 --> 00:32:28,779
they're part, they're visible to the Objective-C runtime.
598
00:32:28,781 --> 00:32:33,317
Okay, and so we'll have info text field observer also.
599
00:32:35,955 --> 00:32:38,922
Okay, so we'll let the ntfObserver equal that,
600
00:32:38,924 --> 00:32:41,892
we'll have the itfObserver equal this. Okay, now
601
00:32:41,894 --> 00:32:45,495
that we have these cookies, we, we wanna remove them here,
602
00:32:45,497 --> 00:32:48,098
okay? We can just put them in here. But we wanna make sure
603
00:32:48,100 --> 00:32:51,034
they're not nil, so I'm gonna say if I can let observer
604
00:32:51,036 --> 00:32:58,442
equal the ntfObserver, then I will remove that observer.
605
00:32:58,811 --> 00:33:04,781
And same thing down here. If I can let observer
606
00:33:04,783 --> 00:33:08,919
equal to itfObserver,
607
00:33:08,921 --> 00:33:12,990
then I will move that. Probably
608
00:33:12,992 --> 00:33:14,725
could have made a nice little function that did this, so
609
00:33:14,727 --> 00:33:19,096
that I don't have this kind of same repeated code style here.
610
00:33:19,098 --> 00:33:21,898
Also, actually probably want to put these in another
611
00:33:21,900 --> 00:33:25,936
private func. Maybe call it private func
612
00:33:25,938 --> 00:33:31,008
stopListeningToTextFields. Put that in there, and
613
00:33:31,010 --> 00:33:37,047
we'll call that from here. Okay? Arrange this
614
00:33:37,049 --> 00:33:41,685
a little bit better. Put this down here. Okay, so basically,
615
00:33:41,687 --> 00:33:44,021
listening and stop listening. So everyone understand that?
616
00:33:44,023 --> 00:33:46,323
See how we're using radio stations to find out what's
617
00:33:46,325 --> 00:33:46,990
happening in the text fields?
618
00:33:46,992 --> 00:33:50,627
So it's kind of a, a fun way to do that. All right,
619
00:33:50,629 --> 00:33:54,331
so the last thing we need to do here is our prepare for
620
00:33:54,333 --> 00:33:57,901
segue, right? Over here, we, when we do this other segue,
621
00:33:57,903 --> 00:34:02,472
this segue, this modal segue, we need to prepare for it. So
622
00:34:02,474 --> 00:34:09,713
let's do that. Say else if segue.identifier ==
623
00:34:09,715 --> 00:34:15,919
EditUserWaypoint. Okay, that's the name of that, identifier.
624
00:34:15,921 --> 00:34:19,056
Then in here we just wanna get that editable waypoint.
625
00:34:19,058 --> 00:34:20,057
So I'm gonna see if I can
626
00:34:20,059 --> 00:34:22,592
let the editableWaypoint equal the waypoint as
627
00:34:22,594 --> 00:34:26,063
an EditableWaypoint, so make sure it's editable. It always
628
00:34:26,065 --> 00:34:29,866
should be or we wouldn't be here. Let's put a comma here.
629
00:34:29,868 --> 00:34:33,336
Then we can let our editable Waypoint view controller
630
00:34:33,338 --> 00:34:38,675
equal the destination as EditWaypointViewController.
631
00:34:38,677 --> 00:34:43,447
Okay, then we just set the EWVC's waypointToEdit, which
632
00:34:43,449 --> 00:34:50,420
is its public model there, to the editableWaypoint, okay?
633
00:34:50,422 --> 00:34:52,022
Now let's go back to our storyboard here and
634
00:34:52,024 --> 00:34:54,658
take a look at how we're going to create the segue
635
00:34:54,660 --> 00:34:56,493
right here. Because right now, there's no segue,
636
00:34:56,495 --> 00:35:00,997
this thing is free as a bird out here flying around, okay?
637
00:35:00,999 --> 00:35:04,201
So we want to edit it, so we're gonna do the same thing
638
00:35:04,203 --> 00:35:07,304
we did here when we created this one, which is,
639
00:35:07,306 --> 00:35:11,875
to drag from the, view controller, oops,
640
00:35:11,877 --> 00:35:15,479
not that one, from this view controller right here.
641
00:35:15,481 --> 00:35:20,717
We're gonna drag from this guy up to our new MVC.
642
00:35:20,719 --> 00:35:23,620
So let's do that. Ctrl+drag up here. Now, this time though,
643
00:35:23,622 --> 00:35:26,890
we're gonna do something new. This one's gonna be presented
644
00:35:26,892 --> 00:35:28,859
modally. So instead of saying show and
645
00:35:28,861 --> 00:35:31,261
having it appear inside this navigation controller,
646
00:35:31,263 --> 00:35:33,463
we're gonna do present modally and it's gonna take over
647
00:35:33,465 --> 00:35:35,932
the whole screen while we're editing the waypoint.
648
00:35:35,934 --> 00:35:38,135
So we do present modally. Now let's go ahead and
649
00:35:38,137 --> 00:35:40,737
take a look at this, sorry, let's make this a little
650
00:35:40,739 --> 00:35:45,442
easier to see here. Okay. Let's go ahead and take a look
651
00:35:45,444 --> 00:35:48,645
at this segue that was just created in the inspector.
652
00:35:48,647 --> 00:35:52,282
Okay, so you obviously will need an identifier.
653
00:35:52,284 --> 00:35:55,919
I think I called this one Edit Waypoint.
654
00:35:55,921 --> 00:35:59,523
I hope that's what I called it. Let's go take a quick
655
00:35:59,525 --> 00:36:02,292
look, make sure we get it right in our constants. Yes,
656
00:36:02,294 --> 00:36:05,962
Edit Waypoint. That's what we called this segue right there.
657
00:36:06,732 --> 00:36:09,599
And you can see it's got Present Modally here,
658
00:36:09,601 --> 00:36:13,570
okay? And we'll take a look at these presentation styles down
659
00:36:13,572 --> 00:36:17,207
here a little bit later. Okay? But for right now,
660
00:36:17,209 --> 00:36:19,209
we just wanna present modally. So let's go ahead and
661
00:36:19,211 --> 00:36:26,082
see what this does. All right, so
662
00:36:26,084 --> 00:36:29,052
here we go, we're gonna drop an editable waypoint, okay?
663
00:36:29,054 --> 00:36:33,089
It says Dropped, we can pick it up and move it, okay,
664
00:36:33,091 --> 00:36:35,892
to anywhere we want, like to that airport again. And
665
00:36:35,894 --> 00:36:38,328
hopefully, we press this i, we're gonna get this
666
00:36:38,330 --> 00:36:42,032
modal segue to this new MVC. So hit the i, there we go,
667
00:36:42,034 --> 00:36:44,100
modal segue. Now, we've got some problems here.
668
00:36:44,102 --> 00:36:46,736
Looks like we've got a little problem with our
669
00:36:46,738 --> 00:36:49,973
auto layout there. We'll have to take a look at that. And
670
00:36:49,975 --> 00:36:54,511
in fact, we've gotta fix that, or we're not gonna be able to,
671
00:36:54,513 --> 00:36:57,714
to edit this. So let's go back to our storyboard here and
672
00:36:57,716 --> 00:36:58,715
take a look at what the problem,
673
00:36:58,717 --> 00:37:01,184
probably was that thing where I said same width.
674
00:37:01,186 --> 00:37:04,821
Somebody has the same width who shouldn't, I believe.
675
00:37:04,823 --> 00:37:06,823
Maybe one of these stack views.
676
00:37:06,825 --> 00:37:09,559
Let's take a look at this guy. So I'm just gonna,
677
00:37:09,561 --> 00:37:12,362
when you get a problem with your auto layout,
678
00:37:12,364 --> 00:37:16,132
it's kind of a good idea to make a little tour through
679
00:37:16,134 --> 00:37:16,733
your various things.
680
00:37:16,735 --> 00:37:19,769
You can even see, I've got red here, conflicting constraints,
681
00:37:19,771 --> 00:37:22,405
so we know there's problems. And sure enough, look at this.
682
00:37:22,407 --> 00:37:26,109
When I said, tried to make the description equal to the name,
683
00:37:26,111 --> 00:37:28,612
I made this whole stack view equal to the name.
684
00:37:28,614 --> 00:37:31,615
You see that? Equal width to name, this whole stack view,
685
00:37:31,617 --> 00:37:35,185
so that's not good. So I don't want this, so I'm just gonna
686
00:37:35,187 --> 00:37:38,288
delete that constraint. And now this has probably fixed
687
00:37:38,290 --> 00:37:40,357
the problem, because you see I have no more warning
688
00:37:40,359 --> 00:37:44,227
of conflicting constraints. Okay? So let's go try again,
689
00:37:44,229 --> 00:37:50,800
see if that makes our UI look a little better. All right.
690
00:37:50,802 --> 00:37:56,172
We'll drop here. Edit. That looks a lot better.
691
00:37:56,174 --> 00:37:59,743
Okay. And you can see that it passed from the prepare for
692
00:37:59,745 --> 00:38:02,279
segue, it passed our existing name and info,
693
00:38:02,281 --> 00:38:04,981
which is just dropped with no info, into here. And
694
00:38:04,983 --> 00:38:07,784
it also put the keyboard up, was nice. And if I get rid of,
695
00:38:07,786 --> 00:38:10,020
hit return, it makes the keyboard go away. And
696
00:38:10,022 --> 00:38:13,290
if I make one of these first responder, it comes back.
697
00:38:13,292 --> 00:38:14,891
All right. And I can edit this.
698
00:38:14,893 --> 00:38:21,231
Let's say, I'll say, I love it when people, do something,
699
00:38:21,233 --> 00:38:27,437
okay. And how about, the fact is your name.
700
00:38:27,439 --> 00:38:31,408
Okay, so here's our basically name of our waypoint and
701
00:38:31,410 --> 00:38:34,678
the description. And so, uh-oh, how do we get out of
702
00:38:34,680 --> 00:38:38,615
here? It's like, we can't get out. So this is a problem I
703
00:38:38,617 --> 00:38:41,818
was telling you about. Modal, you have to provide a way for
704
00:38:41,820 --> 00:38:44,621
your user to escape once they've edited whatever they
705
00:38:44,623 --> 00:38:47,757
want to edit. Okay? So how are we gonna do that? Let's go
706
00:38:47,759 --> 00:38:49,993
back to our storyboard. The simple way to do it
707
00:38:49,995 --> 00:38:53,029
is to put this inside a navigation controller and
708
00:38:53,031 --> 00:38:57,000
put a done button up there like we saw in the slides.
709
00:38:57,002 --> 00:38:58,201
So let's do that. Let's go here,
710
00:38:58,203 --> 00:39:00,270
Edit > Embed In > Navigation Controller,
711
00:39:00,272 --> 00:39:03,440
it puts it in a navigation controller right here. And
712
00:39:03,442 --> 00:39:04,741
we're just gonna put a Done button right there.
713
00:39:04,743 --> 00:39:09,145
So let's go down here. We'll search for bar button item.
714
00:39:09,147 --> 00:39:11,815
Here it is, a Bar Button Item. We'll put it right up here.
715
00:39:11,817 --> 00:39:15,919
I think there's even a standard system one for that,
716
00:39:15,921 --> 00:39:20,824
maybe. Yeah, there's a Done button right there, okay, for
717
00:39:20,826 --> 00:39:21,124
clicking Done.
718
00:39:21,126 --> 00:39:24,594
And then we'll just make this done button call a method
719
00:39:24,596 --> 00:39:26,229
in this view controller, by the way, so
720
00:39:26,231 --> 00:39:29,165
you can see our stack view frames are a little off now.
721
00:39:29,167 --> 00:39:32,235
We can fix that clicking on here and
722
00:39:32,237 --> 00:39:35,372
doing a update frames, moves that down. But
723
00:39:35,374 --> 00:39:38,875
let's make this so that it sends a message to ourselves,
724
00:39:38,877 --> 00:39:41,578
and then we'll dismiss ourselves. Okay, so
725
00:39:41,580 --> 00:39:46,149
let's do that. We get here, we'll put this.
726
00:39:46,151 --> 00:39:48,585
Let's make it a little more space for you there. Okay,
727
00:39:48,587 --> 00:39:52,856
we'll put that dismiss, how about, I don't know,
728
00:39:52,858 --> 00:39:56,259
right up after our view controller lifecycle here.
729
00:39:56,261 --> 00:39:59,062
Okay? So I'm just gonna Ctrl+drag right in here. This
730
00:39:59,064 --> 00:40:02,932
is gonna be an action. This is my done action, I'll call it.
731
00:40:02,934 --> 00:40:07,137
Okay, BarButtonitem is gonna be the argument. Here it is.
732
00:40:07,139 --> 00:40:08,972
Now remember, again,
733
00:40:08,974 --> 00:40:13,943
let's go twofold width here. Remember, here,
734
00:40:13,945 --> 00:40:18,214
we don't want to say just dismissViewControllerAnimated.
735
00:40:18,216 --> 00:40:23,353
We want to get our presentingViewController.
736
00:40:23,355 --> 00:40:28,091
That's the view controller that presented us and
737
00:40:28,093 --> 00:40:32,195
ask it to dismiss us, okay. And we don't care when that
738
00:40:32,197 --> 00:40:34,864
happens. Okay, so don't forget that.
739
00:40:34,866 --> 00:40:38,301
I'm kind of putting this self here just to let you know I'm
740
00:40:38,303 --> 00:40:41,805
talking about my self's presentingViewController but
741
00:40:41,807 --> 00:40:43,773
you don't need self there obviously,
742
00:40:43,775 --> 00:40:46,810
okay? Understand that? All right, so let's see that.
743
00:40:46,812 --> 00:40:54,150
See how that makes this work. All right,
744
00:40:54,152 --> 00:40:58,922
so we'll drop this. Go here, we'll edit it. Comes up,
745
00:40:58,924 --> 00:41:00,623
we've got our Navigation Controller, it's there.
746
00:41:00,625 --> 00:41:03,460
We probably should have put a title called edit waypoint or
747
00:41:03,462 --> 00:41:06,696
something like that. So let's go ahead and edit this again.
748
00:41:06,698 --> 00:41:12,235
So let's say the best thing. Okay, that's what our name is.
749
00:41:12,237 --> 00:41:17,740
And the description is, the only way. All right, so we got
750
00:41:17,742 --> 00:41:21,177
the best thing and the only way there. And we hit Done.
751
00:41:21,179 --> 00:41:25,048
And it still says, Dropped, okay? Now why is that?
752
00:41:25,050 --> 00:41:27,851
Well, that's because that callout was put up, and
753
00:41:27,853 --> 00:41:30,954
when it's put up, it's loaded up with the waypoint's
754
00:41:30,956 --> 00:41:34,090
information. And it stays that way all the time,
755
00:41:34,092 --> 00:41:37,894
okay? The only time it changes is if it goes away and
756
00:41:37,896 --> 00:41:42,232
comes back. When it comes back, it came back, okay?
757
00:41:42,234 --> 00:41:45,201
Now we got a bug somewhere in our code cuz it should have
758
00:41:45,203 --> 00:41:49,706
our subtitle there. But that was really annoying for
759
00:41:49,708 --> 00:41:51,474
our user that they went and edited this and
760
00:41:51,476 --> 00:41:54,244
when they came back it still said Dropped, okay.
761
00:41:54,246 --> 00:41:56,479
So let's fix the bug so that this the only way,
762
00:41:56,481 --> 00:42:01,684
which I think was the info was yeah, so here we go.
763
00:42:01,686 --> 00:42:05,755
This should be info = the infoTextField,
764
00:42:05,757 --> 00:42:10,360
okay? So how are we gonna fix that? Well, one thing about it
765
00:42:10,362 --> 00:42:16,165
right off the bat is that when we click on this little i,
766
00:42:16,167 --> 00:42:19,402
I'm gonna take this callout off the screen. Because I know
767
00:42:19,404 --> 00:42:22,572
it's pretty much invalid right away because if I go over here
768
00:42:22,574 --> 00:42:23,673
and change this to anything else,
769
00:42:23,675 --> 00:42:26,576
that thing is no good anymore. So let's do that as step one.
770
00:42:26,578 --> 00:42:29,646
Let's just take that callout away when we do this
771
00:42:29,648 --> 00:42:31,948
performForSeg, performSegue. So
772
00:42:31,950 --> 00:42:33,516
let's find where we do that performSegue,
773
00:42:33,518 --> 00:42:38,588
it's in this funny little calloutAccessoryTapped, right?
774
00:42:38,590 --> 00:42:40,089
Here's where we perform that segue.
775
00:42:40,091 --> 00:42:41,558
And when we perform that segue,
776
00:42:41,560 --> 00:42:45,128
we're going to remove that callout when we do that. And
777
00:42:45,130 --> 00:42:50,099
so, we do that by saying mapView.deselectAnnotation
778
00:42:50,101 --> 00:42:52,735
because the reason that callout is up is because this
779
00:42:52,737 --> 00:42:55,805
annotation is selected. So, if we deselect this annotation,
780
00:42:55,807 --> 00:42:59,042
it will remove that callout. And what do we wanna do?
781
00:42:59,044 --> 00:43:02,078
We wanna remove the annotation in the view that our
782
00:43:02,080 --> 00:43:05,348
callout is currently accessory control tapping.
783
00:43:05,350 --> 00:43:07,550
And sure, we'll, we'll animate it, although it probably
784
00:43:07,552 --> 00:43:09,752
doesn't matter that it's animated because we're gonna
785
00:43:09,754 --> 00:43:10,853
put a modal thing right on top of it. But
786
00:43:10,855 --> 00:43:13,756
maybe it'll be fading out as that's sliding in, that might
787
00:43:13,758 --> 00:43:18,428
be kinda nice. Okay, so let's see if that fixes our problem.
788
00:43:22,233 --> 00:43:25,068
All right, here we go. Put this on the airport here.
789
00:43:25,070 --> 00:43:27,704
We'll click there, there's Dropped. We click.
790
00:43:27,706 --> 00:43:31,341
Okay, we go to Dropped. And we'll put something in here.
791
00:43:31,343 --> 00:43:36,412
Say I'm so excited, that's good.
792
00:43:36,414 --> 00:43:40,083
Description, the fact is. Okay, and
793
00:43:40,085 --> 00:43:44,253
we'll hit Done and hopefully, it's not showing it.
794
00:43:44,255 --> 00:43:45,855
So at least it's not saying dropped, so
795
00:43:45,857 --> 00:43:49,192
that's the improvement, right? But if we click on it, it's
796
00:43:49,194 --> 00:43:52,829
showing it. So really what we'd like is when we come back
797
00:43:52,831 --> 00:43:55,732
from that modal segue, we'd like it to put this thing back
798
00:43:55,734 --> 00:43:58,968
up with the new information. Okay, so how are we gonna do
799
00:43:58,970 --> 00:44:02,839
that, cuz we don't know when that modal thing goes away,
800
00:44:02,841 --> 00:44:05,708
okay. It's just getting dismissed, right? So this is
801
00:44:05,710 --> 00:44:09,679
a good opportunity to show you unwind. Okay, so we're gonna,
802
00:44:09,681 --> 00:44:12,915
instead of hitting Done and having it dismiss ourselves,
803
00:44:12,917 --> 00:44:14,917
we're going to do an unwind segue back to
804
00:44:14,919 --> 00:44:17,387
our GPXController. And when it gets that message,
805
00:44:17,389 --> 00:44:20,657
that special message, it'll know I'm gonna put this thing
806
00:44:20,659 --> 00:44:23,826
back up, okay. So let's do that, so how do we do that?
807
00:44:23,828 --> 00:44:27,330
We know that when we unwind, we always wanna create one of
808
00:44:27,332 --> 00:44:31,401
this special methods, okay? And those special methods,
809
00:44:31,403 --> 00:44:34,904
first of all, they're always IBActions, okay?
810
00:44:34,906 --> 00:44:38,141
They can have any name that we want, so we'll call ours
811
00:44:38,143 --> 00:44:42,045
updatedUserWaypoint because that's what happened, okay?
812
00:44:42,047 --> 00:44:44,414
When the unwind happens, the user update waypoint
813
00:44:44,416 --> 00:44:48,785
got updated. And the other thing that's special about it,
814
00:44:48,787 --> 00:44:52,288
the argument has to be a UIStoryboardSegue, okay?
815
00:44:52,290 --> 00:44:54,957
So this is the special method and this is all we need to do
816
00:44:54,959 --> 00:44:57,794
to create one of these special message, methods.
817
00:44:57,796 --> 00:44:59,829
We'll call it a func. All right, and
818
00:44:59,831 --> 00:45:03,599
so now anybody can unwind to this as long as there's
819
00:45:03,601 --> 00:45:06,669
something we presented. Any MVC that we've presented or
820
00:45:06,671 --> 00:45:09,672
that was presented by somebody we presented,
821
00:45:09,674 --> 00:45:12,408
etc., can now unwind back to us here.
822
00:45:12,410 --> 00:45:14,644
Now what are we gonna do when we unbind back to here?
823
00:45:14,646 --> 00:45:18,314
Well, we're gonna reselect that annotation that we
824
00:45:18,316 --> 00:45:22,452
deselected when we left. So let's do that.
825
00:45:22,454 --> 00:45:25,054
I'll create a little function to do that called sec,
826
00:45:25,056 --> 00:45:31,094
selectWaypoint. Okay, and selectWaypoint, we gotta get
827
00:45:31,096 --> 00:45:34,030
the waypoint we selected, it's a little tricky, watch this.
828
00:45:34,032 --> 00:45:38,668
I'm gonna get the segue's sourceViewController. That's
829
00:45:38,670 --> 00:45:41,237
the view controller that is sending us this unwind.
830
00:45:41,239 --> 00:45:43,272
So that's gonna be the modal segue, right?
831
00:45:43,274 --> 00:45:45,274
That's the source of the unwind. So
832
00:45:45,276 --> 00:45:46,776
we're gonna get that sourceViewController.
833
00:45:46,778 --> 00:45:49,479
And actually we'll get its contentViewController,
834
00:45:49,481 --> 00:45:55,451
okay? And it has to be an EditWaypointViewController.
835
00:45:55,453 --> 00:45:57,253
And if we're able to find that,
836
00:45:57,255 --> 00:46:00,223
then we'll just get its waypointToEdit. Okay,
837
00:46:00,225 --> 00:46:03,659
that's obviously the waypoint that we want to select.
838
00:46:03,661 --> 00:46:07,463
So we need to, this method selectWaypoint. Let's do that.
839
00:46:07,465 --> 00:46:12,502
func selectWaypoint takes a waypoint
840
00:46:12,504 --> 00:46:17,240
which is at GPX.Waypoint, point. We'll
841
00:46:17,242 --> 00:46:19,675
even make it optional if you wanna pass it in there. And
842
00:46:19,677 --> 00:46:24,614
we'll just say if the waypoint != nil then we are gonna go
843
00:46:24,616 --> 00:46:29,786
to the mapView and tell it to select an annotation, which is
844
00:46:29,788 --> 00:46:34,991
the waypoint. Okay, remember that all of our waypoints
845
00:46:34,993 --> 00:46:37,827
are annotations, so we can directly ask the mapView. This
846
00:46:37,829 --> 00:46:40,797
is what's really nice about having some data structure
847
00:46:40,799 --> 00:46:43,866
that we already have in our app, like a GPX waypoint,
848
00:46:43,868 --> 00:46:45,735
implement that MK annotation protocol.
849
00:46:45,737 --> 00:46:48,704
Because we're not having to constantly create these other
850
00:46:48,706 --> 00:46:51,774
little things that contain all the waypoint in you know,
851
00:46:51,776 --> 00:46:54,777
the MK notation information, we just get it directly from
852
00:46:54,779 --> 00:46:57,346
our waypoints. Okay, so this is gonna select it.
853
00:46:57,348 --> 00:47:01,017
So now we have to unwind to this instead of doing Done.
854
00:47:01,019 --> 00:47:03,653
So let's go back to our storyboard here. Okay,
855
00:47:03,655 --> 00:47:06,622
we've got this Done. I'm gonna disconnect Done
856
00:47:06,624 --> 00:47:09,525
from this done method, right. So I'm just going here and
857
00:47:09,527 --> 00:47:12,395
disconnecting it. So now it's not gonna send Done. And
858
00:47:12,397 --> 00:47:16,065
in fact, if we go back to our EditWaypointViewController,
859
00:47:16,067 --> 00:47:19,368
we can just delete this done method entirely whenever it
860
00:47:19,370 --> 00:47:22,905
is, oops, there it is. Okay, we're not even gonna use that
861
00:47:22,907 --> 00:47:25,808
anymore. Instead of doing that, we are going to unwind.
862
00:47:25,810 --> 00:47:28,878
So let's go back here and unwind. So, how do we unwind?
863
00:47:28,880 --> 00:47:33,583
Everyone remember? Ctrl+drag to this Exit button. Okay,
864
00:47:33,585 --> 00:47:36,519
this is how you unwind. This is saying I want this Done
865
00:47:36,521 --> 00:47:39,021
button to unwind, okay? To exit this MVC and
866
00:47:39,023 --> 00:47:41,624
go back to one of its presenters. And look, when we
867
00:47:41,626 --> 00:47:44,861
click, look what we see in the list! updatedUserWaypoint.
868
00:47:44,863 --> 00:47:46,796
That method we put there with the special
869
00:47:46,798 --> 00:47:49,065
IBAction in this segue as an argument,
870
00:47:49,067 --> 00:47:51,601
okay? And every method in every MVC we ever
871
00:47:51,603 --> 00:47:54,203
would've put in our entire app that was like that would
872
00:47:54,205 --> 00:47:57,173
appear here. Yeah, question. >> Does the unwind only work
873
00:47:57,175 --> 00:47:59,342
in a Navigation Controller? >> So the question,
874
00:47:59,344 --> 00:48:01,844
does unwind only work inside a Navigation Controller?
875
00:48:01,846 --> 00:48:03,379
And the answer is no, it works anywhere.
876
00:48:03,381 --> 00:48:06,616
So you could have just a non-navigation controller MBC
877
00:48:06,618 --> 00:48:09,719
present another one modally which presents another one
878
00:48:09,721 --> 00:48:13,055
modally, and then you unwind back to either of those two.
879
00:48:13,057 --> 00:48:15,358
So it has nothing to do with navigation controller.
880
00:48:15,360 --> 00:48:17,860
This is not pop you now back or anything.
881
00:48:17,862 --> 00:48:21,664
This is really just take this MBCs off until you get back to
882
00:48:21,666 --> 00:48:24,166
the guy who implements this method, okay?
883
00:48:24,168 --> 00:48:26,535
Now it would work in a navigation controller as well.
884
00:48:26,537 --> 00:48:29,772
It would pop back from that as well, but it works in any
885
00:48:29,774 --> 00:48:33,476
any environment basically. All right so I'm gonna click that
886
00:48:33,478 --> 00:48:37,113
and that's gonna make the done button unwind to that method.
887
00:48:37,115 --> 00:48:42,618
Okay so that method is gonna get called. So let's ugh. We
888
00:48:42,620 --> 00:48:47,256
looking at this method when it happens. Let's go over here.
889
00:48:51,529 --> 00:48:55,665
All right so drop the pin right here gonna click.
890
00:48:55,667 --> 00:48:59,435
It says dropped right here. We're gonna go edit it.
891
00:48:59,437 --> 00:49:03,339
Change it to something here,
892
00:49:03,341 --> 00:49:08,511
how about the fact that you have to,
893
00:49:08,513 --> 00:49:11,747
and we'll call this I'm so
894
00:49:11,749 --> 00:49:15,785
happy, okay? So we're gonna hit done here and hopefully,
895
00:49:15,787 --> 00:49:18,187
we're gonna see that call out, show up,
896
00:49:18,189 --> 00:49:20,056
without having to click on anything and, sure enough,
897
00:49:20,058 --> 00:49:24,226
it does. Okay because it unwind back, called this code
898
00:49:24,228 --> 00:49:26,529
right here which selected that annotation.
899
00:49:26,531 --> 00:49:31,667
This is back in our TPX view controller here, see? Okay,
900
00:49:31,669 --> 00:49:34,036
everybody got the unwind there? All right,
901
00:49:34,038 --> 00:49:37,974
the next segue we're gonna look at is popovers. Okay,
902
00:49:37,976 --> 00:49:41,110
let's take a look at our app on iPad. And
903
00:49:41,112 --> 00:49:44,280
to do this I'm actually gonna do it on my device over here.
904
00:49:44,282 --> 00:49:49,819
So let's go up here, this is device and run this.
905
00:49:54,559 --> 00:49:57,760
We made this to a universal app so it should work fine
906
00:49:57,762 --> 00:50:01,097
over here. Sure enough there it is, zooming in to where we
907
00:50:01,099 --> 00:50:05,167
are, okay. Here's our stuff we can zoom in here and
908
00:50:05,169 --> 00:50:08,304
we can click on these things just like we could before. And
909
00:50:08,306 --> 00:50:14,643
we can segue to see this. Okay all that stuff is nice.
910
00:50:14,645 --> 00:50:18,314
And we can also add new pins, so I'm just long pressing.
911
00:50:18,316 --> 00:50:21,684
There's a pin. Okay, let's click there, okay dropped.
912
00:50:21,686 --> 00:50:27,390
We're going to edit it. Whoa. Okay this is pretty ugly UI.
913
00:50:27,392 --> 00:50:29,325
Okay do not do this in your final project.
914
00:50:29,327 --> 00:50:31,660
I cannot tell you how many final projects
915
00:50:31,662 --> 00:50:35,031
think I've made it work on the iPad with this kind of UI.
916
00:50:35,033 --> 00:50:37,099
Okay, this kind of UI looks ridiculous.
917
00:50:37,101 --> 00:50:39,568
You're never going to have the name and description be that
918
00:50:39,570 --> 00:50:43,506
long, okay. So you don't want that. There are other ways of
919
00:50:43,508 --> 00:50:45,608
presenting modal NBC's that look a lot
920
00:50:45,610 --> 00:50:49,345
better on iPad. So let's go pick one of them, let's go
921
00:50:49,347 --> 00:50:53,482
back to our storyboard here, oops. Let's go back to our
922
00:50:53,484 --> 00:50:57,553
storyboard and we're just gonna change our segue here,
923
00:50:57,555 --> 00:51:00,890
this is the modal segue okay. That puts this thing up,
924
00:51:00,892 --> 00:51:03,893
okay we're gonna inspect it and I'm gonna try a different
925
00:51:03,895 --> 00:51:07,396
presentation like for example form sheet. So form sheet is
926
00:51:07,398 --> 00:51:10,399
much more appropriate for the iPad. So let's go ahead and
927
00:51:10,401 --> 00:51:12,935
run this. See what form sheet looks like. The form sheet
928
00:51:12,937 --> 00:51:16,472
modal presentation. It's just a different presentation than
929
00:51:16,474 --> 00:51:17,540
take over the whole screen.
930
00:51:17,542 --> 00:51:18,574
It still takes over the whole screen,
931
00:51:18,576 --> 00:51:21,877
it just does it a little more space efficiently here.
932
00:51:21,879 --> 00:51:24,013
All right, so I'm dropping a pin there and
933
00:51:24,015 --> 00:51:24,580
we're gonna edit it. And
934
00:51:24,582 --> 00:51:28,617
you can see this gives a much more sensible looking space.
935
00:51:28,619 --> 00:51:32,154
Now the background here on the side, the grey on the side,
936
00:51:32,156 --> 00:51:34,423
okay, is not active. I can't do anything with it so
937
00:51:34,425 --> 00:51:36,525
it's still modal, it's still completely taking over,
938
00:51:36,527 --> 00:51:40,029
it just presenting it a little nicer here. Okay, and I can
939
00:51:40,031 --> 00:51:44,767
still do all the things that I would do here. Ya know, hello.
940
00:51:44,769 --> 00:51:49,271
Okay so, that's nice but, really on the iPad, what would
941
00:51:49,273 --> 00:51:53,776
be really cool is to do it in a little pop over right there.
942
00:51:53,778 --> 00:51:57,179
Okay to have the name, and info just be in that small,
943
00:51:57,181 --> 00:52:00,249
little pop over pointing right at that little pin,
944
00:52:00,251 --> 00:52:03,352
that would be a much better UI. And so it's great because
945
00:52:03,354 --> 00:52:06,222
we can learn how to do pop overs. So lets do that, how
946
00:52:06,224 --> 00:52:11,393
are we gonna do popovers? So a, and when we have a popover
947
00:52:11,395 --> 00:52:14,630
we're not gonna want this Navigation Controller in here,
948
00:52:14,632 --> 00:52:17,032
okay? We'll zoom out so you can see it.
949
00:52:17,034 --> 00:52:20,069
Okay if when we have a popover to do this we wanna get
950
00:52:20,071 --> 00:52:21,303
rid of this Navigation Controller here.
951
00:52:21,305 --> 00:52:24,140
So I'm gonna delete that. So we have this, okay?
952
00:52:24,142 --> 00:52:27,276
Now, I'm just gonnato create a popover in the same way,
953
00:52:27,278 --> 00:52:28,677
as we created all the other ones, okay?
954
00:52:28,679 --> 00:52:32,781
Which is control drag from here up to here. But
955
00:52:32,783 --> 00:52:36,285
this time, I'm gonna choose present as popover. Okay and
956
00:52:36,287 --> 00:52:40,422
this creates this right here and you can see here's the pop
957
00:52:40,424 --> 00:52:43,659
over segue. Now we of course need an identifier for it.
958
00:52:43,661 --> 00:52:45,728
We're going to do the same identifier cause we're
959
00:52:45,730 --> 00:52:47,630
going to have the same prepare segue as we have for
960
00:52:47,632 --> 00:52:52,168
the mog modal one which was edit waypoint. All right.
961
00:52:52,170 --> 00:52:55,771
Notice popover has some extra things down here. Okay, and
962
00:52:55,773 --> 00:52:58,741
this mostly has to do with when I put this popover up,
963
00:52:58,743 --> 00:53:01,911
okay, where do you want it to appear? And part of where
964
00:53:01,913 --> 00:53:04,380
it wants to appear is where you are going to allow
965
00:53:04,382 --> 00:53:07,583
the Popovers little arrow to point, point up, or left, or
966
00:53:07,585 --> 00:53:10,920
down, or right that's gonna to kind of position it a little
967
00:53:10,922 --> 00:53:13,689
bit. In our case we'll let it point any direction.
968
00:53:13,691 --> 00:53:17,526
So, depending on whether your weight point is up in the left
969
00:53:17,528 --> 00:53:18,761
corner, in the right corner,
970
00:53:18,763 --> 00:53:20,362
we'll let the pop over be above it, below it,
971
00:53:20,364 --> 00:53:24,300
whatever the system thinks is best. But the popover also
972
00:53:24,302 --> 00:53:26,936
needs needs an anchor, which is when I come up I've gotta
973
00:53:26,938 --> 00:53:29,471
point at something. And we're not bringing this up with
974
00:53:29,473 --> 00:53:32,308
a bar button, or something we can point to. We're bringing
975
00:53:32,310 --> 00:53:35,311
it up by clicking on this little annotation thing. So,
976
00:53:35,313 --> 00:53:39,748
we really want the anchor to be those pins. Okay so
977
00:53:39,750 --> 00:53:42,218
those pins are in the map view so
978
00:53:42,220 --> 00:53:45,754
I'm going to have the anchor right here be the map view and
979
00:53:45,756 --> 00:53:48,591
then in code I'm going to specify a rectangle in the map
980
00:53:48,593 --> 00:53:51,794
view which contains that little annotation view. So
981
00:53:51,796 --> 00:53:55,731
the way I make the map view be anchor is I Ctrl+Drag
982
00:53:55,733 --> 00:53:59,168
over to this. Okay, from the little circle. Like that and
983
00:53:59,170 --> 00:54:01,070
you can see it puts the map view in there.
984
00:54:01,072 --> 00:54:03,272
So now it's saying okay, I understand that
985
00:54:03,274 --> 00:54:06,342
I'm gonna point up pointing to somewhere in the map view.
986
00:54:06,344 --> 00:54:08,010
In your code you better tell me where and
987
00:54:08,012 --> 00:54:11,480
we're gonna tell you, tell it where, okay?
988
00:54:13,551 --> 00:54:16,785
Okay, okay, all right.
989
00:54:16,787 --> 00:54:20,022
So where are we gonna set that little rectangle, okay?
990
00:54:20,024 --> 00:54:23,659
We're gonna set that in our prepareforsegue, for
991
00:54:23,661 --> 00:54:26,862
this segue, that's where you set up all your popover stuff,
992
00:54:26,864 --> 00:54:28,831
is in the prepareforsegue for that segue.
993
00:54:28,833 --> 00:54:32,635
So here is that part of this prepareforsegue,
994
00:54:32,637 --> 00:54:35,871
write the edit user waypoint, part of the prepareforsegue.
995
00:54:35,873 --> 00:54:39,241
All we're gonna do here is get the popover presentation
996
00:54:39,243 --> 00:54:43,178
controller. So if we can get that, we get it from the MBC
997
00:54:43,180 --> 00:54:47,983
we're presenting. So in this case it's the EWVC, the edit
998
00:54:47,985 --> 00:54:50,719
wavepoint view controller. We get it from that.
999
00:54:50,721 --> 00:54:51,720
And we get it from this method,
1000
00:54:51,722 --> 00:54:55,257
pop over presentation controller. Now if we're now
1001
00:54:55,259 --> 00:54:58,427
popping over this would be null. This code won't execute,
1002
00:54:58,429 --> 00:55:01,230
excellent, okay? But if we're doing a popover, then it will
1003
00:55:01,232 --> 00:55:04,800
not be null. So in here we configure the popover
1004
00:55:04,802 --> 00:55:06,535
presentation controller and you can go look at the doc,
1005
00:55:06,537 --> 00:55:10,506
but one of the things you can do is the source rectangle
1006
00:55:10,508 --> 00:55:14,710
that it's gonna point to in your anchor view, okay? And
1007
00:55:14,712 --> 00:55:16,278
what is this gonna be? Well that's easy for
1008
00:55:16,280 --> 00:55:20,683
us, that's just gonna be the annotation view's frame. Okay,
1009
00:55:20,685 --> 00:55:21,650
the frame of the little pin,
1010
00:55:21,652 --> 00:55:26,355
that's where we want the popover to point. Make sense?
1011
00:55:26,357 --> 00:55:29,091
All right so, so, let's run this, see what it looks like.
1012
00:55:39,270 --> 00:55:43,939
All right? So, we'll drop this here, click on it and
1013
00:55:43,941 --> 00:55:47,710
whoa! Well, hm, okay we're makin progress but I
1014
00:55:47,712 --> 00:55:52,581
don't think we're quite there. So what's wrong with this?
1015
00:55:52,583 --> 00:55:54,883
Well, one thing is that thing is huge!
1016
00:55:54,885 --> 00:55:57,753
Way bigger than it needs to be! Why is it so big?
1017
00:55:57,755 --> 00:56:00,456
The answer is because no one told it what size it was
1018
00:56:00,458 --> 00:56:03,826
supposed to be. Okay, and you remember from the slides,
1019
00:56:03,828 --> 00:56:05,594
we do this in an object-oriented way.
1020
00:56:05,596 --> 00:56:09,164
The system wants the MVC that's being put up to say,
1021
00:56:09,166 --> 00:56:13,669
what size do you want to be? Now, what's really cool is,
1022
00:56:13,671 --> 00:56:18,207
I'm gonna calculate that size using Auto Layout, okay?
1023
00:56:18,209 --> 00:56:21,110
I'm basically gonna say, make yourself as small as you can
1024
00:56:21,112 --> 00:56:23,746
and still obey the Auto Layout rules, okay? And you can do
1025
00:56:23,748 --> 00:56:26,482
that with one line of code. So it's really cool. All right,
1026
00:56:26,484 --> 00:56:28,884
so where am I gonna do that? So I'm gonna do that in my
1027
00:56:28,886 --> 00:56:30,185
EditWaypointViewController. Okay,
1028
00:56:30,187 --> 00:56:32,955
this is the view controller that's coming up there. So
1029
00:56:32,957 --> 00:56:33,655
it's the one responsible for
1030
00:56:33,657 --> 00:56:37,159
setting its preferred size. And I'm gonna do it in
1031
00:56:37,161 --> 00:56:42,231
the view controller life cycle method viewWillLayoutSubviews.
1032
00:56:42,233 --> 00:56:45,334
Okay, so, I know that in viewWillLayOutSubviews,
1033
00:56:45,336 --> 00:56:49,071
my balance is gonna be set to whatever the latest thing is.
1034
00:56:49,073 --> 00:56:53,375
So I know I'm gonna have some good, bounds situation.
1035
00:56:53,377 --> 00:56:55,677
So I'm just gonna, in here, I'm gonna set my,
1036
00:56:55,679 --> 00:57:02,151
preferred content size, that's the size that I prefer to be,
1037
00:57:02,153 --> 00:57:04,119
to be the following thing,
1038
00:57:04,121 --> 00:57:09,825
view dot, okay, and all views implement this cool method,
1039
00:57:09,827 --> 00:57:17,299
systemLayoutSizeFittingSize, okay.
1040
00:57:17,301 --> 00:57:20,402
So that's basically saying, use Auto Layout and
1041
00:57:20,404 --> 00:57:20,803
tell me what size will
1042
00:57:20,805 --> 00:57:25,407
fit in this size the best. And there's a really cool size,
1043
00:57:25,409 --> 00:57:29,845
predefined size, called the UILayoutFittingSize.
1044
00:57:29,847 --> 00:57:33,282
There's a ExpandedSize, which means, make me as big as you
1045
00:57:33,284 --> 00:57:36,852
can and obey Auto Layout, and then there's CompressedSize,
1046
00:57:36,854 --> 00:57:40,389
which is, make me as small as you can. Okay, so, I'm gonna
1047
00:57:40,391 --> 00:57:43,559
go for CompressedSize, okay, and it's going to give me
1048
00:57:43,561 --> 00:57:46,962
a size that fits as small as possible using Auto Layout.
1049
00:57:46,964 --> 00:57:48,931
Okay, so this is a really cool little method,
1050
00:57:48,933 --> 00:57:52,534
it's how you get Auto Layout to calculate a size for you.
1051
00:57:52,536 --> 00:57:55,637
All right, so let's see, now that the size is right,
1052
00:57:55,639 --> 00:57:58,974
let's go see if that's going to fix things. I don't think
1053
00:57:58,976 --> 00:58:02,544
it's gonna be quite right, but it's gonna be close.
1054
00:58:07,485 --> 00:58:11,854
All right, here we go, drop a pin. We're gonna click on it,
1055
00:58:11,856 --> 00:58:16,658
oops, I clicked on the side of it. Okay, and edit it. Okay,
1056
00:58:16,660 --> 00:58:19,828
now, this is really close. Okay, it's really nice.
1057
00:58:19,830 --> 00:58:22,364
See how it's making it just the right size?
1058
00:58:22,366 --> 00:58:25,868
The problem is, the text editing part is pretty small.
1059
00:58:25,870 --> 00:58:29,972
Because look what it did, it sized it to the word Dropped.
1060
00:58:29,974 --> 00:58:33,175
Okay, and why did it do that? Because in our Auto Layout,
1061
00:58:33,177 --> 00:58:36,111
we didn't tell anything else about what size it should be.
1062
00:58:36,113 --> 00:58:37,346
So it put the contents in there, and
1063
00:58:37,348 --> 00:58:39,882
it made it as small as possible and fit the contents.
1064
00:58:39,884 --> 00:58:43,886
So we actually wanna go back to our storyboard and put
1065
00:58:43,888 --> 00:58:47,289
a minimum, reasonable minimum size for those text fields so
1066
00:58:47,291 --> 00:58:50,459
that they will give the person room to type something in.
1067
00:58:50,461 --> 00:58:53,729
Okay? The other thing is, the background is white.
1068
00:58:53,731 --> 00:58:56,698
This may be hard for you to see on that screen. But if you
1069
00:58:56,700 --> 00:59:00,769
look where the little arrow is pointing next to the word
1070
00:59:00,771 --> 00:59:04,540
description there, that's actually semitransparent.
1071
00:59:04,542 --> 00:59:06,041
And then also where the name and
1072
00:59:06,043 --> 00:59:09,077
description is is a white box. Okay, so it's actually
1073
00:59:09,079 --> 00:59:12,781
possible to have the whole thing be this semitransparent,
1074
00:59:12,783 --> 00:59:15,284
so that we can kind of see that we're on top of a map.
1075
00:59:15,286 --> 00:59:19,221
It won't feel so kind of obnoxious, this white box. And
1076
00:59:19,223 --> 00:59:21,657
the way to do that is just to make this MVC have
1077
00:59:21,659 --> 00:59:25,894
a background of clear. Okay, then it's just going to appear
1078
00:59:25,896 --> 00:59:28,730
clear on top of the popover's background,
1079
00:59:28,732 --> 00:59:30,399
which is semitransparent.
1080
00:59:30,401 --> 00:59:32,034
Okay, so we're gonna fix both those things at once.
1081
00:59:32,036 --> 00:59:35,370
So we're going back to our storyboard here. Okay, so
1082
00:59:35,372 --> 00:59:38,674
we're gonna make it so that these text fields right here,
1083
00:59:38,676 --> 00:59:41,343
okay, are a reasonable minimal, minimum size.
1084
00:59:41,345 --> 00:59:43,645
So what would be a reasonable minimum size?
1085
00:59:43,647 --> 00:59:48,483
Let's go here to our width here and height. And so
1086
00:59:48,485 --> 00:59:52,354
we could say a width of maybe, I don't know, 250 points?
1087
00:59:52,356 --> 00:59:54,289
This is something that you could play with in your UI.
1088
00:59:54,291 --> 00:59:57,025
I really only need to set one of these, because these
1089
00:59:57,027 --> 00:59:59,194
are already gonna be the same width. And so
1090
00:59:59,196 --> 01:00:02,130
that's gonna force these to be the same width. But actually,
1091
01:00:02,132 --> 01:00:06,835
I don't want it to be exactly 250. I want it to be at least
1092
01:00:06,837 --> 01:00:11,106
250, okay. Because if the name is really long, and
1093
01:00:11,108 --> 01:00:14,176
it'll fit without going off the edge of the screen, sure,
1094
01:00:14,178 --> 01:00:18,714
you can do it. So I add the constraint here to be 250.
1095
01:00:18,716 --> 01:00:21,149
And you can see that it makes it exactly 250, but
1096
01:00:21,151 --> 01:00:23,785
I don't want that. So I'm gonna go over here to my
1097
01:00:23,787 --> 01:00:27,889
dimensions inspector and look at this width 250. I'm gonna
1098
01:00:27,891 --> 01:00:32,427
edit it and change it to greater than or equal to 250,
1099
01:00:32,429 --> 01:00:38,266
okay? So understand that Auto Layout is pretty configurable.
1100
01:00:38,268 --> 01:00:40,836
You don't have to make things always be exactly certain
1101
01:00:40,838 --> 01:00:47,109
sizes, okay? So we'll go ahead and adjust our frames here.
1102
01:00:47,111 --> 01:00:52,147
Okay, got that? Okay, now what about the clear? That's really
1103
01:00:52,149 --> 01:00:54,616
simple. I'm just gonna click the background view here,
1104
01:00:54,618 --> 01:00:56,451
and instead of having it be white, okay,
1105
01:00:56,453 --> 01:00:58,987
this background view, I'm gonna have it be the default,
1106
01:00:58,989 --> 01:01:04,192
which is clear. Okay? So let's run it again.
1107
01:01:13,671 --> 01:01:14,870
Okay, yes, so that's working.
1108
01:01:14,872 --> 01:01:16,938
All right, so let's drop a pin. Okay.
1109
01:01:16,940 --> 01:01:22,310
We're gonna edit it, here we go. Boom. Perfect. Okay,
1110
01:01:22,312 --> 01:01:24,680
give us a little extra space to work with.
1111
01:01:24,682 --> 01:01:25,614
We can see, can you see that?
1112
01:01:25,616 --> 01:01:26,648
Yeah, you can see it's kind of a little bit see through.
1113
01:01:26,650 --> 01:01:30,318
You can see it's a little bit green behind that, looks a,
1114
01:01:30,320 --> 01:01:34,189
a lot nicer. Okay so, excellent.
1115
01:01:34,191 --> 01:01:37,659
Wow, popovers are great. I'm sure this didn't do any damage
1116
01:01:37,661 --> 01:01:40,896
on the iPhone side. Let's go take a look, okay?
1117
01:01:40,898 --> 01:01:47,436
iPhone. Sure the iPhone looks just great.
1118
01:01:50,307 --> 01:01:53,709
All right, here it is. We'll drop a pin.
1119
01:01:53,711 --> 01:01:56,912
All right, there it is, all right, here we go. Oops.
1120
01:01:56,914 --> 01:02:00,215
Ugh. Okay, well, clearly we totally broke, and
1121
01:02:00,217 --> 01:02:03,118
understandably, broke our iPhone side of this.
1122
01:02:03,120 --> 01:02:05,787
So what's wrong here, what's going on? Well, first of all,
1123
01:02:05,789 --> 01:02:07,456
we took it out of the navigation controller, so
1124
01:02:07,458 --> 01:02:09,925
we lost our done button, okay? So we're done, okay?
1125
01:02:09,927 --> 01:02:13,261
[LAUGH] Second one is, we made the background clear, so
1126
01:02:13,263 --> 01:02:17,065
it's looking into the void. Because, we know that when
1127
01:02:17,067 --> 01:02:20,502
it adapts, okay, first of all, why is it coming up this way?
1128
01:02:20,504 --> 01:02:23,338
Well, the system knows this is horizontally compact, so
1129
01:02:23,340 --> 01:02:26,074
it adapted from popover to full screen modal, okay.
1130
01:02:26,076 --> 01:02:30,245
Full screen modal completely covers
1131
01:02:30,247 --> 01:02:32,514
the thing underneath it, doesn't show anything,
1132
01:02:32,516 --> 01:02:34,616
doesn't show through, it completely covers it.
1133
01:02:34,618 --> 01:02:38,420
And it covers it with the void, the great void, okay.
1134
01:02:38,422 --> 01:02:41,656
And since we're looking clear into the void, it's black,
1135
01:02:41,658 --> 01:02:44,126
okay, the void happens to be black. All right, so that's
1136
01:02:44,128 --> 01:02:47,028
why this looks so terrible. So what are we gonna do about
1137
01:02:47,030 --> 01:02:51,066
this, okay? There's a number of things that we can do
1138
01:02:51,068 --> 01:02:55,704
to make this a lot better. So, actually, before we do this,
1139
01:02:55,706 --> 01:02:58,907
let's do one other quick thing. I wanna show you one
1140
01:02:58,909 --> 01:03:01,910
other quick thing. Let's go back over to our,
1141
01:03:01,912 --> 01:03:07,215
look at our iPad here, okay? We're back on the iPad there.
1142
01:03:07,217 --> 01:03:11,319
Okay, so when I change this, let's say I change something
1143
01:03:11,321 --> 01:03:15,857
here, like, you know, let's change it to, I'm not going,
1144
01:03:15,859 --> 01:03:19,194
okay, I'm not going, and I hit return. And now,
1145
01:03:19,196 --> 01:03:22,397
how do we dismiss a popover? We just hit somewhere else,
1146
01:03:22,399 --> 01:03:26,535
right? So here we go, tap. Look at that. It didn't
1147
01:03:26,537 --> 01:03:29,838
show it to us. Remember, when we had it on the iPhone,
1148
01:03:29,840 --> 01:03:33,008
when I clicked Done, it showed it. Now it's not showing.
1149
01:03:33,010 --> 01:03:36,511
Now, if I click on it, okay, it worked, that's great. But
1150
01:03:36,513 --> 01:03:39,815
no, we want it to show just like it did on the iPhone.
1151
01:03:39,817 --> 01:03:44,085
So how are we gonna fix that? That's pretty easy to fix.
1152
01:03:44,087 --> 01:03:47,455
We're gonna use, over here in our GPX controller,
1153
01:03:47,457 --> 01:03:50,625
this same popover presentation controller.
1154
01:03:50,627 --> 01:03:53,595
We're going to implement one of its delegate methods.
1155
01:03:53,597 --> 01:03:55,931
Okay, so we're going to set ourself as the delegate
1156
01:03:55,933 --> 01:03:58,767
of this pop-over presentation controller, which means,
1157
01:03:58,769 --> 01:04:02,370
of course, we have to go up here. And say that we
1158
01:04:02,372 --> 01:04:07,475
are a UIPopoverPresentationControll-
1159
01:04:07,477 --> 01:04:11,880
erDeligate, okay? And that, all right. So we're
1160
01:04:11,882 --> 01:04:14,216
PopoverPresentationController- Deligate. Which
1161
01:04:14,218 --> 01:04:16,685
PopoverPresentationController- Deligate method are we gonna
1162
01:04:16,687 --> 01:04:20,522
implement here? We're gonna implement the one where we
1163
01:04:20,524 --> 01:04:26,595
find out that it dismissed, dismiss pop over.
1164
01:04:26,597 --> 01:04:28,096
Okay, so this message gets sent to
1165
01:04:28,098 --> 01:04:29,898
the pop over presentation controller's delegate,
1166
01:04:29,900 --> 01:04:31,933
when the pop over is dismissed. A perfect time for
1167
01:04:31,935 --> 01:04:35,370
us to reselect that waypoint. Okay, we have that method
1168
01:04:35,372 --> 01:04:37,539
still, stay select waypoint, so let's go ahead and
1169
01:04:37,541 --> 01:04:39,774
do it. How do we get it this time? Well, we're going to
1170
01:04:39,776 --> 01:04:42,777
get this from the pop over presentation controller. Okay?
1171
01:04:42,779 --> 01:04:46,648
We're gonna get it's presented ViewController, okay?
1172
01:04:46,650 --> 01:04:48,917
That's the controller its presenting which is our
1173
01:04:48,919 --> 01:04:52,220
editable view, waypoint view controller. And it better be
1174
01:04:52,222 --> 01:04:56,291
an EditWaypointViewController, okay? And if it is,
1175
01:04:56,293 --> 01:05:01,363
then we can just get the waypoint to edit, okay? So
1176
01:05:01,365 --> 01:05:07,736
now, we're on this. Okay. So we'll drop,
1177
01:05:07,871 --> 01:05:15,043
click, go up here, change this to something, random words.
1178
01:05:15,879 --> 01:05:20,048
The only thing is tip away, and we got it. Okay.
1179
01:05:20,050 --> 01:05:22,317
So that's a great use of that little delegate. But
1180
01:05:22,319 --> 01:05:25,620
there are even better uses of that little delegate.
1181
01:05:25,622 --> 01:05:28,256
And a lot of them have to do with this adaptation
1182
01:05:28,258 --> 01:05:31,526
behavior, okay? The way that the system is adopting to that
1183
01:05:31,528 --> 01:05:33,962
horizontally compact environment and
1184
01:05:33,964 --> 01:05:38,199
putting that thing up on our iPhone
1185
01:05:38,201 --> 01:05:42,037
in this really obnoxious way. Okay so let's go and
1186
01:05:42,039 --> 01:05:45,707
fix some of these things. One thing we could do to fix this
1187
01:05:45,709 --> 01:05:50,478
is we could make it not adapt. Okay? We can make it just so
1188
01:05:50,480 --> 01:05:53,081
that it uses popover on iPhone. It doesn't do this
1189
01:05:53,083 --> 01:05:55,850
whole putting a modal one up instead. Okay so
1190
01:05:55,852 --> 01:05:59,321
how would we do that? All right, that one we're gonna
1191
01:05:59,323 --> 01:06:01,790
implement a different delegate method here.
1192
01:06:01,792 --> 01:06:06,461
Popover delegate. This one is called the,
1193
01:06:06,463 --> 01:06:11,066
note here, adaptivePresentationStyleforP-
1194
01:06:11,068 --> 01:06:15,870
resentationController. Okay, this guy right here. Okay,
1195
01:06:15,872 --> 01:06:19,040
adapt to presentation style for presentation controller,
1196
01:06:19,042 --> 01:06:20,208
actually we want a different one,
1197
01:06:20,210 --> 01:06:23,645
we want the one that gives us the trade collection.
1198
01:06:24,781 --> 01:06:29,951
AdaptivePresentationStyle, it's this one. Okay?
1199
01:06:29,953 --> 01:06:32,954
So this one is basically asking us, let me make it so
1200
01:06:32,956 --> 01:06:36,324
you can see it a little better here. Okay?
1201
01:06:36,326 --> 01:06:39,494
It has two arguments here and it returns
1202
01:06:39,496 --> 01:06:42,230
a ModalPresentationStyle. So this is saying, for
1203
01:06:42,232 --> 01:06:45,533
a given trait collection like horizontally compact or
1204
01:06:45,535 --> 01:06:49,404
whatever how do you want to present this
1205
01:06:49,406 --> 01:06:52,607
thing in when you're adapting. Now if I don't implement this,
1206
01:06:52,609 --> 01:06:55,276
what it does is, it looks at this tray collection, says,
1207
01:06:55,278 --> 01:06:59,748
is it horizontally compact? Yes? Then return full screen.
1208
01:06:59,750 --> 01:07:01,916
That's how this whole adaptation thing works.
1209
01:07:01,918 --> 01:07:05,387
Well, I can fix that by just saying return none. Okay,
1210
01:07:05,389 --> 01:07:10,191
that means don't adapt, okay? So now we will never adapt,
1211
01:07:10,193 --> 01:07:12,327
now if I run on the iPhone,
1212
01:07:12,329 --> 01:07:16,898
you'll see that it's not going to adapt because I set
1213
01:07:16,900 --> 01:07:20,702
the presentation style for, adapting style, to be none so
1214
01:07:20,704 --> 01:07:24,406
it's never going to adapt. Go here, you click this.
1215
01:07:24,408 --> 01:07:27,809
Sure enough we get that. Now look how terrible this looks.
1216
01:07:27,811 --> 01:07:31,346
Okay? Clearly horizontally compact is not wide enough to
1217
01:07:31,348 --> 01:07:34,849
show this popover. Okay? It's, smashed it in there,
1218
01:07:34,851 --> 01:07:37,986
tried to obey the 250 point wide for the text field.
1219
01:07:37,988 --> 01:07:40,855
The name and description got cut off. It just not you
1220
01:07:40,857 --> 01:07:44,025
know couldn't quite point to the [LAUGH] the pin prob
1221
01:07:44,027 --> 01:07:46,661
is a big mess. So this is not the solution to this problem.
1222
01:07:46,663 --> 01:07:48,797
It seemed like it would be a good solution to this problem.
1223
01:07:48,799 --> 01:07:52,834
But it's not. So, lets go try and solve some of those other
1224
01:07:52,836 --> 01:07:57,405
problems we had. Okay, one is, we had no done button, okay?
1225
01:07:57,407 --> 01:07:59,074
So how can we get the done button back? Well,
1226
01:07:59,076 --> 01:08:01,876
we need to put that thing back in a navigation controller,
1227
01:08:01,878 --> 01:08:06,614
but only when we're adapting. So, first of all,
1228
01:08:06,616 --> 01:08:07,749
I'm going to comment this out so
1229
01:08:07,751 --> 01:08:10,418
that we're back to adapting. And I'm gonna input another
1230
01:08:10,420 --> 01:08:15,423
delegate method here which is called view controller for
1231
01:08:15,425 --> 01:08:20,462
adaptive presentation style. Here it is. Okay,
1232
01:08:20,464 --> 01:08:26,801
again I'll show you the arguments here. Okay, now look
1233
01:08:26,803 --> 01:08:30,505
what this one returns. This one returns a view controller.
1234
01:08:30,507 --> 01:08:33,274
This is basically saying, okay, I'm trying to adapt
1235
01:08:33,276 --> 01:08:36,144
to a certain style, that's gonna be the full screen style
1236
01:08:36,146 --> 01:08:38,880
here give me a View Controller to show
1237
01:08:38,882 --> 01:08:41,916
when I adapt. Now if I don't implement this, it's just
1238
01:08:41,918 --> 01:08:44,452
going to use the edit waypoint view controller, okay.
1239
01:08:44,454 --> 01:08:47,422
But I'm going to fix this to return a navigational control
1240
01:08:47,424 --> 01:08:50,325
that has an edit waypoint controller in it. Okay. So
1241
01:08:50,327 --> 01:08:53,194
let's do that. I'm going to say, first I'm gonna let,
1242
01:08:53,196 --> 01:08:56,831
well first I'm gonna say, if the style that we're adapting
1243
01:08:56,833 --> 01:09:01,569
to is full screen. Okay, then I'm going to create
1244
01:09:01,571 --> 01:09:05,907
a navigation controller, let navcon equal a UINavigation.
1245
01:09:05,909 --> 01:09:09,144
I'm just creating it with its constructor, right,
1246
01:09:09,146 --> 01:09:12,614
its initializer. And the rootViewController of it is
1247
01:09:12,616 --> 01:09:19,187
going to be the controllers. PresentedViewController,
1248
01:09:19,189 --> 01:09:21,723
remember we used that in the other one, okay,
1249
01:09:21,725 --> 01:09:22,423
that is the EditWaypoint,
1250
01:09:22,425 --> 01:09:25,293
EditWaypointViewController that we're trying to present,
1251
01:09:25,295 --> 01:09:28,129
that's what it is. And so now I'm just gonna return this
1252
01:09:28,131 --> 01:09:31,266
navigation controller instead. So it's gonna use
1253
01:09:31,268 --> 01:09:34,369
the navigation controller instead of this. If we're not
1254
01:09:34,371 --> 01:09:36,671
adapting to full screen then I'm gonna return nil and
1255
01:09:36,673 --> 01:09:39,507
let it do whatever it does by default which is you know
1256
01:09:39,509 --> 01:09:43,144
use the popover or it's not adapting so it doesn't matter.
1257
01:09:43,146 --> 01:09:46,214
All right let's see if that fixes anything here.
1258
01:09:51,388 --> 01:09:54,656
All right here we go. Drop it. Click on it,
1259
01:09:54,658 --> 01:09:58,459
boom. Wahoo! Look at that we have a done button back.
1260
01:09:58,461 --> 01:10:00,528
Now that done button, by the way, you might say,
1261
01:10:00,530 --> 01:10:00,762
where'd that come from?
1262
01:10:00,764 --> 01:10:03,598
Okay, well that's because we put the done button into that
1263
01:10:03,600 --> 01:10:06,167
NBC back when it used to be a navigation controller.
1264
01:10:06,169 --> 01:10:09,003
When we removed the navigation controller it kept the done
1265
01:10:09,005 --> 01:10:11,472
button. It wasn't able to display it because it wasn't
1266
01:10:11,474 --> 01:10:14,475
in a navigation controller until now. Again, but it kept
1267
01:10:14,477 --> 01:10:17,412
the Done button. Remember that all the things that appear
1268
01:10:17,414 --> 01:10:19,647
in a navigation controllers title bar, the buttons and
1269
01:10:19,649 --> 01:10:23,384
stuff are associated with the NBC inside, okay. And
1270
01:10:23,386 --> 01:10:25,920
when you remove, put something in, embed in, or
1271
01:10:25,922 --> 01:10:28,056
take out of a navigational controller in NBC,
1272
01:10:28,058 --> 01:10:30,892
it keeps all it's things so that's why we kept the Done
1273
01:10:30,894 --> 01:10:33,228
button. Okay, not only kept the Done button there,
1274
01:10:33,230 --> 01:10:34,829
it's still going to unwind. Okay?
1275
01:10:34,831 --> 01:10:37,565
Cuz it's still hooked up to that to unwind there. Now,
1276
01:10:37,567 --> 01:10:40,702
we still have this black background. That's not so
1277
01:10:40,704 --> 01:10:43,137
good. Okay? How are we gonna fix that? Well,
1278
01:10:43,139 --> 01:10:46,174
I'm gonna do that in a cool way, which is to stop
1279
01:10:46,176 --> 01:10:51,079
having it basically cover up the the thing underneath, and
1280
01:10:51,081 --> 01:10:52,747
let it show through. Okay?
1281
01:10:52,749 --> 01:10:56,451
I'm gonna have this modal view controller come up. But
1282
01:10:56,453 --> 01:10:59,220
not cover up what's behind, okay, and
1283
01:10:59,222 --> 01:11:01,756
that's really simple to do. Actually,
1284
01:11:01,758 --> 01:11:04,959
we're going to use the same method we did back here, okay,
1285
01:11:04,961 --> 01:11:07,929
but instead of saying return none, okay,
1286
01:11:07,931 --> 01:11:12,700
we're just going to say let's just return. If the trace
1287
01:11:12,702 --> 01:11:18,139
collection here collection, okay trait selection,
1288
01:11:18,141 --> 01:11:22,644
horizontal size class, equals compact, then we're gonna
1289
01:11:22,646 --> 01:11:26,881
return this new style which is over full screen.
1290
01:11:26,883 --> 01:11:28,549
So over full screen is like full screen but
1291
01:11:28,551 --> 01:11:31,919
it's over the things so the things shines through. Okay
1292
01:11:31,921 --> 01:11:33,588
that's the only difference between full screen and
1293
01:11:33,590 --> 01:11:38,126
over full screen. Otherwise, we're just going to not adapt.
1294
01:11:38,128 --> 01:11:40,762
Okay, we're only gonna adapt in the horizontalSizeClass
1295
01:11:40,764 --> 01:11:45,466
Compact. Okay, so let's see what that looks like.
1296
01:11:51,074 --> 01:11:56,044
All right, here it is. Drop one, click, bring it up.
1297
01:11:56,046 --> 01:12:00,114
All right! Okay, it brought it up. But, it's kinda weird that
1298
01:12:00,116 --> 01:12:02,950
we can see all the way through it kinda makes it think that
1299
01:12:02,952 --> 01:12:06,521
we could click on this things. Also having the word
1300
01:12:06,523 --> 01:12:09,123
description be on the dark background makes it hard to
1301
01:12:09,125 --> 01:12:13,895
see it. Okay. I also noticed that maybe our 250 is a little
1302
01:12:13,897 --> 01:12:17,598
too wide of a minimum. Maybe we wanna go down to 200.
1303
01:12:17,600 --> 01:12:19,734
But, what were really cool is if we could actually show
1304
01:12:19,736 --> 01:12:23,137
what's behind slightly blurred. Just like the popover
1305
01:12:23,139 --> 01:12:26,641
one does. Right? The popover one it showed the name and
1306
01:12:26,643 --> 01:12:29,844
description, but it was only kinda the slightly see-through
1307
01:12:29,846 --> 01:12:31,612
semi-transparent blurred thing.
1308
01:12:31,614 --> 01:12:34,449
Well we can do that ourselves. Okay? There's a view, okay,
1309
01:12:34,451 --> 01:12:37,418
we can talk about in the slides. But there's a view
1310
01:12:37,420 --> 01:12:42,457
which essentially presents whatever's behind it blurred
1311
01:12:42,459 --> 01:12:45,626
okay, tha's what the view does. so were gonna poke one
1312
01:12:45,628 --> 01:12:49,764
of this views right at the top of the navigation controllers
1313
01:12:49,766 --> 01:12:52,533
view hierarchy okay. Even behind the things
1314
01:12:52,535 --> 01:12:55,036
like this bar at the top of the navigation control
1315
01:12:55,038 --> 01:12:57,705
everything. Okay, we're gonna be really right at the top, so
1316
01:12:57,707 --> 01:12:58,773
it's gonna blur everything behind. And
1317
01:12:58,775 --> 01:13:01,109
the navigation controller's gonna be on top of this nice,
1318
01:13:01,111 --> 01:13:04,746
blurred thing. Okay? So, how do we do that? Well,
1319
01:13:04,748 --> 01:13:07,281
we create that navigation controller right here. So
1320
01:13:07,283 --> 01:13:10,218
all we need to do is to create this little blurring view
1321
01:13:10,220 --> 01:13:15,890
doohickey, which is called a visual effects view. And
1322
01:13:15,892 --> 01:13:19,594
also, by the way, we're gonna wanna put here if the style
1323
01:13:19,596 --> 01:13:24,365
is OverFullScreen so that we can do it in both cases there.
1324
01:13:24,768 --> 01:13:27,068
So, we're gonna create a visual effects view,
1325
01:13:27,070 --> 01:13:30,938
visualEffectsView, we'll call it and
1326
01:13:30,940 --> 01:13:31,973
it's UIVisualEffectView.
1327
01:13:31,975 --> 01:13:38,246
That's the name of the class. VisualEffectView. I guess
1328
01:13:38,248 --> 01:13:42,083
it would be VisualEffectView cuz it's a single effect. And
1329
01:13:42,085 --> 01:13:46,587
you just specify what kind of effect you want. Effect, and
1330
01:13:46,589 --> 01:13:51,392
I want a UIBlurEffect. And the style of
1331
01:13:51,394 --> 01:13:54,695
it is I want a ExtraLight okay which is a really
1332
01:13:54,697 --> 01:13:57,965
extra light ver, blur. And the reason I want extra light is
1333
01:13:57,967 --> 01:13:59,801
cuz I'm gonna be putting black text on top of it and
1334
01:13:59,803 --> 01:14:02,670
things like that so I really want that to stand out. So
1335
01:14:02,672 --> 01:14:05,139
there's extra light blurring.
1336
01:14:05,141 --> 01:14:10,445
Now I'm going to set the visual effects views frame
1337
01:14:11,247 --> 01:14:14,482
to equal the navCon bounds. Okay.
1338
01:14:14,484 --> 01:14:16,150
So navCon is a view controller. It's just
1339
01:14:16,152 --> 01:14:17,919
a regular view controller like any other view controller.
1340
01:14:17,921 --> 01:14:20,621
It's got a view which is it's top level view. So I'm gonna
1341
01:14:20,623 --> 01:14:23,991
put my visual effects right at the top of its hierarchy, so
1342
01:14:23,993 --> 01:14:25,893
I'm gonna have it fill the entire thing. Now,
1343
01:14:25,895 --> 01:14:28,296
I'm also gonna do a little bit of magic here which we didn't
1344
01:14:28,298 --> 01:14:30,164
talk about at all, which is, there's
1345
01:14:30,166 --> 01:14:33,734
a really kind of an old style way of doing auto layout where
1346
01:14:33,736 --> 01:14:36,604
you can specify just a couple of things.
1347
01:14:36,606 --> 01:14:40,608
And, I'm going to have this visual effect view
1348
01:14:40,610 --> 01:14:44,445
always stick to the same size as the nab cons bounds.
1349
01:14:44,447 --> 01:14:47,315
And the way I'm gonna do it, is by using these old style
1350
01:14:47,317 --> 01:14:51,018
constraints where I basically say that the width and height
1351
01:14:51,020 --> 01:14:55,122
of my visual effects view is flexible. So, as things change
1352
01:14:55,124 --> 01:14:57,859
it tries to stick to its super view. Okay? Don't worry
1353
01:14:57,861 --> 01:15:00,628
too much about this magic It's just a little easier way than
1354
01:15:00,630 --> 01:15:03,965
doing this than trying to show you how to do it with
1355
01:15:03,967 --> 01:15:05,166
the new way.
1356
01:15:05,168 --> 01:15:08,870
So I just basically say that's auto resizing math is flexible
1357
01:15:08,872 --> 01:15:09,203
width and flexible height.
1358
01:15:09,205 --> 01:15:13,374
If it's superview changes it's size than it sizes with it,
1359
01:15:13,376 --> 01:15:15,443
using the rules keep your width and
1360
01:15:15,445 --> 01:15:16,544
height flexible. Okay?
1361
01:15:16,546 --> 01:15:20,815
So now I'm just going to take that navcon top level view and
1362
01:15:20,817 --> 01:15:23,551
insert a subview, which is a visual effects view.
1363
01:15:23,553 --> 01:15:28,189
And I'm gonna put it at index zero, which is all the way at
1364
01:15:28,191 --> 01:15:31,359
the back. Remember subviews is ordered int he subview list.
1365
01:15:31,361 --> 01:15:34,762
All right, so let's take a look at that.
1366
01:15:39,936 --> 01:15:45,473
All right. Drop it here. Up. There we go.
1367
01:15:45,475 --> 01:15:48,209
Okay, see how it, you can kinda see the,
1368
01:15:48,211 --> 01:15:51,012
I don't know how, how good the projection there is, but
1369
01:15:51,014 --> 01:15:52,513
you can kinda almost see the valleys here.
1370
01:15:52,515 --> 01:15:55,783
The mountains and the valleys. In there. When you get this
1371
01:15:55,785 --> 01:16:01,455
we'll go run it at home you'll see it a lot clearer. Okay? So
1372
01:16:01,457 --> 01:16:03,024
that's it we only have three minutes so
1373
01:16:03,026 --> 01:16:08,963
I'm not going to the slides on that. I will post on
1374
01:16:08,965 --> 01:16:12,533
how you can learn a little more about that. And
1375
01:16:12,535 --> 01:16:14,435
that's it. So, that's everything there.
1376
01:16:14,437 --> 01:16:15,903
You learned a lot about map view and
1377
01:16:15,905 --> 01:16:17,572
about all kinds of segways there, so
1378
01:16:17,574 --> 01:16:18,773
hopefully that will prepare you for
1379
01:16:18,775 --> 01:16:20,708
your final presentation. So,
1380
01:16:20,710 --> 01:16:22,343
next week is just the alternate final,
1381
01:16:22,345 --> 01:16:24,445
there's a holiday on Monday, so there's no class then,
1382
01:16:24,447 --> 01:16:26,747
and Wednesday is the alternate final. And,
1383
01:16:26,749 --> 01:16:30,718
if you have any questions I'll help you here as usual. For
1384
01:16:30,720 --> 01:16:30,751
more, please visit us at stanford.edu.
================================================
FILE: subtitles/2. Applying MVC.srt
================================================
1
00:00:00,001 --> 00:00:03,402
[MUSIC]
2
00:00:03,404 --> 00:00:07,739
Stanford University. >> Okay, well, welcome
斯坦福大学. >> 欢迎
3
00:00:07,741 --> 00:00:12,276
to Stanford CS193P. This is spring quarter of 2016, and
参加斯坦福大学的 CS193P 课程.这是2016年的春季教程的
4
00:00:12,278 --> 00:00:16,013
this is lecture number 2. And today, we are going to talk
第二个课时.今天我们将要聊聊
5
00:00:16,015 --> 00:00:19,950
about MVC, okay, I'm gonna try and really briefly cover that
关于 MVC 的知识.接下来我将尝试简短的描述一下 MVC.
6
00:00:19,952 --> 00:00:23,553
because I know only about half of you know what MVC is, and
因为我知道你们中大概只有一半知道MVC 是什么.
7
00:00:23,555 --> 00:00:27,257
it's a very important part of doing any iOS development.
MVC 是 iOS 开发过程中非常重要的一部分.
8
00:00:27,259 --> 00:00:30,126
And then after I'm done with that, I'm gonna continue
我将在上次代码的基础上完成计算器的功能
9
00:00:30,128 --> 00:00:32,795
the demo from last time, we'll use MVC and learn yet
我们将使用 MVC 以及之前学过的
10
00:00:32,797 --> 00:00:36,565
some more things about Swift and Objective C, all right?
Swift 和 Objective C 的知识,准备好了吗?
11
00:00:36,567 --> 00:00:40,535
All right, so MVC, what is it? As I mentioned last time, it's
那么什么是 MVC 呢?就像我上次提到过的,
12
00:00:40,537 --> 00:00:44,172
essentially a way of dividing up your application or
从本质上来说它是将你应用或代码
13
00:00:44,174 --> 00:00:47,975
your source code into three different camps,
分割成三个不同的"阵营".
14
00:00:47,977 --> 00:00:52,980
okay? The three camps pictured here are the model camp.
就如图片所展示的.而左下角就是模型的"阵营".
15
00:00:52,982 --> 00:00:57,484
The model camp is what your application does. Okay,
所谓模型的"阵营"指的是你应用是做什么的.
16
00:00:57,486 --> 00:00:58,952
nothing about how it's drawn on screen or
而跟你界面上如何绘制以及展示
17
00:00:58,954 --> 00:01:01,221
anything like that, okay. It's not how it's displayed,
什么毫无关系.不是关于它如何展示
18
00:01:01,223 --> 00:01:03,856
it's just what it is. So for a calculator app,
而是关于应用是什么.那么对于一个计算器的应用来说
19
00:01:03,858 --> 00:01:06,926
what it is it's a calculator, so the model is probably gonna
Model "阵营"就是计算器,也就是说 Model 将会被
20
00:01:06,928 --> 00:01:08,827
be the part that does calculating,
划分为一些计算的部分
21
00:01:08,829 --> 00:01:11,930
okay. Next piece is the controller.
那么接下来我们说说 Controller.
22
00:01:11,932 --> 00:01:17,369
The controller is how your model is displayed on screen.
Controller 就是控制你的模型如何在界面上进行展示的工具
23
00:01:17,371 --> 00:01:20,138
Okay, it's kind of the how. This is basically all your
关键点就在于如何进行展示.它主要是包含
24
00:01:20,140 --> 00:01:23,708
UI logic, goes into your controller, all right? And
你的界面逻辑以及界面的跳转.
25
00:01:23,710 --> 00:01:27,778
the view, you can think of as your controller's minions,
View 你可以把它当做是 Controller 的仆从
26
00:01:27,780 --> 00:01:30,547
okay, the things that the controller's gonna use to put
那些 Controller 想要展示
27
00:01:30,549 --> 00:01:33,283
things on screen. So that's buttons and labels and
在屏幕上的东西. 所以那些按钮,标签,
28
00:01:33,285 --> 00:01:36,419
tables and all those kinda things that the controller
列表以及一些 Controller 希望在界面上展示的控件的控件
29
00:01:36,421 --> 00:01:38,688
needs to display what's in the model and
用于展示 Model 的信息,
30
00:01:38,690 --> 00:01:41,924
to get input from the user to update the model as well,
用户的输入和更新 Model 的内容
31
00:01:41,926 --> 00:01:44,126
okay? So those are the three camps.
这就是三个"阵营"的大体概念.
32
00:01:44,128 --> 00:01:46,628
Now it's one thing to decide where things go
接下来根据之前提到的"阵营"概念
33
00:01:46,630 --> 00:01:49,197
based on the description of the camp, but a really
来说说决定整套机制的运转的机制,
34
00:01:49,199 --> 00:01:52,333
important piece of it is the communication between camps,
三个"阵营"交互的关键部分,
35
00:01:52,335 --> 00:01:54,435
what's allowed, what's allowed, what's not.
哪些行为是允许的,哪些是不允许的.
36
00:01:54,437 --> 00:01:57,204
And when communication is allowed, how do you do it,
以及什么时候进行交互,如何在 iOS 中实现,
37
00:01:57,206 --> 00:02:00,607
okay, in iOS? How, how is that communication facilitated?
如何促进各"阵营"的交流.
38
00:02:00,609 --> 00:02:04,944
So, to help with this, I've kind of, drew, drawn here this
为了帮助理解,我在中心绘制了一个
39
00:02:04,946 --> 00:02:08,114
little Y in the middle. It's kinda like road signs, okay?
Y 字的图案.有点类似马路标志.
40
00:02:08,116 --> 00:02:10,916
It's like double yellow at the bottom there is don't cross.
在 'Y' 的下方有两条黄色的线条表示两边是不相通的
41
00:02:10,918 --> 00:02:13,752
And then solid white is yeah, you can cross, but
上方两条白色的线表示两边是可以相通的.
42
00:02:13,754 --> 00:02:17,088
you're not really supposed to generally do this without
你应该在编程时尽可能
43
00:02:17,090 --> 00:02:20,358
being very careful.
的小心这方面.
44
00:02:20,360 --> 00:02:22,193
the traffic is going in the same direction, so
三个"阵营"交互的方向总是固定的
45
00:02:22,195 --> 00:02:23,627
you can pretty much crossover.
所以你只要关注一下大体的"交互"方向.
46
00:02:23,629 --> 00:02:24,828
Probably wanna put your turn indicator on,
也许你想打破规定
47
00:02:24,830 --> 00:02:28,732
but off you go. Okay, so let's talk about how that works for
那你就那样做吧. 那么我们先来谈谈 MVC
48
00:02:28,734 --> 00:02:32,301
these three camps. First let's talk about controller talking
是如何工作的吧.首先我们先聊聊 Controller 和
49
00:02:32,303 --> 00:02:35,905
to the model. The controller can talk to the model all it
Model 之间的交互吧. Controller 能调用 Model 的方法.
50
00:02:35,907 --> 00:02:37,940
wants. It knows everything about the model.
它能知道 Model 的所有.
51
00:02:37,942 --> 00:02:39,708
It can send any message it wants to the model.
它能调用所有 Model 所有方法.
52
00:02:39,710 --> 00:02:42,477
The controller is in complete control of the model. Okay,
Controller 能完全控制 Model,
53
00:02:42,479 --> 00:02:44,679
and the controller needs that because the controller's
它也需要这样,因为 Controller 的任务就是
54
00:02:44,681 --> 00:02:48,149
job is to present what's in the model to the user or
展示给用户看 Model 的信息,或者
55
00:02:48,151 --> 00:02:49,950
to get information from the user and update the model.
从用户处获得信息以及更新 Model 的信息.
56
00:02:49,952 --> 00:02:52,352
So it needs full control, so that's a full green arrow,
所以 Controller 需要完全控制 Model ,因此图中有一个绿色箭头
57
00:02:52,354 --> 00:02:56,556
dashed white road sign, road line there can do anything at
以及白色虚线表示可以完全控制 Model.
58
00:02:56,558 --> 00:02:59,992
once. Same thing on the other side, the controller obviously
相同地,在另一边 Controller 明显地
59
00:02:59,994 --> 00:03:02,795
needs to be able to use its minions however it wants to
需要使用到它的"仆从"( View ),无论 Controller 希望如何展示
60
00:03:02,797 --> 00:03:06,265
display the model. And most of the time, the connection
Model 的信息.大部分情况下, Controller 和 View 之间的链接
61
00:03:06,267 --> 00:03:09,968
between the controller and its minions is via an outlet.
都是通过 Outlet 来实现的.
62
00:03:09,970 --> 00:03:13,104
And you remember we had an outlet on Monday, right?
所以你们还记得周一提到的 Outlet 吗?
63
00:03:13,106 --> 00:03:13,871
It was the display?
在之前的 Demo 中的展示.
64
00:03:13,873 --> 00:03:16,007
You remember that, it was a var instance variable.
你需要记得它是一个实例变量和
65
00:03:16,009 --> 00:03:20,477
Display with an optional UI label. And that connection is
展示中 UILabel 的链接.这种链接方式就是
66
00:03:20,479 --> 00:03:22,679
how the controller was talking to it's view.
Controller 和 View 之间的交互方式.
67
00:03:22,681 --> 00:03:25,715
That label, that UI label was part of it's view.
那个Label, UILabel 就是那个 View 中的一部分.
68
00:03:25,717 --> 00:03:28,317
It was a minion in it's view. So that's
就是那个仆从—— View .所以这里
69
00:03:28,319 --> 00:03:30,753
full green communication, kind of do whatever it wants,
对 Controller 来说能完全
70
00:03:30,755 --> 00:03:32,588
controller knows everything about both sides.
控制它的两边的 View 和 Model.
71
00:03:32,590 --> 00:03:36,191
It has to. Let's talk about the model in the view.
实际情况也要求这样设计. 接下来我们说说 Model 和 View 之间.
72
00:03:36,193 --> 00:03:40,795
Those never speak to each other. Why is that? Simple,
这二者之间不会产生交互,为什么呢?很简单,
73
00:03:40,797 --> 00:03:41,963
the model is UI independent,
Model 和界面是相互独立的.
74
00:03:41,965 --> 00:03:45,366
so there's absolutely nothing it has to say to the view,
因此 Model 和 View 之间完全不可能产生直接交互.
75
00:03:45,368 --> 00:03:46,567
which is completely UI dependent,
特别对于那些完全和界面独立的
76
00:03:46,569 --> 00:03:49,236
that's all the view is. The view is just the minions
Model 来说. View 只是 Controller
77
00:03:49,238 --> 00:03:52,639
of the controller. And so, you know, it makes no sense for
的仆从. 由此可知 View 和 Model 之间的交互
78
00:03:52,641 --> 00:03:55,408
these two to talk to each other. So that fire, that's
是完全没有意义的.这就是为什么
79
00:03:55,410 --> 00:03:58,911
double yellow line, don't ever do that in this class, okay?
这里采用两条黄线表示,别在具体实现中这样做.
80
00:03:58,913 --> 00:04:02,014
No communication there at all. Okay, all communication in
重申一次, Model 和 View 之间完全没有交互.所有的交互
81
00:04:02,016 --> 00:04:04,950
the model, in the view goes through the controller.
都是通过 Controller 进行传递的.
82
00:04:04,952 --> 00:04:07,419
All right, what about from the view to the controller?
那么关于从 View 到 Controller 有交互吗?
83
00:04:07,421 --> 00:04:10,254
Can the view, like a label and stuff like that,
那么 View (例如Label 等控件)能访问
84
00:04:10,256 --> 00:04:12,923
talk to its controller? Well, yes and
Controller 吗?答案是:既可以也不可以.
85
00:04:12,925 --> 00:04:15,893
no. The problem with the view is all the minions
问题在于所有的控件
86
00:04:15,895 --> 00:04:19,296
in there are generic objects like UIButton or UILabel.
都是一个通用的类型,例如UIButton 或UILabel.
87
00:04:19,298 --> 00:04:21,297
Those were written by Apple years ago.
这些都是由苹果官方在多年前进行封装的.
88
00:04:21,299 --> 00:04:23,199
They know absolutely nothing about a calculator.
对于这些控件来说,他们对计算器毫不知情.
89
00:04:23,201 --> 00:04:26,168
So there's way to kind of for them to talk to a calculator
因此一定存在一种方法让它们与计算器交互
90
00:04:26,170 --> 00:04:28,804
and know it's a calculator. Okay, so there's limited
以及知道它是一个计算器.因此对于 Controller 和 View 来说
91
00:04:28,806 --> 00:04:31,239
communication between the view and the controller. But
它们之间的交互是有限制的.但是从另一方面来说
92
00:04:31,241 --> 00:04:33,441
off course the view needs to talk to the controller because
View 是需要访问 Controller 的,因为他是 Controller 的"仆从"
93
00:04:33,443 --> 00:04:36,177
it's the controller's minions and things happen in the U.I.
并且也需要通知 Controller 在界面上的事件,
94
00:04:36,179 --> 00:04:38,279
and need to tell the controller what's going on so.
同时也需要告诉 Controller 具体在界面上发生了什么.
95
00:04:38,281 --> 00:04:42,516
The kind of communication we have there has to be blind and
类似这种的交互方式,我们必须做到适当屏蔽以及
96
00:04:42,518 --> 00:04:42,549
structured.
结构化.
97
00:04:42,551 --> 00:04:47,087
Blind meaning the objects in the view don't know what class
所谓的屏蔽是指 View 不需要知道
98
00:04:47,089 --> 00:04:47,720
they're talking to. 'Kay?
和什么对象进行交互.
99
00:04:47,722 --> 00:04:50,589
Cause view, buttons don't know anything about calculator view
因为对于 View (例如按钮)来说完全不知道他在跟一个计算器的 Controller 进行交互
100
00:04:50,591 --> 00:04:53,225
controllers. And it's structured because
而结构化的原因在于
101
00:04:53,227 --> 00:04:57,262
since there is no knowledge of the Objects on either end.
它对于要交互的对象一无所知.
102
00:04:57,264 --> 00:05:00,432
They have to communicate in a well-defined, pre-defined way,
它们之间是通过之先定好的方法进行交互,
103
00:05:00,434 --> 00:05:03,301
okay. So let's talk about some of those structured ways that
那么我们就来说说
104
00:05:03,303 --> 00:05:06,737
the view minions talk to the controller. One of them
View 通知 Controller 的方法.其中一种
105
00:05:06,739 --> 00:05:09,707
you learned last time is target action, okay. So
我们在上节课讲到过,就是采用 Target-Action 模式实现.
106
00:05:09,709 --> 00:05:12,843
target action's very simple, the controller hangs a target
这种方法非常简单,只需 Controller 将自己设置成 target
107
00:05:12,845 --> 00:05:17,714
on itself by defining a method with at sign ib action on it,
并且定义一个与 IBAction 的方法
108
00:05:17,716 --> 00:05:18,381
usually, in Xcode, so
通常在 Xcode 内,
109
00:05:18,383 --> 00:05:20,316
that little dot will work, okay. And
如果有一个小的实心远点,说明它就生效了.
110
00:05:20,318 --> 00:05:23,585
then the view when it wants to talk to the controller simply
当 View 希望通知 Controller 时,只需简单地
111
00:05:23,587 --> 00:05:26,388
calls that method and that connection. Okay.
调用该方法即可.
112
00:05:26,390 --> 00:05:29,257
The action being sent, from the view controller,
只需通过 Ctrl +左键拖拉
113
00:05:29,259 --> 00:05:31,926
is wired up usually with control drag. You saw us do
的方式建立对应的 Action.
114
00:05:31,928 --> 00:05:35,296
that. It can be done in code. But 99% of the time we control
虽然我们可以通过代码来实现,但是99%的情况下,我们通过
115
00:05:35,298 --> 00:05:37,531
drag to create this target action connection.
Ctrl +左键拖来来创建对应的 Target-Action 的链接.
116
00:05:37,533 --> 00:05:40,500
So there's an example. Very simple communication between.
最简单的例子就是
117
00:05:40,502 --> 00:05:44,771
Menu in the View like the UI button and the Controller,
菜单里的视图(如按钮)和 Controller 之间的链接就是 Target-Action 模式.
118
00:05:44,773 --> 00:05:49,208
the other method. Okay? Simple one. All right, what else,
非常简单的方法.那么
119
00:05:49,210 --> 00:05:50,209
what other kind of communication we had
除此之外,还有什么其他 View 通知 Controller 的方式吗?
120
00:05:50,211 --> 00:05:53,412
besides Target action? Well, sometimes the View needs to
当然,有些时候 View 需要
121
00:05:53,414 --> 00:05:56,348
communicate something a little more complicated than just
通知 Controller 一些复杂的事件
122
00:05:56,350 --> 00:05:59,918
I was touched or something like that. Okay. For example,
如点击的详情等.
123
00:05:59,920 --> 00:06:03,554
it might be a scroll view, that's a generic view minion.
例如一个通用的滚动视图,
124
00:06:03,556 --> 00:06:05,956
And it might need to tell the controller,
有时候需要通知 Controller
125
00:06:05,958 --> 00:06:09,093
hey, this guy just started scrolling. Okay.
我(滚动视图)开始滚动的事件了.
126
00:06:09,095 --> 00:06:12,929
Or the person zoomed into this zoom scale. All right.
或者是一个人缩放了一个缩放视图.
127
00:06:12,931 --> 00:06:15,498
So let's notify the controller cuz the controller might need
那么这时候就需要通知 Controller 了,因为也许 Controller
128
00:06:15,500 --> 00:06:17,834
to know that and react to that, okay.
需要知道这些事件发生的时候,并且在这些事件发生时与View进行交互,
129
00:06:17,836 --> 00:06:20,936
Maybe it effects the model when you zoom in or out.
也许是在放大或缩小的过程中会影响到 Model,
130
00:06:20,938 --> 00:06:23,605
Also maybe the view like the scroll view needs to make sure
也许一些视图(如滚动视图)需要知道其是否能
131
00:06:23,607 --> 00:06:26,875
it's okay to do something, like if the scroll view says
做一些操作,如一个滚动视图需要知道
132
00:06:26,877 --> 00:06:29,377
should I allow vertical scrolling right now?
是否能在垂直方向上滚动?
133
00:06:29,379 --> 00:06:31,913
Maybe it wants to ask the controller that. So you have
有时候就是希望通过 Controller 来知道一些的信息.因此在
134
00:06:31,915 --> 00:06:35,483
a lot of messages that have words in them like should,
方法设计过程中,很多方法都包含 should,
135
00:06:35,485 --> 00:06:40,187
will, and did, okay? That the minions wanna ask
will, did 等关键字.有些时候 View 希望通过
136
00:06:40,189 --> 00:06:42,589
questions of the controller involved with controller.
Controller 获得一些信息,因此需要调用一些方法来获得.
137
00:06:42,591 --> 00:06:46,292
Okay? So, [COUGH] This is done via what's called
上面所说的那么多种情况都是通过一种叫
138
00:06:46,294 --> 00:06:48,795
a delegate. And we're gonna talk about delegation next
委托的方式进行实现的.我们将在下周讲述委托的课程.
139
00:06:48,797 --> 00:06:51,897
week. And the word delegate is appropriate here because
这里用委托这个词语是非常恰当的,
140
00:06:51,899 --> 00:06:53,899
it's essentially the view's minions are delegating
因为本质上来说 View 的"仆从"就是将
141
00:06:53,901 --> 00:06:57,803
some responsibility to the controller Okay. The way this
一些职责交给 Controller 来实现.这种实现方式
142
00:06:57,805 --> 00:07:01,906
is implemented is very simple. Delegate, the delegate is just
也非常简单.委托通常是
143
00:07:01,908 --> 00:07:05,243
a property in the view and that property,
View 的一个属性
144
00:07:05,245 --> 00:07:07,778
you might ask, what's the class of that property,
也许你会问这个属性是什么类型(Class)的,
145
00:07:07,780 --> 00:07:12,616
because the view doesn't know anything about the calculator
因为 View 对计算器 Controller 一无所知.
146
00:07:12,618 --> 00:07:15,852
view controller. The answer is, it's not gonna be a class.
答案就是委托不是一个类型(Class),
147
00:07:15,854 --> 00:07:19,055
It's going to be what's called a protocol. Okay, and
更恰当的说法是叫做协议.
148
00:07:19,057 --> 00:07:20,089
we're gonna talk about what protocols are.
我们来说说什么叫协议.
149
00:07:20,091 --> 00:07:22,491
Protocols are basically just a description of a bunch of
协议就是一系列
150
00:07:22,493 --> 00:07:27,229
methods that the other guy promises to implement. Okay,
希望别人来实现的方法的描述.
151
00:07:27,231 --> 00:07:28,864
and so if you can imagine if the controller
因此你可以设想一下如果一个 Controller
152
00:07:28,866 --> 00:07:31,299
would promise to implement these will, should, and did
实现了带有 will, should,did 等关键字的方法后,
153
00:07:31,301 --> 00:07:33,768
things, then the viewer could talk to it even if the view
View 就可以通过委托通知 Controller,即使 View
154
00:07:33,770 --> 00:07:37,705
doesn't know what class it is. Okay, no similarly There's
不知道委托具体是什么类型(Class).
155
00:07:37,707 --> 00:07:40,741
an important aspect of MVC which is the views,
还有一个MVC中非常重要的方面,
156
00:07:40,743 --> 00:07:45,545
okay, the view can not own the data they are displaying. Now,
就是任何视图都不能包含他们所展示的数据(Model).
157
00:07:45,547 --> 00:07:47,881
how are they going to display it if they don't own it?
那么在没有拥有 Model 的情况下, View 是如何进行展现的?
158
00:07:47,883 --> 00:07:50,650
Well, they're going to ask for it from the controller all
它们总是通过询问 Controller
159
00:07:50,652 --> 00:07:52,885
the time and the controller is going to get it from
而 Controller 从 Model 中获得数据.
160
00:07:52,887 --> 00:07:56,689
the model. Okay, so that's another kind of protocol but
除了上面所述的协议外,还有另一种
161
00:07:56,691 --> 00:07:57,422
instead of will did and
不含有关键字 will, did, 和 should
162
00:07:57,424 --> 00:08:00,525
should you've got messages in that protocol like give
关键字的协议, 主要是通过协议来获得
163
00:08:00,527 --> 00:08:03,594
me the data at this location and how many pieces of data
某个位置的数据或者是数据的总数等.
164
00:08:03,596 --> 00:08:06,997
are there, okay? Things that are asking about the data so
通过这些协议 View 可以
165
00:08:06,999 --> 00:08:09,366
the viewer can figure out what's going on and
获得需要显示的数据以及
166
00:08:09,368 --> 00:08:13,103
display it, okay. And that's also done with delegation,
根据数据来决定如何显示, 而这也是通过委托的方式来实现
167
00:08:13,105 --> 00:08:16,706
although we call that Delegate the Data Source. Okay, so
但是我们称这些委托为数据源(Data Source).
168
00:08:16,708 --> 00:08:19,308
there'd be another property on some views called
因此在一些View中也存在着
169
00:08:19,310 --> 00:08:22,878
the Data Source, which is this protocol based pointer,
数据源的属性,并且是该数据类型的指针.
170
00:08:22,880 --> 00:08:23,812
basically, to another object and
通过设置其他对象或
171
00:08:23,814 --> 00:08:27,849
the controller sets itself as that so it can get involved.
Controller 为数据源, 这样 View 就能调用该数据源的方法.
172
00:08:27,851 --> 00:08:29,784
And providing the data for the view.
这样同时也就能为 View 提供显示的数据.
173
00:08:29,786 --> 00:08:32,320
Kay? So those are the ways that the view can communicate
因此这就是我们所说的 View 到 Controller 的交互.
174
00:08:32,322 --> 00:08:34,588
to the controller. You can see they're all pretty defined,
你可以看到他们都是定义好的方法
175
00:08:34,590 --> 00:08:37,524
well-defined ways they're not just open ended.
而不是完全开放随便定义的.
176
00:08:37,526 --> 00:08:40,827
Mkay? Now, this leads to a situation where
这就导致了 Controller 的任务可以
177
00:08:40,829 --> 00:08:45,165
the controller's job can be described as interpreting and
描述为"为了 View 解释和
178
00:08:45,167 --> 00:08:49,335
formatting the model data for the view. Okay.
格式化 Model 的数据"
179
00:08:49,337 --> 00:08:52,538
It also interprets view input for the model. So
同时也可以为了 Model 而处理通过 View 的输入.
180
00:08:52,540 --> 00:08:53,939
it's an interpreter between both. That it,
因此 Controller 是一个类似 Model 和 View 的翻译人员一样.
181
00:08:53,941 --> 00:08:56,942
controller job so that's really where all your UI logic
因此可以看出 Controller 的任务主要处理界面上的逻辑
182
00:08:56,944 --> 00:09:00,211
is in there. Okay, how bout the model?
那么对于 Model 到 Controller 的交互呢?
183
00:09:00,213 --> 00:09:02,847
Can it talk directly to the controller?
它能直接调用 Controller 的方法吗?
184
00:09:02,849 --> 00:09:07,084
Absolutely not because the controller is
当然不可以!因为 Controller 是用来处理
185
00:09:07,086 --> 00:09:08,252
your UI logic.
你界面上的逻辑的.
186
00:09:08,254 --> 00:09:11,421
The model is UI independent. So there's absolutely no way
而 Model 是相对于界面完全独立的.因此 Model 不能
187
00:09:11,423 --> 00:09:13,957
the model could have anything to say to the controller.
直接调用Controller里面的行为和变量.
188
00:09:13,959 --> 00:09:17,126
However, what happens if the model,
那么当Model的数据变化的时候,
189
00:09:17,128 --> 00:09:20,830
which is UI independent, has some data that changes,
如何通知界面数据以及修改了?
190
00:09:20,832 --> 00:09:24,133
okay? So it's maybe the model is representing data on
例如 Model 里面是用来表现网络上的数据.
191
00:09:24,135 --> 00:09:26,435
a network. In some ways change is gonna be on the network and
有时候通过网络得知数据变化了.
192
00:09:26,437 --> 00:09:29,471
it's changing. How does the model let the controller know?
那么如何通知 Controller ?
193
00:09:29,473 --> 00:09:33,808
Well, to do this we use what we call a radio station model,
为了实现这个,我们采用广播的形式
194
00:09:33,810 --> 00:09:37,111
okay? So the radio station is just a thing that
而广播就是通过
195
00:09:37,113 --> 00:09:38,512
the model can set up,
Model 自己来完成广播的机制
196
00:09:38,514 --> 00:09:39,546
set up its own radio station.
将 Model 自己设立为广播中心.
197
00:09:39,548 --> 00:09:41,915
And it broadcasts on that radio station whenever,
然后通过发送广播,通知那些对数据变化
198
00:09:41,917 --> 00:09:43,883
whenever anything interesting happens. Okay,
有监听的广播站.
199
00:09:43,885 --> 00:09:47,987
and then the controller just tunes in to that station. So
然后将 Controller 设置成接收广播的设备.
200
00:09:47,989 --> 00:09:49,755
the model is not really talking to the controller.
因此可以看出 Model 并不会直接对 Controller 进行通知.
201
00:09:49,757 --> 00:09:52,190
It's just talking to anyone who wants to know.
它只是告诉那些想知道的对象.
202
00:09:52,192 --> 00:09:54,826
What's going on in the model. Now all that communication on
那么 Model 的"广播"是如何实现的呢? 所有通过广播中心
203
00:09:54,828 --> 00:09:57,762
that rad, radio station since it's done by the model has
的形式,而不直接与界面打交道.
204
00:09:57,764 --> 00:10:00,931
nothing to do with UI. It's about the data in the model.
而所有的广播都是建立在Model的数据基础上,
205
00:10:00,933 --> 00:10:03,701
I have new data, my data changed, those kind of
例如我有一个新的数据,我的数据修改了, 这些事件
206
00:10:03,703 --> 00:10:07,070
messages are going out on this radio station, okay?
都是通过广播中心发送出去.
207
00:10:07,072 --> 00:10:11,474
Now other greater stations can be worked between other camps
或许有人想是否可以在非 Model 和 Controller 的
208
00:10:11,476 --> 00:10:12,942
besides the model and the controller, and
其他"阵营"之间
209
00:10:12,944 --> 00:10:15,678
some have asked, hey, can I just create a view that tunes
建立通知机制,例如在 View 与 Model之间
210
00:10:15,680 --> 00:10:18,213
into the model directly, and short circuit the controller?
直接建立广播的机制, 或者是直接绕过 Controller 的形式?
211
00:10:18,215 --> 00:10:20,849
And the answer is no, you don't wanna do it that way.
当然是不行, 不建议这样做.
212
00:10:20,851 --> 00:10:23,018
Okay? You would want to have the controller tuning in
你最好在 Controller 和 Model 之间建立管道.
213
00:10:23,020 --> 00:10:24,719
to the model. And having the controller set up
然后通过 Controller 来控制 View
214
00:10:24,721 --> 00:10:26,487
this generic view thing to display the data.
是如何展示数据的.
215
00:10:26,489 --> 00:10:28,989
Question? [INAUDIBLE] Standpoint, it's easy to
提问:关于什么是
216
00:10:28,991 --> 00:10:33,126
understand the controller view log model just like your idea
Controller 和 View很容易理解, 而对于 Model 只是一个概念,
217
00:10:33,128 --> 00:10:36,696
of how this things implemented in the software?
那么如何在软件中实现 Model ?
218
00:10:36,698 --> 00:10:37,930
>> So the question is, so
这位同学提的问题是:
219
00:10:37,932 --> 00:10:40,666
it's easy to understand what the controller and
关于 Controller 和 View
220
00:10:40,668 --> 00:10:42,835
view are, they're displaying the UI.
是什么很容易理解,因为他们是直接展示在界面上的.
221
00:10:42,837 --> 00:10:45,537
The model is less easy to kinda conceptualize,
而 Model 却更像是一个概念性的东西,
222
00:10:45,539 --> 00:10:49,040
what that is, so what is the model? Really the model
那么具体什么是 Model呢?
223
00:10:49,042 --> 00:10:51,976
it takes a little more design but to design the model you
关于 Model 你需要进行一些设计, 但是设计 Model
224
00:10:51,978 --> 00:10:56,080
have to think about what is it my app does fundamentally,
需要你想清楚你的程序在本质上是关于什么的,
225
00:10:56,082 --> 00:10:58,549
independent of how it would be displayed. Like imagine I
而不是关于你的程序在界面上该如何展示. 例如想想
226
00:10:58,551 --> 00:11:01,785
wanted a calculator and had a command line interface where I
我希望完成一个计算器, 而计算器通过命令行来实现,
227
00:11:01,787 --> 00:11:03,420
could type five times three equals and
当如输入5 乘以 3 等于的时候,
228
00:11:03,422 --> 00:11:07,290
it would work. Okay, well that's a user interface but
Model 就发挥作用了. 它更偏向于计算器的接口
229
00:11:07,292 --> 00:11:09,458
the calculation, the actual multiplication and stuff,
并且所有的计算和运算符都
230
00:11:09,460 --> 00:11:12,428
that would be in the model. So the model is more about
将存储在这个 Model. 所以 Model 更像是
231
00:11:12,430 --> 00:11:14,363
trying to understand what it is your application does,
让我们知道程序是做什么的,
232
00:11:14,365 --> 00:11:17,332
not how it's displayed. That's the separation that we have to
而不是它是如何进行展示的. 这就是为什么我们必须
233
00:11:17,334 --> 00:11:18,800
do in this design. >> So it's
将进行划分模块的原因.
234
00:11:18,802 --> 00:11:20,167
kind of like an algorithm? >> Yeah,
这个是不是有点像算法?
235
00:11:20,169 --> 00:11:23,303
it's more of the algorithms, the data, the databases and
那些算法,数据,数据库以及其他的一些
236
00:11:23,305 --> 00:11:25,505
stuff like that are more in the model. And you'll see it
都讲存储在 Model 中. 你也能根据之前的
237
00:11:25,507 --> 00:11:27,841
by experience. we'll deal with the calculator today and
经验来了解这一点. 我们今天将做一个计算器
238
00:11:27,843 --> 00:11:32,545
you'll get an example how that plays out. Okay.
并且你们也将了解如何进行完成.
239
00:11:32,547 --> 00:11:37,249
Now, this all only builds one MVC, okay? One MVC,
接下来我们将写一个 MVC 的例子.
240
00:11:37,251 --> 00:11:42,154
generally an iOS, controls one iPhone screen or
一个通用的 iOS 的程序,
241
00:11:42,156 --> 00:11:44,923
maybe on an iPad it's two pieces or
无论是在 iPhone, iPad
242
00:11:44,925 --> 00:11:47,859
three different pieces on the iPad screen. In other words
或者是其他的设备, 都是有多个 MVC 部分构成的. 换一句话来说
243
00:11:47,861 --> 00:11:50,161
this is only controlling a little part of your app.
一个 MVC 只是完成了一个程序的一部分.
244
00:11:50,163 --> 00:11:53,030
To build a real app we have to take these MVCs,
为了完成一个程序,我们需要构建非常多的 MVC
245
00:11:53,032 --> 00:11:56,300
make a whole bunch of them and then combine them, okay?
似的这些 MVC 完成我们的工作.
246
00:11:56,302 --> 00:12:00,103
That's how we make a big app, all right? Now, when we do
这就是如何制作一个大的程序.当我们
247
00:12:00,105 --> 00:12:03,873
that, it, it's still important that the communication is well
制作一个程序的时候, 在 MVC 之间定义一个良好的交互
248
00:12:03,875 --> 00:12:09,178
defined and basically the MVC, an MVC can only serve as
是非常重要的, 并且有的时候一个 MVC 是为了
249
00:12:09,180 --> 00:12:12,882
part of the view of another MVC, okay?
另外一个 MVC 服务的.
250
00:12:12,884 --> 00:12:14,616
Do you see how this is arranged up here?
大家看看上图中的是如何进行排列的.
251
00:12:14,618 --> 00:12:17,219
If you look at any of the purple controllers up there
如果你看到图中紫色的 Controller 的,
252
00:12:17,221 --> 00:12:20,221
you notice that any arrow they have to another MVC
你将会注意到有很多绿色的线条指向其他的 MVC,
253
00:12:20,223 --> 00:12:24,225
goes out that view side, okay? So we always wanna
代表着跳转到其他视图. 我们可以认为
254
00:12:24,227 --> 00:12:26,961
think of these MVCs as part of the view of another MVC.
这些 MVC 是另外一些 MVC 的一部分.
255
00:12:26,963 --> 00:12:30,397
And there are some MVCs like tab bar controller that's
例如像苹果官方提供一个典型的 MVC —— UITabBarController.
256
00:12:30,399 --> 00:12:33,533
an MVC that's provider iOS. Where you might have three or
它将会将3-4个 MVC 作为自己的子视图.
257
00:12:33,535 --> 00:12:37,570
four other MVCs as part of its view. And those are the things
这些视图会
258
00:12:37,572 --> 00:12:39,872
when you press on the tabs at the bottom,
在你点击下方的按钮时,
259
00:12:39,874 --> 00:12:41,374
you see a different MVC, right.
进行切换不同的 MVC.
260
00:12:41,376 --> 00:12:45,344
So, that's what we built app at four MVCs let say,
当我们为用整个 MVC 时,将会有4个 MVC,
261
00:12:45,346 --> 00:12:47,546
one of them is the top level tab bar controller.
而且中一个会显示在界面上,
262
00:12:47,548 --> 00:12:49,748
And then we have let say, three other MVCs.
而其他三个将不会展示在界面上.
263
00:12:49,750 --> 00:12:52,850
And those three MVCs might do completely independent things
而这些 MVC 可能作者完全没有关联的事情,
264
00:12:52,852 --> 00:12:56,754
and as we build this we really want each MVC to be completely
当我们这样做时,我们希望每一个 MVC 都能完全完成自己部分的功能.
265
00:12:56,756 --> 00:12:58,355
self-contained, just like when we design
就如我们设计一个对象,
266
00:12:58,357 --> 00:13:00,624
objects we want them to be completely self-contained. We
我们希望他能完成他自己部分的功能一样,
267
00:13:00,626 --> 00:13:02,959
don't want them reaching into the internal implementations
我们不希望这些对象能深入了解其他对象的内部实现.
268
00:13:02,961 --> 00:13:06,295
of other objects, right? So and sometimes we're building
就如同我们设计
269
00:13:06,297 --> 00:13:09,665
an object orient system here out of MVCs as well.
面向对象系统一样,我们提出了 MVC 的概念.
270
00:13:09,667 --> 00:13:11,867
Okay, now you'll see how all this works in week three.
我们将在第三周展示这些事如何进行工作的,
271
00:13:11,869 --> 00:13:15,404
We'll start doing multiple MVCs and it'll all make sense.
并且我们将开始使用一些 MVC 完成我们的程序.
272
00:13:15,539 --> 00:13:18,006
Okay, one thing we don't wanna do of course is
而我们当然不希望所完成的
273
00:13:18,008 --> 00:13:20,374
build something when MVCs are [LAUGH] not working together.
MVC 没办法很好的完成我们的程序.
274
00:13:20,376 --> 00:13:21,875
If these arrows start going in every which
如果这些线条指向各个
275
00:13:21,877 --> 00:13:24,044
way direction then there's gonna be now way to understand
方向,并且一旦指向的方向变得复杂
276
00:13:24,046 --> 00:13:26,513
how your app works once it gets to a certain complexity.
那么我们就不可能理解你的程序是如何进行工作的.
277
00:13:26,515 --> 00:13:28,614
It's just gonna be beyond your comprehension.
那么它将超出你的理解能力.
278
00:13:28,616 --> 00:13:32,918
Okay, so, we don't want this. This is bad. All right,
这并不是我们想要的,这也是不好的设计.
279
00:13:32,920 --> 00:13:35,721
so the demo I'm gonna dive right into here.
所以我们深入写出一个 Demo.
280
00:13:35,723 --> 00:13:37,155
Again, this is a slide you can look at later,
这个幻灯片你可以之后再回来查看,
281
00:13:37,157 --> 00:13:39,891
important things that I'm gonna cover in this demo.
重要的是我们将要开始封装这个 Demo.
282
00:13:39,893 --> 00:13:42,727
Cuz I'm not coming back to the slide so let me summarize,
而之后也不在返回这张幻灯片,所以让我们来大概总结一下
283
00:13:42,729 --> 00:13:46,764
what's coming up? On Friday, we do have this debugging,
幻灯片里面的内容. 在周五, 我们将会有调试的课程.
284
00:13:46,766 --> 00:13:49,667
session. It's at 1:30 in this room,
周五下午一点半,在这间教室.
285
00:13:49,669 --> 00:13:52,235
okay? I highly recommend you go to that,
我强烈建议你们来听.
286
00:13:52,237 --> 00:13:54,437
especially if you've never done debugging in Xcode,
特别是针对那些没有使用过 Xcode 进行调试的同学,
287
00:13:54,439 --> 00:13:57,640
cuz you'll kinda be wondering how the heck it all works
因为你可能想要了解它是如何进行工作的.
288
00:13:57,642 --> 00:14:00,910
otherwise. Next Monday we'll be talking about more Swift,
另一方面,下周一我们将会深入了解 Swift
289
00:14:00,912 --> 00:14:03,012
that's when your first reading assignment is due and
你的第一个阅读作业即将到期,
290
00:14:03,014 --> 00:14:04,212
your second reading assignment will go out.
而在你的第二次阅读作业也将要开始了.
291
00:14:04,214 --> 00:14:07,282
And then next Wednesday we're gonna start about talking
并且在下周三,我们将开始了解
292
00:14:07,284 --> 00:14:08,116
about custom drawing in iOS.
如何在iOS中自定义绘图.
293
00:14:08,118 --> 00:14:11,585
What if we wanna not just use a button and a, and a label,
例如我们不想用系统自带的按钮或者标签
294
00:14:11,587 --> 00:14:14,188
but we wanna draw our own stuff? And that's when
而是希望自己绘制出自己想要的控件.
295
00:14:14,190 --> 00:14:16,957
programming assignment one will be due before lecture and
当我们编程作业也即将在课程结束前到期,
296
00:14:16,959 --> 00:14:21,061
programming assignment two will go out after lecture.
而我们的第二次编程作业也将在课程结束后布置.
297
00:14:21,063 --> 00:14:22,395
Okay? Any questions,
还有什么问题吗?
298
00:14:22,397 --> 00:14:26,899
you all ready to jump in this demo? All righty, here we go,
你们准备好开始今天的 Demo 了吗?那我们开始吧.
299
00:14:26,901 --> 00:14:32,271
I'm just gonna pick up right where we left off with
我们将在上次开发的基础上
300
00:14:32,273 --> 00:14:35,240
I'm gonna go to developer here as our calculator, all right.
继续完成我们的计算器开发.
301
00:14:35,242 --> 00:14:36,741
I'm gonna, when I wanna relaunch it,
首先,当我想重新运行它时,
302
00:14:36,743 --> 00:14:41,212
I could just launch and get the splash screen here. And
我可以通过 Xcode 的启动页来启动.
303
00:14:41,214 --> 00:14:44,648
then, click on this to open it and here it is and
点击我们的项目来打开.
304
00:14:44,650 --> 00:14:47,618
the, before, if you remember where we were, we only had
如果你还记得我们上节讲到哪, 应该记得我们上节
305
00:14:47,620 --> 00:14:52,155
a pi button and then the keypad. That was great and now
有了一个π的按钮和一个数字键盘. 我们接下来
306
00:14:52,157 --> 00:14:54,624
we wanna add more buttons, and that's what we're going to do.
将添加更多的按钮, 将完成更多的功能.
307
00:14:54,626 --> 00:14:56,259
We're gonna add more operations and
我们将添加更多的功能,
308
00:14:56,261 --> 00:14:59,161
more sophisticated operations, like multiplying and
一些复杂的计算, 如乘法
309
00:14:59,163 --> 00:14:59,895
things like that.
等操作.
310
00:14:59,897 --> 00:15:02,731
Before I do that I wanna talk a little bit about a feature
在开始之前,我先说一个 Swift 的特性,
311
00:15:02,733 --> 00:15:06,368
in Swift that can really make your code read a lot better.
一个能让你的代码可读性更好的特性.
312
00:15:06,370 --> 00:15:09,537
You notice here that we have this type conversion.
你可能注意到了这里有一个类型转换,
313
00:15:09,539 --> 00:15:12,373
String and pi, right? Where when the pi button gets
讲一个π的数值转换成 String 类型.
314
00:15:12,375 --> 00:15:15,909
pressed, we have to convert pi to a double which is a string.
当我们点击π按钮时,我们将一个 Double 类型的π转换成 String 类型
315
00:15:15,911 --> 00:15:18,879
Well if I think ahead about all the operations I'm gonna
那么如果我考虑到将来对于计算器来说,所有的操作数
316
00:15:18,881 --> 00:15:21,915
wanna add to my calculator there all doubles,
我们都讲采用 Double
317
00:15:21,917 --> 00:15:24,217
everything is doubles, not strings, okay?
所有都用 Double 的数值,而不是 String 的类型.
318
00:15:24,219 --> 00:15:26,585
So am I really gonna have for all these operations,
那么我们有必要将这些操作符
319
00:15:26,587 --> 00:15:29,054
all kinds of converting back and forth between strings and
在 Double 和 String 之间转来转去吗?
320
00:15:29,056 --> 00:15:32,457
doubles as I try to put the results into the display or
例如将结果展示在界面上或者
321
00:15:32,459 --> 00:15:34,259
get the number out of the display?
从界面上获得数值?
322
00:15:34,261 --> 00:15:36,127
That is gonna end up being really tedious,
如果不断这样做的话,那么将会有非常多冗余的代码.
323
00:15:36,129 --> 00:15:38,696
okay, and it's gonna make my code kind of a mess,
它会让我的代码看起来很混乱,
324
00:15:38,698 --> 00:15:40,330
lots of type conversions back and forth.
因为有非常多的类型转换的代码.
325
00:15:40,332 --> 00:15:45,168
Wouldn't it be cool if I had a var called Display Value which
如果有一个叫做 displayValue 的属性,
326
00:15:45,170 --> 00:15:50,073
was a double, and this bar automatically tracked what was
一个 Double 类型的属性,并且会监控到界面上的变化.
327
00:15:50,075 --> 00:15:52,608
in that display? In other words if I ever got
也就是说,如果我希望获得属性的值
328
00:15:52,610 --> 00:15:55,177
the value of this, it would be the value of the display
我将通过界面上的字符串转换成一个
329
00:15:55,179 --> 00:15:57,746
as a double. And if I ever set the value of this, it would
Double 类型的数值.而当我改变该变量的数值时,
330
00:15:57,748 --> 00:16:00,649
set the display. Wouldn't that be cool? Right, that would
界面也会跟着变化. 这不是非常便利吗? 这样会
331
00:16:00,651 --> 00:16:02,951
make all the rest of my code a lot easier because I would
使我其余的代码更加容易读懂, 因为我总是在跟
332
00:16:02,953 --> 00:16:03,718
be all in double land and
一个 Double 类型的数值打交道.
333
00:16:03,720 --> 00:16:05,653
Is not having to be doing this string version?
并且不用一个处理 String 的代码.
334
00:16:05,655 --> 00:16:08,188
And the answer is we can absolutely do that kind of
那么问题就是我们能不能有这样的属性,
335
00:16:08,190 --> 00:16:10,824
var, a var that tracks something else, okay? This
一个可以监控某一值变化的属性?
336
00:16:10,826 --> 00:16:13,393
var, our user is in the middle of typing, is just stored.
我们之前学的都是存储属性,
337
00:16:13,395 --> 00:16:15,828
That true false value is stored somewhere with this
例如之前学过布尔类型的存储属性.
338
00:16:15,830 --> 00:16:19,265
object. This one, instead of being stored, it's going to be
而今天说的是一个不同于存储属性,
339
00:16:19,267 --> 00:16:23,802
calculated, okay? And we call this a computed property. And
而是一个通过计算获得的属性.我们通常都称其为计算属性.
340
00:16:23,804 --> 00:16:27,072
we do it by just putting curly braces after it, okay.
我们只需要在属性后面添加一个花括号.
341
00:16:27,074 --> 00:16:28,006
And inside this curly braces,
并且在花括号内,
342
00:16:28,008 --> 00:16:29,974
we're gonna put some code to calculate
我们添加对应的计算该属性值的代码,
343
00:16:29,976 --> 00:16:35,680
the value of this property. Both when we get it, okay. And
当我们希望获得该属性值时,添加get关键字
344
00:16:35,682 --> 00:16:37,881
when we set it. So, we have this get and
当我们希望获得改变属性值时,添加 set 关键字.
345
00:16:37,883 --> 00:16:41,985
set, keywords here. And inside here, we just put code to get
在这里,我们只需要获得显示的值即可.
346
00:16:41,987 --> 00:16:45,021
the value of display value. And set is the code that gets
而set的代码
347
00:16:45,023 --> 00:16:48,624
executed when someone tries to set the value of this var.
是用户想要改变该属性值时调用的代码.
348
00:16:48,626 --> 00:16:51,927
Okay? Super simple. So, what's the implementation of this?
非常简单吧. 那么这里的 get 方法该如何实现呢?
349
00:16:51,929 --> 00:16:55,164
Really easy when someone tries to get the display value
非常简单,只需要返回显示在界面上的数值,
350
00:16:55,166 --> 00:16:58,734
I'm just going to return the display's text. Okay?
首先我只返回界面上显示的文本
351
00:16:58,736 --> 00:17:03,938
Unwrapped, but of course, this is a string. Right? Okay? And
然后unwarp它,但是这是一个 String 类型. 对吧?
352
00:17:03,940 --> 00:17:06,307
this is supposed to be returning a double. So,
但是这里需要返回一个 Double 的数值.
353
00:17:06,309 --> 00:17:09,210
I need to convert this string to a double. So, I'm gonna say
所以我需要将 String 转换成 Double.
354
00:17:09,212 --> 00:17:15,416
double. That, okay, now this is still not gonna work,
那么我这里进行类型转换成 Double , 但是这里还是有错误.
355
00:17:15,418 --> 00:17:18,218
okay? Why is that? Let's look at our error. It says
为什么呢? 让我们看看错误信息.
356
00:17:18,220 --> 00:17:21,087
the value of optional double is not unwrapped. Look it's
一个 Optional Double 并没有unwarp.
357
00:17:21,089 --> 00:17:24,691
trying to unwrap this. Okay, that's really weird. See it's
从界面可以看得出来,这里试图unwarp它.这个看起来非常奇怪.
358
00:17:24,693 --> 00:17:27,426
pointing an exclamation point at the end of this double.
这里在强制转换后自动添加了一个感叹号.
359
00:17:27,428 --> 00:17:28,560
I didn't have to do that down here.
其实我完全没必要这么做.
360
00:17:28,562 --> 00:17:31,697
When I converted from this Double to a string, I didn't
这里当我将 Double 转换成 String 时,我没有
361
00:17:31,699 --> 00:17:36,134
unwrap it. Why is this? I'm trying to create a double
进行unwarp. 那么上面这里当我从一个 String 生成一个 Double 类型时
362
00:17:36,136 --> 00:17:37,168
here using this string.
为什么需要unwarp?
363
00:17:37,170 --> 00:17:40,037
Why do you think this is returning an optional double
为什么这里返回一个 Optional Double ,而不是
364
00:17:40,039 --> 00:17:44,641
instead of a double? >> Because it might not be
一个 Double 类型? [学生回答:] 因为字符串
365
00:17:44,643 --> 00:17:45,275
convertible. >> Correct.
可能不是一个数字. [白老头:] 答对了.
366
00:17:45,277 --> 00:17:48,878
It might not be convertible. Right? If I press hello in
这里的值可能不是一个可以转换成 Double 类型的 String . 例如我这里输入 'Hello' .
367
00:17:48,880 --> 00:17:54,951
there as the string. Double of hello, eh, I don't know. Okay,
将'hello'转换成Double, 不知道具体转换成什么.
368
00:17:54,953 --> 00:17:57,086
now again it could return zero or something else but
如果出现这种情况,我们可以返回0或者其他的数值.
369
00:17:57,088 --> 00:17:59,755
really it wants to say, I don't know. I can't do it.
这就代表着我不知道具体的数值是多少.
370
00:17:59,757 --> 00:18:02,591
And the best way to do that is with an optional. So, some
最好的方式就是返回一个 Optional 的类型.
371
00:18:02,593 --> 00:18:05,860
constructors. Okay? Some of these initializers for various
对于一些构造器来说,
372
00:18:05,862 --> 00:18:10,431
classes can return optional versions of the thing.
在创建实例的过程中可能会返回 Optianal 的类型.
373
00:18:10,433 --> 00:18:13,067
In the case where they can't necessarily create one for
也就是说,他们却好必要参数来创建一个对象.
374
00:18:13,069 --> 00:18:14,935
you. Okay? So that's really kind of awesome.
这是一个非常好的的特性.
375
00:18:14,937 --> 00:18:17,871
So let's go ahead and unwrap that. Okay now this would,
我们继续,并且unwarp它.
376
00:18:17,873 --> 00:18:20,607
again, this would crash if we ever put hello in here, it's
当我们在界面上显示 'hello' 时,这里就会产生奔溃.
377
00:18:20,609 --> 00:18:23,009
gonna crash. So, we're kinda designing our codes assuming
所以我们需要设计好我们的代码,
378
00:18:23,011 --> 00:18:26,679
this is always going to have a number. How about setting it?
并且假设界面上总是显示数值. 那么如何进行设置它?
379
00:18:26,681 --> 00:18:30,115
Okay? Here we want to set the display's text
这里我们只需设置文本框的显示
380
00:18:30,117 --> 00:18:33,819
equal to what the person is setting the display value to.
设置成我们想要设置的值即可.
381
00:18:33,821 --> 00:18:36,755
Okay? When someone sets the display values they're
当我们设置 displayValue 的值为5后,
382
00:18:36,757 --> 00:18:39,924
going to say in their code display value equals five,
也就等于设置了文本框显示为5.
383
00:18:39,926 --> 00:18:44,295
right? So, how do I get the five in here, in this set?
那么我们如何设置文本框的数值为5呢?
384
00:18:44,297 --> 00:18:47,498
And the answer is there's a special key word called new
答案是使用一个叫做 newValue 的关键字.
385
00:18:47,500 --> 00:18:51,635
value. Okay? This new value is going to be the double that
这里的 newValue 是一个用户设置 Double类型
386
00:18:51,637 --> 00:18:56,973
somebody set. Okay? Display value equals something. Now,
displayValue 的值也将 newValue.
387
00:18:56,975 --> 00:18:58,841
I want to put this in display text, but, of course,
接下来 newValue 传递 display 控件的文本,
388
00:18:58,843 --> 00:19:03,145
this what type is this right here? The double, right?
那么这里的 newValue 是什么类型呢? Double 类型.
389
00:19:03,147 --> 00:19:05,047
Because they said display value equals something and
因为他们想要设置 displayValue
390
00:19:05,049 --> 00:19:07,315
it's a double. And this has got to be a string. So
所以 newValue 就是一个 Double 类型. 而这里 display.text 需要传递一个 String 类型.
391
00:19:07,317 --> 00:19:11,986
I've got to convert this to a string. Just like I did below.
所以我需要将其转化为 String 类型.就像我下面所实现的.
392
00:19:11,988 --> 00:19:15,223
That, okay, can always convert a double to a string so
因为总是能将 Double 转换成 String,
393
00:19:15,225 --> 00:19:17,324
there's no optional, stuff going on.
所以这里就不会出现Optional的类型.
394
00:19:17,326 --> 00:19:22,129
And that's it, okay. I've now invented a new property,
这就是计算属性. 到这里我就新建了一个计算属性.
395
00:19:22,131 --> 00:19:25,065
that is calculated. And every time I ask for
这样当我每次请求它的值的时候
396
00:19:25,067 --> 00:19:26,966
its value I'm gonna get what's in the display's double.
我讲从界面展示的文本框内获得一个 Double 的值.
397
00:19:26,968 --> 00:19:30,169
And every time I set it, it's gonna set the display. Pretty
而当我每次设置它的时候, 它将会将其显示在界面上.
398
00:19:30,171 --> 00:19:33,439
cool? And it makes our code like down here a lot better.
有了它能使我们的代码更简单.
399
00:19:33,441 --> 00:19:36,041
Instead of having this go down here, we're just gonna say
接下来替换这里的代码,只需
400
00:19:36,043 --> 00:19:41,113
displayValue = Pi, okay? We don't need to do this
改成 displayValue = M_PI 即可. 我们不在需要
401
00:19:41,115 --> 00:19:44,449
type conversion in reference displayed text, okay?
进行类型的转换了.
402
00:19:44,684 --> 00:19:47,985
Everyone understand that? And, this is going to make it a lot
大家都明白了吗? 通过添加计算属性, 让我们的代码更简单了.
403
00:19:47,987 --> 00:19:50,888
easier to add new things. Let's add another property, or
接下来让我们xib上添加一个新的
404
00:19:50,890 --> 00:19:55,125
a another, operation here. I'm gonna add square root, okay?
按钮.添加一个根号操作.
405
00:19:55,127 --> 00:19:56,159
So let's go here into square root.
让我们找找根号的字符.
406
00:19:56,161 --> 00:19:59,395
The square root symbol I'm gonna get from the edit.
根号的字符我们能从Edit菜单下找到.
407
00:19:59,397 --> 00:20:02,431
If you go into edit menu of most Mac apps you'll see this
在大部分Mac的程序里,你都能在 Edit 菜单下找到
408
00:20:02,433 --> 00:20:03,999
emoji and symbols thing at the bottom,
Emoji & Symbols 的子菜单.
409
00:20:04,001 --> 00:20:07,569
brings up this, window or you can have a lot of emoji, but
这个子菜单会弹出一个弹出框,里面包含 Emoji 表情
410
00:20:07,571 --> 00:20:11,840
you can also have math symbols and, down here here's square
当然你也可以在这里找到数学符号, 当然包括了根号.
411
00:20:11,842 --> 00:20:13,574
root. Just the square root symbol, okay.
这里就选中这个根号即可.
412
00:20:13,576 --> 00:20:17,545
So I'm gonna put the square root symbol on this button.
并且将这个根号字符显示到按钮上.
413
00:20:17,747 --> 00:20:22,883
Square root. Okay? And, then it's already wired
并且当我鼠标放在
414
00:20:22,885 --> 00:20:25,719
up if I hold over here you can see it's hooked up because I
这个"+"号这里, 你会发现新的按钮已经和代码绑定了.
415
00:20:25,721 --> 00:20:29,155
copy and pasted the pi button. We can see it's okay here
因为我是通过拷贝π按钮的.同时你可以看到它并没有
416
00:20:29,157 --> 00:20:31,457
because I didn't copy and paste the digit button.
跟数字按钮绑定在一起,因为复制数字按钮产生的.
417
00:20:31,459 --> 00:20:34,159
If I right-click on it we can see that it's only gonna send
当我在这里点击右键,我们能看到它和哪个
418
00:20:34,161 --> 00:20:37,362
perform operation, right, so that's all good. And
方法管理在一起了.
419
00:20:37,364 --> 00:20:40,765
all I need to do here is say if the mathematical
我这里所要做的就是
420
00:20:40,767 --> 00:20:45,536
symbol equals, that square root thing then the display
点击了根号按钮的时候, 界面显示
421
00:20:45,538 --> 00:20:50,741
value equals the square root of the display value.
数值变成其根号的数值.
422
00:20:52,078 --> 00:20:54,678
Okay? So, you can see that this code is really nice.
可以看得出来, 代码方面更加简洁了.
423
00:20:54,680 --> 00:20:57,247
If I didn't have that I would have had to get the display
如果没有计算变量, 我就需要
424
00:20:57,249 --> 00:21:00,950
text, convert it to a double, do the square root, convert it
将界面显示转换成 Double, 然后计算出根号值
425
00:21:00,952 --> 00:21:03,786
back to a string, and put it back into display text.
然后转换成 String 后, 再显示回界面.
426
00:21:03,788 --> 00:21:05,287
See how that would have been a mess? Okay?
可以想象一下多麻烦.
427
00:21:05,289 --> 00:21:08,690
And, this is only just the very first one I added.
这只是我添加的第一个计算属性.
428
00:21:08,692 --> 00:21:11,793
If we add a whole bunch more it's gonna be even more and
如果我们有一堆的计算按钮,
429
00:21:11,795 --> 00:21:13,394
more leverage to have this thing. But
它会提高非常多的效率
430
00:21:13,396 --> 00:21:15,996
mostly I'm showing you this because I want you to see what
但是这里我主要是为了给你们介绍
431
00:21:15,998 --> 00:21:16,864
computed properties look like.
什么叫做计算属性.
432
00:21:16,866 --> 00:21:19,733
We use them all the time in Swift, and we're going to use
我们在用 Swift 的过程中经常用到它, 我们也将在这个例子
433
00:21:19,735 --> 00:21:22,335
them yet again in this demo, and you should get comfortable
中不断的用到它, 你应该明白
434
00:21:22,337 --> 00:21:24,837
with the fact that not all your properties are stored
并不是所有的属性都是存储属性
435
00:21:24,839 --> 00:21:28,240
some of them might be computed like this. All right.
有一些属性是计算属性.
436
00:21:28,242 --> 00:21:33,212
I want to add more operations now, but I have to be careful
接下来我想添加更多的操作, 但是需要注意的是
437
00:21:33,214 --> 00:21:37,182
here because this code really does not belong in my
这些所有的计算的代码不应该属于 Controller
438
00:21:37,184 --> 00:21:42,220
controller, okay? Because this is the code of what my app is.
因为这些运算的代码正是我程序所完成的事情.
439
00:21:42,222 --> 00:21:45,456
It's a calculator and I'm doing calculations here. So,
这个程序完成的是一个计算器,而这里所完成的就是计算的功能.
440
00:21:45,458 --> 00:21:48,759
this needs to move into a model class.
所以需要将这些代码移到 Model 的类中.
441
00:21:48,761 --> 00:21:51,395
Okay? So now, it's time to do MBC here and
那么是时候来开始用 MVC 了,
442
00:21:51,397 --> 00:21:54,531
move this stuff into a model class. So,
并且将这里的运算的代码移到一个 Model 的类中.
443
00:21:54,533 --> 00:21:57,934
what's our model class gonna look like? Let's create it and
那么 Model 的类该如何实现呢? 首先先创建它,
444
00:21:57,936 --> 00:22:01,237
kinda design in an API for it and then we'll get back and
然后像设计 API 一样, 最后我们在这里使用它.
445
00:22:01,239 --> 00:22:04,106
use it here. Okay? So, to create it, okay, in
那么我们开始创建它吧.
446
00:22:04,108 --> 00:22:07,376
fact to create any new file in x code, you're gonna go file,
为了在 Xcode 中创建一个新的文件, 我们可以通过
447
00:22:07,378 --> 00:22:10,846
new File. Okay? File, new file. And when you go here,
File 菜单下的 New 子菜单下的 File来创建. 当你点击后,
448
00:22:10,848 --> 00:22:12,981
it's going to say, what kind of file do you want to create?
将会弹出一个弹出框询问想要创建什么文件类型.
449
00:22:12,983 --> 00:22:16,784
And of course, we want to create an iOS Source file.
这里我们当然是创建一个 iOS 的源文件.
450
00:22:16,786 --> 00:22:18,819
Okay? Not watch OS or something.
而不是 watch OS 或者是其他的文件.
451
00:22:18,821 --> 00:22:21,388
And here we're going to create a Swift file. If we were
然后选择源文件中的 Swift 文件. 如果想要
452
00:22:21,390 --> 00:22:24,658
creating a Cocoa Touch Class, like a new view controller,
创建 UIViewController 之类的文件时,我们可以选 Cocoa Touch Class.
453
00:22:24,660 --> 00:22:27,394
we would go here. But if we're going to create just a model
但是我们只想建一个 Model,
454
00:22:27,396 --> 00:22:29,896
class, we go here. So I'm going to double-click.
所以我们这里选择 Swift 文件. 双击 Swift 文件
455
00:22:29,898 --> 00:22:31,764
It's going to say where do you want to put this?
接下来就是选择要将文件存在哪?
456
00:22:31,766 --> 00:22:34,099
I'm going to put it in the same group, calculator,
这里我存在同一个 Group 里, Calculator 的 Group
457
00:22:34,101 --> 00:22:36,034
that all my other swift files are in.
所有的 Swift 文件都存在这里.
458
00:22:36,036 --> 00:22:37,602
You see, ViewController.swift there.
可以看到 ViewController.swift 也存在着.
459
00:22:37,604 --> 00:22:40,805
I'm going to call it calculator brain because it's
命名文件为 CalculatorBrain
460
00:22:40,807 --> 00:22:42,573
going to be the brain of our calculator.
因为这将是所有运算的代码所在文件
461
00:22:42,575 --> 00:22:45,743
It's going to be the model for our calculator.
并且是它就是我们计算器的 Model.
462
00:22:45,745 --> 00:22:49,546
Then click create. Here it is right here. You can see that
然后点击创建. 然后就展示创建的文件.
463
00:22:49,548 --> 00:22:51,948
the very first thing, it imports Foundation,
你能看到文件中最开始就引入了 Foundation,
464
00:22:51,950 --> 00:22:56,686
not UI Kit. Never import UI Kit in a model file because
而不是 UIKit. 永远不要在 Model 中引入UIKit,
465
00:22:56,688 --> 00:22:59,488
the model is UI independent. So it would never do that.
因为 Model 是相对于界面独立的. 所以永远不要这样做.
466
00:22:59,490 --> 00:23:02,324
If you find yourself importing UI Kit, you're doing it wrong.
如果你发现你引入的 UIKit, 那么肯定是错误的.
467
00:23:02,326 --> 00:23:05,527
Okay? So, Foundation is what we want. Foundation is that
Foundation 正是我们所需要的. Foundation 是
468
00:23:05,529 --> 00:23:07,962
core services layer, kind of the basic stuff,
基础的服务层, 提供基础的数据功能,
469
00:23:07,964 --> 00:23:11,032
non-UI. Base stuff. By the way, let
而跟界面完全没关系. 顺带说一句,
470
00:23:11,034 --> 00:23:13,401
me show you how you can put different things on each side.
如何通过 Xcode 查看不同内容的文件呢?
471
00:23:13,403 --> 00:23:15,102
So I've got calculator running over here,
在左边显示 Calculator 的 Model,
472
00:23:15,104 --> 00:23:17,804
what if I wanna have my controller still be over here?
那如何在右边显示 Controller 的内容呢?
473
00:23:17,806 --> 00:23:19,639
And you do that with these things at the top. Okay?
你只需要点击上边的这里.
474
00:23:19,641 --> 00:23:23,343
The top line here is actually changeable. You can pick
上面的导航栏是可以变化. 你可以选择
475
00:23:23,345 --> 00:23:26,679
other things to show. So, for example, I can go show my
其他的内容来显示. 例如, 我可以显示我的
476
00:23:26,681 --> 00:23:29,515
controller here. Okay, now I can have them both on
Controller 的内容. 这样我就可以在同一屏幕里
477
00:23:29,517 --> 00:23:32,084
screen at the same. Which is kind of convenient, especially
显示 Model 和 Controller 了. 非常的方便,
478
00:23:32,086 --> 00:23:34,619
if I have a class that I'm using in another class. I can
特别是那些我引用了其他类的情况下.
479
00:23:34,621 --> 00:23:37,989
see its API here, and use it over here. All right, so
我能通过这种方法查看他的 API, 并且调用正确的方法.
480
00:23:37,991 --> 00:23:40,157
I'm going to create a new class called Calculator Brain,
因此我在这里创建一个新的 Class 取名为 CalculatorBrain,
481
00:23:40,159 --> 00:23:43,627
and we know how to do that. Right? We know how to do that.
就如同我们所熟悉的方式.
482
00:23:43,629 --> 00:23:48,131
Okay, class Calculator Brain. What's its super class?
通过使用 class 后面跟着 CalculatorBrain 即可. 那么它的父类是什么呢?
483
00:23:50,369 --> 00:23:53,002
No superclass, right? CalculatorBrain, this model,
没有父类. CalculatorBrain 并没有
484
00:23:53,004 --> 00:23:54,136
it doesn't inherit from anything.
从任何类上继承.
485
00:23:54,138 --> 00:23:55,971
It doesn't need to inherit from anything, okay?
它不需要继承任何一个类.
486
00:23:55,973 --> 00:23:58,440
So it's just a base class. All right, now let's
它只是一个基础类. 那么我们现在来考虑考虑
487
00:23:58,442 --> 00:24:00,942
talk about what its API is. Everyone knows the phrase
它的API吧. 大家都知道API的语法.
488
00:24:00,944 --> 00:24:03,611
API, I hope. That means the interface through which we're
这就意味着我们对于 CalculatorBrain 将跟着这些
489
00:24:03,613 --> 00:24:06,881
going to be programming, using this, CalculatorBrain.
API 来进行编程, 并且使用它.
490
00:24:06,883 --> 00:24:10,017
It's all the methods and properties in it.
所有的方法和属性都在保存在这里.
491
00:24:10,019 --> 00:24:14,488
So, I'm gonna do a little function called setOperand,
这里我先加一个名为 setOperand 的方法,
492
00:24:14,490 --> 00:24:18,191
okay, which just takes a Double, okay? That's gonna be
它的参数是 Double 类型. 这将是它的
493
00:24:18,193 --> 00:24:21,094
part of it. So if I'm using my CalculatorBrain, I'm gonna set
一部分. 当我使用 CalculatorBrain, 我需要设置一个
494
00:24:21,096 --> 00:24:24,330
an operand. Then I'm gonna have another function in here,
操作数. 这里再添加另一个方法,
495
00:24:24,332 --> 00:24:27,066
called performOperation, which is gonna operate on
命名为 performOperation, 用来计算操作数的.
496
00:24:27,068 --> 00:24:30,302
that operand. And the argument there is gonna be a String,
该方法将接收一个 String 的参数,
497
00:24:30,304 --> 00:24:33,405
which is the mathematical symbol, okay? And then lastly,
一个用来数学操作符的参数.
498
00:24:33,407 --> 00:24:38,242
I'm gonna have a var, which is the result of the operation,
最后还有一个属性, 用来保存结果的操作数.
499
00:24:38,244 --> 00:24:39,677
which is gonna be a Double.
一个为 Double 类型的属性.
500
00:24:39,679 --> 00:24:43,681
And I'm gonna do something interesting here, instead of
我将在这里做一件有趣的事
501
00:24:43,683 --> 00:24:47,384
just having this be a public var that could be set and got.
我不会让这个公有变量同时拥有 get 和 set 方法
502
00:24:47,386 --> 00:24:49,953
Because the setting of this doesn't really make sense for
因为对于使用 CalculatorBrain 的人来说
503
00:24:49,955 --> 00:24:51,854
anyone using my CalculatorBrain to set this.
set 方法实在没有什么意义
504
00:24:51,856 --> 00:24:55,558
I set it internally, okay, because of performOperation.
因为我只在 performOperation 的内部 set 它
505
00:24:55,560 --> 00:24:58,494
So I'm actually gonna make this computed and
所以我会实际的去做运算,并
506
00:24:58,496 --> 00:25:01,229
only implement the get side of it, okay?
实现在它的 get 方法里面
507
00:25:01,231 --> 00:25:02,864
I'm not gonna implement this set, so
我不会实现它的 set 方法
508
00:25:02,866 --> 00:25:05,833
now this becomes a read-only property. Do you, do you all
所以现在它变成了一个只读属性
509
00:25:05,835 --> 00:25:09,637
remember another a read-only property we used last time?
你们还记得我们上次使用的另一个只读属性吗?
510
00:25:09,639 --> 00:25:12,940
Current title in button, okay? So current title in button is
Button 的 currentTitle
511
00:25:12,942 --> 00:25:16,476
a computed read-only property in button. That title, that
它是 Button 中的一个可计算的只读属性
512
00:25:16,478 --> 00:25:18,545
current title, is probably gotten from a UI label or
currentTitle 可能来自于一个 UI Label
513
00:25:18,547 --> 00:25:21,080
something that the button is using to draw its title, okay?
或者被 Button 用来绘制它的 title 的东西
514
00:25:21,082 --> 00:25:24,150
It comes from somewhere else, that's why it's computed,
它是其它地方获取到的,所以它是一个可计算的属性
515
00:25:24,152 --> 00:25:27,019
okay? So I'm gonna do the same thing here. So this is how you
我在这里也将做同样的事情
516
00:25:27,021 --> 00:25:29,321
can make a property be read-only to the callers,
你们也可以使用类似的方法来把属性设置成只读的
517
00:25:29,323 --> 00:25:33,692
okay? Yeah. >> So can we use the get for
[学生提问]
518
00:25:33,694 --> 00:25:35,893
comparison, not just for assignments,
[学生提问]
519
00:25:35,895 --> 00:25:37,795
like with equal equal sign? >> Okay, so
[学生提问]
520
00:25:37,797 --> 00:25:40,998
the question is, is the get used for comparison?
问题是,在做比较的时候,会调用 get 方法吗?
521
00:25:41,000 --> 00:25:45,135
Well, comparison is actually quite interesting in Swift.
Swift 中的比较还是挺有趣的
522
00:25:45,137 --> 00:25:48,038
The equals equals operator is like a function, and
== 运算符像一个函数
523
00:25:48,040 --> 00:25:50,139
it takes those two sides as arguments.
它把两边的东西当作参数
524
00:25:50,141 --> 00:25:53,342
And those two sides have to implement certain methods
如果两边的东西想要被比较
525
00:25:53,344 --> 00:25:54,843
if they wanna be comparable, okay?
就需要实现某些待定的方法
526
00:25:54,845 --> 00:25:57,979
Now, we're not, we're not far enough along in terms of our
现在我们还没有学习的那么深
527
00:25:57,981 --> 00:26:01,282
understanding of Swift to see exactly how that works. But
还没有理解到那种程度
528
00:26:01,284 --> 00:26:03,518
the answer to your question succinctly is no,
但是你的问题的答案是不会
529
00:26:03,520 --> 00:26:06,053
the get really doesn't have anything to do with equality.
get 方法与比较(==)无关
530
00:26:06,055 --> 00:26:09,590
Equality is just a function that is different,
比较是一个方法,这两者是不同的
531
00:26:09,592 --> 00:26:14,594
okay? All right, so, I'm gonna return 0 for
这里先 return 0
532
00:26:14,596 --> 00:26:18,832
right now, okay? Just to get rid of my little, error there.
先把这个错误提示给躲掉
533
00:26:18,834 --> 00:26:21,934
But eventually, we're gonna have to implement this,
不过最终我们还是会实现这里
534
00:26:21,936 --> 00:26:22,735
internally and make it work.
让它正确工作
535
00:26:22,737 --> 00:26:26,972
Now, I wanna talk a little bit about APIs right here, okay?
我想再谈论一些有关 API 的东西
536
00:26:26,974 --> 00:26:29,675
So far, every method and property we've done
到目前为止,这个类中的每个方法和属性
537
00:26:29,677 --> 00:26:33,078
in this whole class has been essentially public. Meaning,
都是 public(公有) 的,意味着
538
00:26:33,080 --> 00:26:36,447
any class can call any of the methods in any of the classes
我们类中的所有方法都可以被其他类调用
539
00:26:36,449 --> 00:26:40,484
we created. For example, all of our controller vars,
举个例子,controller 中的所有变量
540
00:26:40,486 --> 00:26:44,722
okay, and functions could all be called by some other class.
还有函数,都可以被其它类调用
541
00:26:44,724 --> 00:26:46,723
Now, that's bad, okay, that's bad.
这样处理不是太好
542
00:26:46,725 --> 00:26:47,891
For example, displayValue,
例如 displayValue
543
00:26:47,893 --> 00:26:50,760
we wouldn't want some other class setting the displayValue
我们不想让应用中的其它类通过这个 controller
544
00:26:50,762 --> 00:26:53,429
in the calculators through this controller. Because we
来修改 displayValue 的值
545
00:26:53,431 --> 00:26:56,832
managed that displayValue by what our model calculates,
因为我们通过我们的 model 计算的值来管理 displayValue
546
00:26:56,834 --> 00:26:59,067
right? So this is internal implementation.
所以这是内部的实现
547
00:26:59,069 --> 00:27:01,770
In fact, all of this is internal implementation or
事实上,所有这些都是内部实现或控制的
548
00:27:01,772 --> 00:27:05,506
control. We do not want other classes to be able to call it,
我们不想让其它类调用它
549
00:27:05,508 --> 00:27:09,944
unlike these three, which are external, okay? They're, they,
不像这里的三个,是外部的
550
00:27:09,946 --> 00:27:12,146
we want people calling these in CalculatorBrain.
我们希望其它人在 CalculatorBrain 中调用它们
551
00:27:12,148 --> 00:27:13,747
That's how our CalculatorBrain works.
因为 CalculatorBrain 就是这么工作的
552
00:27:13,749 --> 00:27:14,481
If people couldn't call this,
如果不调用这几个方法
553
00:27:14,483 --> 00:27:16,315
they couldn't even use the CalculatorBrain.
CalculatorBrain 就一点用都没有了
554
00:27:16,317 --> 00:27:19,051
So how do we specify that difference between something
所以我们怎么来控制其他人
555
00:27:19,053 --> 00:27:21,887
that should be called by other people or not? We do that with
能不能调用指定的方法呢?
556
00:27:21,889 --> 00:27:26,091
the private keyword. So I'm gonna add private, okay,
可以通过 private(私有的)关键字
557
00:27:26,093 --> 00:27:28,193
this private keyword right here,
就是这个 private
558
00:27:28,195 --> 00:27:31,329
to all of my functions and methods over here.
把这所有的方法都标记为 private
559
00:27:31,331 --> 00:27:33,564
I don't, this is not really part of Swift again,
再说一次,这不是 Swift 中的关键字
560
00:27:33,566 --> 00:27:34,865
this is kind of an Xcode thing, so
这是 Xcode 相关的东西
561
00:27:34,867 --> 00:27:36,533
I put it after that. But otherwise,
所以要放到它的后面
562
00:27:36,535 --> 00:27:38,568
you put it there, and we're gonna put it for all of these.
把它们都标记成 private 的
563
00:27:38,570 --> 00:27:40,403
We're gonna make all of these be private.
它们都是
564
00:27:40,405 --> 00:27:43,305
And as you program, okay, you're gonna see that one of
当你写程序的时候
565
00:27:43,307 --> 00:27:45,908
the evaluation criteria on your homework is that you
也就是在你们的作业中你们会使用这种判断标准
566
00:27:45,910 --> 00:27:48,610
properly make things private when they should be private.
当它们应该是 private 的时候,才把它们设置成 private 的
567
00:27:48,612 --> 00:27:51,680
And I generally would err on the side of making it private.
但我不建议你们这样做
568
00:27:51,682 --> 00:27:53,881
It's a lot easier to make something private and
我还是建议你们先设置成 private 的
569
00:27:53,883 --> 00:27:53,981
decide to make it public, than to use something public,
当你决定让它们成为 public 的时候
570
00:27:53,983 --> 00:27:56,083
go back later and
再调整为 public 的
571
00:27:56,085 --> 00:27:57,851
have a whole bunch of coders start using it, and
如果有很多人调用了你开发的类
572
00:27:57,853 --> 00:27:59,853
then decide, no, no, I want that to be private.
然后你又决定把某个方法变成 private 的
573
00:27:59,855 --> 00:28:02,789
Then you break all those other people. So err on the side of
那么所有其他人的程序都会出现问题
574
00:28:02,791 --> 00:28:06,125
private first, and then making things public, okay?
所以还是先把方法设置为 private 的,需要的时候再改成 public
575
00:28:06,127 --> 00:28:10,129
Now, it's actually possible to look at something and see what
现在可以通过
576
00:28:10,131 --> 00:28:13,699
its public interface is by going up here to the top and
点击上方的这里,选择 Generated Interface
577
00:28:13,701 --> 00:28:16,568
picking Generated Interface. This will show you the public
来看所有的公共接口
578
00:28:16,570 --> 00:28:20,438
API of the class in the main window on the left there.
它会显示左边窗口的类的所有公共的 API
579
00:28:20,440 --> 00:28:23,341
So we're gonna look at the public API of CalculatorBrain.
让我们看看 CalculatorBrain 公共的 API
580
00:28:23,343 --> 00:28:25,676
You can see that it has that setOperand, performOperation,
可以看到有 setOperand, performOperation 和 result
581
00:28:25,678 --> 00:28:28,211
and result. Notice this looks just like current title,
注意,这个 result 看起来和 currentTitle 一样
582
00:28:28,213 --> 00:28:30,914
right, where it's saying this is a read-only thing.
这表明它是一个只读的
583
00:28:30,916 --> 00:28:32,715
We don't see any implementation here,
我们在这里看不到实现
584
00:28:32,717 --> 00:28:35,918
this is purely the API, okay? So
只能看到 API
585
00:28:35,920 --> 00:28:38,787
no implementation here. Also notice this says internal,
这里没有实现。还需要注意这里的 internal
586
00:28:38,789 --> 00:28:41,490
you would think this might say public, okay? But there's
你可能会认为这里是 public
587
00:28:41,492 --> 00:28:44,392
actually a slight difference between internal and public.
但是 public 和 internal 还是有点区别的
588
00:28:44,394 --> 00:28:47,695
Internal means it's public within your module.
internal 代表,它只在你的 module 中是 public 的
589
00:28:47,697 --> 00:28:51,565
Public would mean it's public to everyone in other modules,
public 代表它在其它的 module 中也是 public 的(全局 public)
590
00:28:51,567 --> 00:28:52,199
so consider UIkit.
想想 UIKit
591
00:28:52,201 --> 00:28:55,736
UIkit has hundreds of public methods that we can call.
UIKit 有许多我们可以调用的公有方法
592
00:28:55,738 --> 00:28:58,271
But it also has hundreds, if not thousands of internal
但还有许多 internal 方法
593
00:28:58,273 --> 00:29:01,007
methods that only other UIkit classes can call between
是只有在 UIKit 中的类才可以相互调用的
594
00:29:01,009 --> 00:29:05,111
themselves. We don't even know what they are, okay? So, but
我们连它们是什么叫什么都不知道
595
00:29:05,113 --> 00:29:05,244
for your purposes,
但对于你来说
596
00:29:05,246 --> 00:29:07,145
since you're always gonna be working in the module,
因为你总是只在你的 module 里工作
597
00:29:07,147 --> 00:29:09,781
which is your app, internal means public,
所以在你的 APP 里 internal 就代表着 public
598
00:29:09,783 --> 00:29:13,117
basically. Let's go look at our controller now, and
看一眼 controller
599
00:29:13,119 --> 00:29:16,554
let's look at its public API, okay? So here I selected it,
看看它的 public API
600
00:29:16,556 --> 00:29:19,423
look over here. And it says, there's only one public thing,
它只有一个东西是 public 的
601
00:29:19,425 --> 00:29:22,526
userIsInTheMiddleOfTyping. I didn't mean that to be public.
userIsInTheMiddleOfTyping,我不想让它是 public 的
602
00:29:22,528 --> 00:29:24,661
I wanted that to be private, too, I just forgot to put
我想让它也变成 private 的
603
00:29:24,663 --> 00:29:26,996
the private on there. So if I go back over here and
我刚刚只是忘了在这加 private
604
00:29:26,998 --> 00:29:30,966
say private, okay, then you'll see it goes away. So now we
回到这里,加上 private,你会发现它已经消失了
605
00:29:30,968 --> 00:29:35,204
have no public API here. Now, it's still completely usable,
现在它已经没有 public API 了,但它仍然是可用的
606
00:29:35,206 --> 00:29:38,707
because in Interface Builder, we can wire up to this
因为在 Interface Builder 中,可以连线到这个 controller
607
00:29:38,709 --> 00:29:40,775
controller and make it appear in a tab bar controller,
让它出现在一个 tab bar controller 中
608
00:29:40,777 --> 00:29:43,778
all those things. We can do all that without having any of
我们可以不把 internal 改成 public
609
00:29:43,780 --> 00:29:45,246
the internal methods here be public.
就可以实现这些
610
00:29:45,248 --> 00:29:49,383
Okay, so we're gonna go back to my, oops, sorry.
返回我们的
611
00:29:49,385 --> 00:29:54,588
I'll go back to my brain over here, it's got my controller
CalculatorBrain
612
00:29:54,590 --> 00:29:59,259
over here. All right, so we've got brain and
这里放 ViewController
613
00:29:59,261 --> 00:30:02,195
controller. So let's think about how we're can use this
现在来想想怎么使用这个 model
614
00:30:02,197 --> 00:30:06,132
model over here, okay? We haven't implemented this yet,
我们还没有实现这个
615
00:30:06,134 --> 00:30:07,700
but we've defined its public API. So
但已经完成了它的 public API
616
00:30:07,702 --> 00:30:11,136
how can we use that over here? Well, we really wanna replace
所以我们怎么在这里使用呢?
617
00:30:11,138 --> 00:30:14,673
all of this business with using our model, right? Cuz
把这里的业务逻辑都用 model 来完成
618
00:30:14,675 --> 00:30:18,376
this is where we were doing model things, calculations.
mode 是我们完成计算的地方
619
00:30:18,378 --> 00:30:20,411
So we don't want that, okay? We wanna get rid of that, and
把这里删掉
620
00:30:20,413 --> 00:30:23,113
we want to start using our model here. Well, to have
开始使用我们的 model
621
00:30:23,115 --> 00:30:26,550
a model in our controller, we need to be able to talk to it,
为了在 controller 中使用 model,也就是与 model 交互
622
00:30:26,552 --> 00:30:28,051
that big green arrow, okay?
正如那个绿箭头所表示的那样
623
00:30:28,053 --> 00:30:32,922
So we need a private var, which I'll call brain,
我们需要创建一个 private 的变量,命名为 brain
624
00:30:32,924 --> 00:30:36,625
which is gonna be a CalculatorBrain, okay?
它的类型是 CalculatorBrain
625
00:30:36,627 --> 00:30:38,961
And this is the var that we're, it's gonna create our,
所以我们创建一个
626
00:30:38,963 --> 00:30:40,195
we're gonna create our CalculatorBrain.
CalculatorBrain 的实例
627
00:30:40,197 --> 00:30:43,365
And we're gonna talk to it to do all the calculations, okay?
然后我们通过它来实现所有的计算
628
00:30:43,367 --> 00:30:46,501
So this is just that big green arrow I showed you on that,
这就是我在之前幻灯片里给你们展示过的
629
00:30:46,503 --> 00:30:49,804
those previous slides, where the controller talks through
大的绿色箭头
630
00:30:49,806 --> 00:30:54,375
this to get to the model. Now, how about creating this thing?
controller 可以通过它来调用 model
631
00:30:54,377 --> 00:30:55,175
Where do we create this?
什么时候把它初始化呢?
632
00:30:55,177 --> 00:30:58,011
Well, you can see that we have an error up here,
这里有一个错误
633
00:30:58,013 --> 00:30:59,645
no initializers again.
没有被初始化
634
00:30:59,647 --> 00:31:00,446
That's because this var,
因为这个 var 跟其它的一样
635
00:31:00,448 --> 00:31:02,981
like any other, has to be initialized. And
需要被初始化
636
00:31:02,983 --> 00:31:06,218
I'm gonna create a CalculatorBrain here. And
我在这里创建 CalculatorBrain
637
00:31:06,220 --> 00:31:11,389
to do that, I have to call one of its initializers. And every
并调用它的其中一个初始化函数
638
00:31:11,391 --> 00:31:15,360
time you create a new class, you get one free initializer,
每一次你新创建的类都会有一个自带的初始化函数
639
00:31:15,362 --> 00:31:19,897
which is an initializer that takes no arguments, okay,
这个函数是没有参数的
640
00:31:19,899 --> 00:31:21,532
kinda the basic initializer. So
它就是最基本的初始化函数
641
00:31:21,534 --> 00:31:25,268
I'm using that CalculatorBrain initializer, it came for free.
我调用了那个自带的初始化函数
642
00:31:25,270 --> 00:31:27,937
I don't have anything that I need to initialize anyway.
我不需要在初始化它的时候初始化其它东西
643
00:31:27,939 --> 00:31:33,109
So, that's perfectly fine, okay, so I've created it. Now,
所以这么做是没问题的
644
00:31:33,111 --> 00:31:38,180
notice that this right here, do we need this?
我们需要这个吗?
645
00:31:38,182 --> 00:31:40,382
No, because Swift can infer that
不需要,因为 Swift 可以推断出来
646
00:31:40,384 --> 00:31:45,087
brain is a CalculatorBrain from that = right there, okay?
从等号的右边可以推断出它的类型是 CalculatorBrain
647
00:31:45,089 --> 00:31:49,724
So we do not wanna put colon CalculatorBrain. All right, so
所以不用在这里放 :CalculatorBrain
648
00:31:49,726 --> 00:31:53,227
now that we have this brain, kay, and it's created here, we
现在已经在这里创建好了 brain
649
00:31:53,229 --> 00:31:56,530
can use it to public API right here, to make things work.
我们就可以调用它的 public API 了
650
00:31:56,532 --> 00:31:59,166
Well, one thing we know is that when the mathematical
当数学符号进到这里的时候
651
00:31:59,168 --> 00:32:02,636
symbol comes through here, we wanna ask the brain to perform
我们想让 brain 来执行对应的操作
652
00:32:02,638 --> 00:32:05,805
that operation. Okay, so we're gonna pass that mathematical
所以要把这个数学符号
653
00:32:05,807 --> 00:32:08,241
symbol as the operation, we know that. We also
当作参数传进来
654
00:32:08,243 --> 00:32:11,544
probably know that after it's done performing the operation,
当它执行完操作以后
655
00:32:11,546 --> 00:32:15,247
we probably wanna put in the display the result,
要把 result 放到 displayValue 中显示
656
00:32:15,249 --> 00:32:19,818
the brain's result, this thing right here. Right? And
是 brain 计算的结果
657
00:32:19,820 --> 00:32:22,620
also at the beginning of the perform operation,
在做运算之前
658
00:32:22,622 --> 00:32:24,789
if we're in the middle of typing a number,
如果我们正在输入数字
659
00:32:24,791 --> 00:32:27,191
we better set that number at the operand for
最好把这个数字设置为 brain 的 operand
660
00:32:27,193 --> 00:32:30,561
the calculator to work on it. If we go 235 square root,
如果我们计算根号235
661
00:32:30,563 --> 00:32:34,464
we got to put that 235 in as the operand for the brain.
要把235设置为 brain 的 operand
662
00:32:34,466 --> 00:32:37,233
So we better say if the user is in the middle of typing
if userIsInTheMiddleOfTyping
663
00:32:37,235 --> 00:32:41,804
a number then brain.set operand to be whatever's
把显示的数字设置为
664
00:32:41,806 --> 00:32:45,508
in the display. You can see even here, having this display
brain.operand
665
00:32:45,510 --> 00:32:49,378
value thing makes our code read really beautifully.
即使这里有 displayValue,代码的可读性也是非常高的
666
00:32:49,380 --> 00:32:52,914
Okay. We can probably put this inside this if, because
可以把它放进这个 if 里
667
00:32:52,916 --> 00:32:57,886
no need to set it false if it's already true. Okay, so
如果它已经是 false 了,就没有把它设置成 false 的必要了(这里 Paul 口误了)
668
00:32:57,888 --> 00:33:01,589
that's it! That's all we need to do to hook our model up
好了,这样就把 model 和 controller 连接起来了
669
00:33:01,591 --> 00:33:04,759
to our controller. Okay. And we've removed everything
并且也把实际计算的代码
670
00:33:04,761 --> 00:33:07,027
in our controller that has to do with actually calculating.
都从 controller 里移除了
671
00:33:07,029 --> 00:33:12,833
We've basically given it all off to the model to do.
已经让 model 来做这件事了
672
00:33:13,068 --> 00:33:14,367
So now we have to implement this, okay?
现在得实现这个了
673
00:33:14,369 --> 00:33:16,168
We've gotta implement this brain over here.
我们得实现 brain 的逻辑
674
00:33:16,170 --> 00:33:19,638
I'm gonna make that be the main window here.
让它占满整个窗口
675
00:33:19,640 --> 00:33:20,172
And how are we gonna do that?
该怎么实现呢?
676
00:33:20,174 --> 00:33:24,743
Well, I'm gonna have a data structure here for my brain
先创建一个变量
677
00:33:24,745 --> 00:33:27,311
which makes pretty much sense, which is gonna be private,
当然要是 private 的
678
00:33:27,313 --> 00:33:30,114
which is gonna be called accumulator. It's gonna be
起名为 accumulator,类型是 Double
679
00:33:30,116 --> 00:33:34,318
a Double and it's going to accumulate the results, okay.
用它来保存结果
680
00:33:34,320 --> 00:33:37,521
As operations are performed, it's accumulating the result.
计算完毕后,把结果存起来
681
00:33:37,523 --> 00:33:40,090
Okay, anyone who knows how calculator's built,
知道怎么实现计算器的人
682
00:33:40,092 --> 00:33:43,226
it has internal calc, accumulator. So,
都应该知道它有个内部的存储器
683
00:33:43,228 --> 00:33:45,594
this is our accumulator. Notice that I,
这就是 accumulator
684
00:33:45,596 --> 00:33:47,696
as soon as I put this in here I get this error.
看这里,写完了以后就出现了一个错误
685
00:33:47,698 --> 00:33:51,233
Again, calculator brain has no initializers, that's because
同样地 calculatorBrain 没有初始化函数
686
00:33:51,235 --> 00:33:54,402
I don't initialize this. So, I'm gonna say this equals 0.0.
因为我还没有初始化它。我会把它设置为 0.0
687
00:33:54,404 --> 00:33:59,874
Once I do that I do not need this because 0.0,
这样做了就不再需要这里的 Double
688
00:33:59,876 --> 00:34:04,311
Swift always infers that. Or any something dot something,
因为 Swift 可以从0.0推断出它是 Double 类型的
689
00:34:04,313 --> 00:34:07,548
it always infers it to be a Double, okay. So that makes
它可以推断出来
690
00:34:07,550 --> 00:34:12,019
this be a Double. You see? If I made this no dots just zero,
所以它就是一个 Double 类型的
691
00:34:12,021 --> 00:34:14,888
then it's gonna infer this as an int, okay.
如果这里只是0,Swift 就会认为它是 int 类型
692
00:34:14,890 --> 00:34:19,092
So, good thing to know there. So now that I have my
了解这一点还是不错的
693
00:34:19,094 --> 00:34:22,628
accumulator, the result is always just the current state
现在已经有了 accumulator
694
00:34:22,630 --> 00:34:25,798
of my accumulator. So that's easy, open my result. And
result 返回的一直是 accumulator 当前的值
695
00:34:25,800 --> 00:34:28,433
same thing when someone says the operand, that kinda
如果有人调用了 setOperand
696
00:34:28,435 --> 00:34:33,271
resets my accumulator to be whatever that operand is.
就会改变 accumulator 的值
697
00:34:33,840 --> 00:34:37,241
Okay. So those are all easy to implement. So that just leaves
实现这些很容易
698
00:34:37,243 --> 00:34:40,844
this guy, perform operation. That's the heart of my model.
还剩下 performOperation,这是这个 model 的核心
699
00:34:40,846 --> 00:34:44,014
That's the thing that's really gonna do some calculation.
它是做计算的地方
700
00:34:44,016 --> 00:34:47,450
Now I could right here go back to what I was doing in my
这里可以像之前在 controller 里实现的那样
701
00:34:47,452 --> 00:34:51,387
controller which is to have some, if there is an essence
执行一些 if else 操作
702
00:34:51,389 --> 00:34:55,725
is here, well actually I'm gonna use switch. So
但在这里我用一下 switch
703
00:34:55,727 --> 00:34:57,993
switch is the same as another language but
switch 和其它语言的功能类似
704
00:34:57,995 --> 00:34:59,661
much more powerful in Swift and
但是在 Swift 中更加强大
705
00:34:59,663 --> 00:35:02,764
also much more important in Swift as you will see. Okay.
你们也会看到它在 Swift 中更重要
706
00:35:02,766 --> 00:35:04,799
Switch It's very important thing is Swift.
switch 在 Swift 中非常重要
707
00:35:04,801 --> 00:35:07,134
So, I can switch on this symbol that's legal.
我可以对这个符号做 switch 操作
708
00:35:07,136 --> 00:35:10,637
The switch on a String. Okay. And I just put the cases that
是对 String 进行的 switch 操作
709
00:35:10,639 --> 00:35:15,542
I wanna try. So, we have for example pi. And if pi happens,
首先把 pi 当作其中的一个 case
710
00:35:15,544 --> 00:35:19,779
I wanna set my accumulator equal to pi,
如果是 pi,就把 accumulator 设置为 pi
711
00:35:19,781 --> 00:35:24,017
okay. If it was for example, square root,
如果是 根号
712
00:35:24,019 --> 00:35:28,354
let's go do that. My square root symbol back, here it is.
我们先把根号调出来
713
00:35:28,356 --> 00:35:31,790
So if we get square root, then I'm just gonna say that my
如果它是根号
714
00:35:31,792 --> 00:35:36,461
accumulator equals the square root of the accumulator. Okay?
accumulator 就等于根号 accumulator
715
00:35:36,463 --> 00:35:40,331
So, this is basically getting us back to exactly where we
这和之前做的事情一样
716
00:35:40,333 --> 00:35:43,067
were before but now we have a model.
只不过现在我们是在 model 里做
717
00:35:43,069 --> 00:35:43,968
Notice we have an error here.
注意,这里有个 error
718
00:35:43,970 --> 00:35:47,171
That's because one thing about switch, you must consider
这是由 switch 引发的
719
00:35:47,173 --> 00:35:51,241
every possible value of this thing you're switching on.
你必须考虑到 switch 的所有可能的情形
720
00:35:51,243 --> 00:35:55,011
Now, this is a String, so it has infinite possibilities,
它的类型是 String,所以它有无限多种可能
721
00:35:55,013 --> 00:35:59,682
okay? Now, we could spend the next few years saying case A,
我们可以花几天的时间 switch 所有的情形
722
00:35:59,684 --> 00:36:02,184
no, we don't wanna do that. Instead, we're gonna put
但我们不会那样做
723
00:36:02,186 --> 00:36:06,155
default break, so default means, if you can't match any
我们只需要写上 default break
724
00:36:06,157 --> 00:36:10,392
of these other ones than just break out of this, okay? Now
它的意思是如果每一个 case 都匹配不上,就跳出这个 switch
725
00:36:10,394 --> 00:36:13,528
notice my indentations gotten a little wonky here, I'm gonna
这里的缩进有一些问题
726
00:36:13,530 --> 00:36:16,931
teach you something fun. If you select a curly braced
教你们一个小技巧吧,选中
727
00:36:16,933 --> 00:36:20,968
region including your whole file, and hit Ctrl+I.
文件中的花括号部分,按下 Ctrl+I
728
00:36:20,970 --> 00:36:23,637
It'll reformat it, okay? Re-lay it out. And
这部分的缩进就会变成正确的状态
729
00:36:23,639 --> 00:36:26,540
I strongly recommend that when you turn your homework in,
我强烈建议你们在交作业之前这么做
730
00:36:26,542 --> 00:36:29,142
you select all and do Ctrl+I. Okay?
选择代码,然后按下 Ctrl+I
731
00:36:29,144 --> 00:36:32,211
That way you'll be using the default indentation, even if
这样就会把代码变成默认缩进的格式
732
00:36:32,213 --> 00:36:35,681
you prefer something else, use the default one because people
即使你偏好其它类型的缩进
733
00:36:35,683 --> 00:36:38,017
reading your code are gonna be able to understand it better.
默认缩进会让其它人更好地理解你的代码
734
00:36:38,019 --> 00:36:41,720
Okay. And believe me, you'll adjust to whatever indentaki,
相信我,你应当适应
735
00:36:41,722 --> 00:36:44,522
indentation style this thing enforces on you, okay?
Xcode 为你调节的这种缩进方式
736
00:36:44,524 --> 00:36:46,891
If you start getting, if you are a computer scientist and
如果你是一个计算机科学家
737
00:36:46,893 --> 00:36:49,794
you start getting religious about things like indentation,
如果你在类似于缩进的事情上有了某些信仰
738
00:36:49,796 --> 00:36:51,328
you're heading down a pretty rocky road,
那你前行的道路可能就很崎岖了
739
00:36:51,330 --> 00:36:53,930
okay. Because when you wanna work in the real world you're
当你在真实的环境中工作
740
00:36:53,932 --> 00:36:56,966
gonna have companies that say this is the way we do it,
你的公司可能会使用你不偏好的那种缩进风格
741
00:36:56,968 --> 00:36:57,033
get used to it.
并且让你适应这种风格
742
00:36:57,035 --> 00:36:59,135
And if you sit there whining I don't like to do it that way,
如果你坐在那抱怨:我不想用那种风格
743
00:36:59,137 --> 00:37:02,104
well, you'll probably get fired. Okay? So don't do that.
你可能会被开除的,所以还是适应吧
744
00:37:02,106 --> 00:37:05,340
So here we're just gonna let the Xcode do our indentation
这里我们让 Xcode 来调整缩进
745
00:37:05,342 --> 00:37:08,843
for us. So this is all we need to do right here, okay.
目前为止要做的已经完成了
746
00:37:08,845 --> 00:37:10,812
This is full implementation. We can go back and
这就是完整的实现
747
00:37:10,814 --> 00:37:13,781
run our app and it's exactly the way it was before,
运行一下 app,和以前还是一样
748
00:37:13,783 --> 00:37:17,985
but now we're using a model. Okay. So
但是现在,我们使用了 model
749
00:37:17,987 --> 00:37:21,321
here we go, let's try 4-5, that's working square root.
试试根号45
750
00:37:21,323 --> 00:37:24,090
That looks like it's working. We'll just be sure by picking
看起来代码起作用了
751
00:37:24,092 --> 00:37:26,626
a number we know the square root of, pi seems to work,
根号9的值是3,也没问题
752
00:37:26,628 --> 00:37:28,728
square root. Okay, so we're back to where we were,
pi,根号 pi 都没问题,又回到了之前的状态
753
00:37:28,730 --> 00:37:33,132
that's nice. Now, they'll, thing about this now is I'm
很不错
754
00:37:33,134 --> 00:37:37,336
about to go add a whole bunch more operations here. And
不过还要添加更多的操作符在这里
755
00:37:37,338 --> 00:37:42,273
if for every single one I have to do the math, do the math,
对于每一个操作符,我都要做计算
756
00:37:42,275 --> 00:37:43,341
do the math each one,
每一个都要做计算
757
00:37:43,343 --> 00:37:45,176
this is gonna be a lot of duplicated code
这将会有很多的重复代码
758
00:37:45,178 --> 00:37:47,978
in here. Because every time I have a unary operation like
当它是一元操作符的时候,比如根号
759
00:37:47,980 --> 00:37:51,281
square root, it's exactly the same. If I have cosign or
它们的操作都是相同的
760
00:37:51,283 --> 00:37:53,049
square root or anything it's exactly the same.
如果这里是 cos 或者根号,这里的操作都是想同的
761
00:37:53,051 --> 00:37:56,786
The only difference is these four characters. Square root,
唯一的区别是这四个字母
762
00:37:56,788 --> 00:38:00,423
or cosign or whatever. Same thing for these constants,
是 sqrt 还是 cos。这里的常量也是类似的
763
00:38:00,425 --> 00:38:02,558
only this part will be different on every line,
唯一的区别是这一点地方
764
00:38:02,560 --> 00:38:05,193
even for binary, like multiplier, whatever.
对于二元操作符也是一样的
765
00:38:05,195 --> 00:38:06,561
It's probably only the function that does
唯一的区别是
766
00:38:06,563 --> 00:38:08,563
the calculation that's gonna be different. So I'm gonna
进行计算的函数
767
00:38:08,565 --> 00:38:12,733
factor this stuff out, so that all of these things,
所以我要重构这部分代码
768
00:38:12,735 --> 00:38:17,805
like pi and square root and multiply are in a table, okay?
把如 pi,根号,乘号之类的符号都放进一个表中
769
00:38:17,807 --> 00:38:21,441
And I'm just going to have this only be doing the generic
然后在这里只需要进行通用的计算
770
00:38:21,443 --> 00:38:22,742
calculations, generic constants,
通用的常量
771
00:38:22,744 --> 00:38:25,611
generic numeral operations, generic binary operations.
还有通用的一元操作和二元操作
772
00:38:25,613 --> 00:38:28,447
And it's gonna look in the table to find out what to do.
它会到表里查找,应该做什么操作
773
00:38:28,449 --> 00:38:30,115
Doesn't that seem like a much better design,
不觉得这是一种更好的设计吗?
774
00:38:30,117 --> 00:38:31,483
more extensible, less code, etc? So
更少的代码,更好的扩展性
775
00:38:31,485 --> 00:38:35,053
that's what were gonna do. So let's create that table, okay?
所有让我们先创建这张表
776
00:38:35,055 --> 00:38:37,488
And were gonna call that table operations. And
起名为 operations
777
00:38:37,490 --> 00:38:40,524
it's going to be the class, it's actually not a class.
它的类是 Dictionary,实际上 Dictionary 不是一个类
778
00:38:40,526 --> 00:38:44,228
Dictionary, okay, so Dictionary is a Swift thing.
它是一个 Swift 中的东西
779
00:38:44,230 --> 00:38:48,331
It is a generic type. You're probably used to that in Java.
它是一个通用的类型,你可能在 Java 中用过
780
00:38:48,333 --> 00:38:51,601
So you specify right here when you're declaring this
你得在这里声明它
781
00:38:51,603 --> 00:38:55,038
what the keys and values are, what type? And so I want one,
key 和 value 的类型
782
00:38:55,040 --> 00:38:57,039
I'm gonna start out just doing the constants.
先从常量开始
783
00:38:57,041 --> 00:38:59,108
Let's have this table only do constants, okay,
先让这个表只放 pi 之类的常量
784
00:38:59,110 --> 00:39:02,377
like pi. Okay? So I'm gonna have my keys be String.
key 的类型是 String
785
00:39:02,379 --> 00:39:06,281
That'll be the name of the constant, like the pi
key 是常量的名字,例如 pi
786
00:39:06,283 --> 00:39:09,350
character or whatever. And the value's gonna be a Double.
value 的类型是 Double
787
00:39:09,352 --> 00:39:14,055
So that'll be like M under bar pi or whatever. Okay? So,
它就是 pi 的值 M_PI 之类的东西
788
00:39:14,057 --> 00:39:16,690
I've declared it here. Now I'm actually gonna initialize
我在这里声明了它,也在这里初始化它
789
00:39:16,692 --> 00:39:19,026
it because remember I have to initialize all my vars.
因为必须要初始化所有 var
790
00:39:19,028 --> 00:39:21,895
You can initialize a Dictionary right on the fly
你可以在创建 Dictionary 的时候就初始化它
791
00:39:21,897 --> 00:39:25,898
just by using the open square bracket. Notation and
只需要使用方括号
792
00:39:25,900 --> 00:39:26,732
you just put like for
你可以放
793
00:39:26,734 --> 00:39:32,971
example pi cuz key colon m under bar pi the value.
"pi" : M_PI
794
00:39:32,973 --> 00:39:36,174
Okay so I'm basically filling up the Dictionary here.
现在我要继续填充这个字典
795
00:39:36,176 --> 00:39:39,210
Let's do another constant how about E that's M under bar e
继续添加另一个常量,"e" : M_E
796
00:39:39,212 --> 00:39:42,714
everyone know what e is 2.71 or whatever it is. Okay?
大家都知道 e 是2.71
797
00:39:42,716 --> 00:39:45,983
So, we could add a whole bunch more of these things into our
还可以向这张表中添加其它的东西
798
00:39:45,985 --> 00:39:47,885
table. Again, we're only doing constants right now,
现在只添加常量
799
00:39:47,887 --> 00:39:50,420
we're not doing square root and those kinds of things.
先不添加根号之类的东西
800
00:39:50,422 --> 00:39:52,122
So, that changes my code over here.
所以这里的代码就得改了
801
00:39:52,124 --> 00:39:54,657
Instead of having all that stuff right there,
把这些删掉
802
00:39:54,659 --> 00:40:01,364
I'm just going to let Constant equal Operations sub symbol.
换成 let constant = operation[symbol]
803
00:40:01,366 --> 00:40:05,701
So,this is how you look something up in a Dictionary.
这就是从 Dictionary 中查东西的方式
804
00:40:05,703 --> 00:40:08,603
Okay? Here's the name of the Dictionary, right here, and
这是 Dictionary 的名字
805
00:40:08,605 --> 00:40:11,272
you look it up with square brackets and the thing to look
通过 [] 来查询
806
00:40:11,274 --> 00:40:14,575
up. Okay? And now, I could just say my accumulator equals
然后把 constant 赋值给 accumulator
807
00:40:14,577 --> 00:40:19,380
that constant. Okay? But, this is not gonna work. Why? Let's
但这不会有效的
808
00:40:19,382 --> 00:40:24,084
find out. Error, it says, value of optional Double?
有错误,值是一个 optional 的 Double
809
00:40:24,086 --> 00:40:27,120
not unwrapped. Uh-oh, it's optionals again.
还没有解包,又是 optional
810
00:40:27,122 --> 00:40:30,590
Okay, what's happening here? It wants to unwrap constant.
发生了什么?
811
00:40:30,592 --> 00:40:33,993
In other words, it's saying this is an optional Double.
它说这是个 optional 的 Double
812
00:40:33,995 --> 00:40:34,760
Why would the thing,
为什么会这样呢?
813
00:40:34,762 --> 00:40:37,463
this Dictionary does not contain optional Doubles,
这个 Dictionary 包含的是 Double
814
00:40:37,465 --> 00:40:41,033
it contains Doubles? So, why would looking this symbol up
而不是 optional Double 啊
815
00:40:41,035 --> 00:40:43,068
in that Dictionary return an optional Double?
为什么从中查询数据的时候它就返回了个 optioal 了呢?
816
00:40:43,070 --> 00:40:46,371
Anybody have an idea? Someone besides you, cuz you got it
有人有什么想法吗?
817
00:40:46,373 --> 00:40:48,572
right before. [INAUDIBLE]. >> Yeah?
那个同学
818
00:40:48,574 --> 00:40:49,373
>> I think their Dictionary
字典里可能没有那个 key
819
00:40:49,375 --> 00:40:50,707
might not have that key? >> Correct!
正确
820
00:40:50,709 --> 00:40:53,043
Exactly the same thing as before, okay? This Dictionary
和之前一样对吧?
821
00:40:53,045 --> 00:40:57,013
might not contain that key that we're looking up. So,
Dictionary 中没有我们查询的那个 key
822
00:40:57,015 --> 00:40:59,882
it's gonna return nil to say I couldn't find it.
没找到,它就会返回 nil
823
00:40:59,884 --> 00:41:04,086
So, we simply need to unwrap it. Now, this is dangerous,
如果只是简单地把它 unwrap,是很危险的
824
00:41:04,088 --> 00:41:06,221
because maybe somebody's using my API, and
因为如果有人调用了我的 API
825
00:41:06,223 --> 00:41:08,623
they perform an operation that I don't understand,
performOperation 的参数在这里没有的话
826
00:41:08,625 --> 00:41:11,359
now I'm gonna crash. That's not very friendly. So,
程序就会崩溃,这很不友好
827
00:41:11,361 --> 00:41:16,764
here I'm gonna use if, the if let and set my accumulator,
这里我会使用 if let
828
00:41:16,766 --> 00:41:20,134
and I'm just gonna ignore any operation that I don't
如果没有的话,就直接忽略这次操作
829
00:41:20,136 --> 00:41:21,968
understand. I'm not going to affect my accumulator.
我不会修改 accumulator 的值
830
00:41:21,970 --> 00:41:26,840
Just leave it. Okay? All right, so let's run, make sure
让我们运行程序,看看是否还正常工作
831
00:41:26,842 --> 00:41:33,346
this works. All right. So, the square root's not gonna work,
根号已经不好用了
832
00:41:33,348 --> 00:41:35,715
cuz we don't have square roots in our table here,
因为表中没有根号
833
00:41:35,717 --> 00:41:36,315
we only have constants.
只有常量
834
00:41:36,317 --> 00:41:38,183
But, we have these are still working, and
但是这些还有效
835
00:41:38,185 --> 00:41:41,219
pi is still working. Okay. So that's good. So
pi 也可以正常工作,还不错
836
00:41:41,221 --> 00:41:43,755
we didn't break pi, at least. And if we had an e key,
至少 pi 还有用
837
00:41:43,757 --> 00:41:47,992
then the e key would work as well. All right. Now,
如果有 e 的 key 值,那它也会起作用的
838
00:41:47,994 --> 00:41:52,229
we want to extend this to do square root. Okay.
现在我们来扩展,实现根号计算
839
00:41:52,231 --> 00:41:52,729
How the heck we going to do that?
应该怎么实现呢?
840
00:41:52,731 --> 00:41:55,865
I mean, really, all we want to do is just say square roots,
我们想做的只是根号
841
00:41:55,867 --> 00:41:58,368
whoops, I shouldn't have done that. Square roots,
不小心打错了
842
00:41:58,370 --> 00:42:01,103
lets get our friendly neighborhood symbol for
把根号调出来
843
00:42:01,105 --> 00:42:06,342
square root here. Okay? Square root we really want to
我们想把 sqrt 这个函数
844
00:42:06,344 --> 00:42:08,577
put square root [LAUGH] right here.
放到这里
845
00:42:08,579 --> 00:42:10,011
Okay? The square root function.
sqrt 这个函数
846
00:42:10,013 --> 00:42:11,479
That's really what I want, what I want to do.
我就想这么做
847
00:42:11,481 --> 00:42:14,849
And, like, If I had cosine I'd really want to put the cosine
如果有 cos,那我也想把 cos 函数放到这里
848
00:42:14,851 --> 00:42:18,919
function here. Okay? Now this is obviously not a Double.
它显然不是一个 Double
849
00:42:18,921 --> 00:42:20,320
[LAUGH] That's not going to work.
它不会起作用的
850
00:42:20,322 --> 00:42:24,357
So, this can't be a Double. This has to be something else.
它不是 Double,它应该是别的类型的
851
00:42:24,359 --> 00:42:27,694
Okay? It has to be something that would work for a Double,
它应该对于 Double 和 function 类型
852
00:42:27,696 --> 00:42:31,630
and would also work for a function. Okay? How are we
同时起作用
853
00:42:31,632 --> 00:42:35,567
gonna do that? Well, we're gonna implement a new type,
该怎么做呢?我们要实现一种新的类型 enum(枚举)
854
00:42:35,569 --> 00:42:41,406
okay, and it's similar to class. It's called enum. Okay?
它与 class 很相似
855
00:42:41,408 --> 00:42:44,642
I'm gonna call this enum operation, and
把它起名为 Operation
856
00:42:44,644 --> 00:42:47,445
inside this enum, I'm gonna have all the different kinds
在 enum 中,有不同的计算方式
857
00:42:47,447 --> 00:42:50,214
of operations I know how to do. Now, you're probably used
你们可能使用过 enum
858
00:42:50,216 --> 00:42:52,683
to enum in other languages. What is enum in
在其它语言中
859
00:42:52,685 --> 00:42:56,286
In most languages it is a discrete set of values, right?
在大多数语言中,它把集合中的值进行分类
860
00:42:56,288 --> 00:42:59,322
An enum has to have discrete values. Same thing in Swift.
Swift 中也是这样做的
861
00:42:59,324 --> 00:43:02,258
It has a discrete value. So, for example, it might
它把值进行分类
862
00:43:02,260 --> 00:43:06,262
be a constant, or maybe it's a unary operation. Or it might
它可能是常量,一元操作
863
00:43:06,264 --> 00:43:10,466
be a binary operation, or many it's equals, the equal sign,
二元操作,也许是等号
864
00:43:10,468 --> 00:43:12,367
which is kind of a special operation, okay.
等号是一种特殊的操作
865
00:43:12,369 --> 00:43:15,970
So, enums are the same. What's different about enums in Swift
Swift 与其它语言不同的地方
866
00:43:15,972 --> 00:43:20,074
is that they're like classes in that they can have methods.
是它可以像 class 一样,拥有方法
867
00:43:20,076 --> 00:43:22,610
Okay? So, I can go down here and say func,
我可以在这里定义函数
868
00:43:22,612 --> 00:43:25,679
you know, something, take some arguments, return something.
设置参数或返回值
869
00:43:25,681 --> 00:43:29,916
I can do that down here. Okay? Enums are allowed to have
在 Swift 中,enum 有函数是合法的
870
00:43:29,918 --> 00:43:34,454
methods. Now, they can't have any vars. Okay? They can have
它不能有 var,但是它可以有
871
00:43:34,456 --> 00:43:37,290
computed vars, but they can't have any storage vars because
computed var(计算属性),但是它不能存储变量
872
00:43:37,292 --> 00:43:39,125
this is essentially their storage. Okay? The enum.
因为这些本质是它们的空间
873
00:43:39,127 --> 00:43:44,363
The other thing about them is they can not have inheritance,
enum 也不能继承
874
00:43:44,365 --> 00:43:46,431
so you can't have an enum that inherits
一个 enum 不能继承于另一个 enum
875
00:43:46,433 --> 00:43:49,100
from another enum, which probably would be weird
这看起来很奇怪
876
00:43:49,102 --> 00:43:51,903
anyway. So that is not much of a restriction. Okay?
其实这些约束也没什么的
877
00:43:51,905 --> 00:43:54,372
The other thing about enums is they're pass by
enum 是通过值传递的
878
00:43:54,374 --> 00:43:57,107
value and I am just going to post while I talk about that
之后我会给你们介绍 struct
879
00:43:57,109 --> 00:43:59,876
until I show you struct, which is another pass by value
它也是通过值传递的数据结构
880
00:43:59,878 --> 00:44:03,513
data structure, in a moment. Okay? So, here is operation,
这是 Operation 枚举
881
00:44:03,515 --> 00:44:07,150
that's great. So, now I can change pi, that's an operation
现在可以把 pi 改成 Operation.Constant
882
00:44:07,152 --> 00:44:12,555
dot constant. Okay? Comment that out for a second.
先把这里注释
883
00:44:12,557 --> 00:44:17,226
This is also an operation of constant. This is
这里也是 Operation.Constant
884
00:44:17,228 --> 00:44:21,797
an operation dot unary operation. Okay?
Operation.UnaryOperation
885
00:44:21,799 --> 00:44:26,634
And this is also an operation dot uniary operation. Okay,
这也是 Operation.UnaryOperation
886
00:44:26,636 --> 00:44:31,839
cool. So, we can now change this Double to the type
现在可以把 Double 改成 Operation 了
887
00:44:31,841 --> 00:44:37,311
operation. Okay? And errors go away. These are all operations
没有错误了。这就是 Operation
888
00:44:37,313 --> 00:44:40,848
and it all works. Now, small problem here is that,
这有个小问题
889
00:44:40,850 --> 00:44:45,452
we've lost track of the actual constants and, functions, and
我们现在丢掉了真正的 constant 和 function
890
00:44:45,454 --> 00:44:49,322
we've commented them out. They're not even involved
我们把它注释掉了,它们没有参与其中
891
00:44:49,324 --> 00:44:51,691
here. So, this obviously had not solved the problem.
这显然没有解决问题
892
00:44:51,693 --> 00:44:53,793
It's a step on the way to solving the problem, but
这只是解决问题中的一步
893
00:44:53,795 --> 00:44:57,496
it has not solved the problem. All right, the other thing is,
问题还在,还没解决
894
00:44:57,498 --> 00:45:01,767
obviously down here, looking up constants like this and
在这里,查询 constant
895
00:45:01,769 --> 00:45:02,834
making the cumulative, this doesn't work,
给 accumulator 赋值,都没用了
896
00:45:02,836 --> 00:45:05,636
this only works for constants, so we're not gonna do that.
它只对 constant 有效,我们就不那么做了
897
00:45:05,638 --> 00:45:08,472
So, how do we look things up now for operations?
所以我们怎样查找 operations 呢?
898
00:45:08,474 --> 00:45:11,241
Well, we're gonna do a similar thing here, okay,
这里我们做件相似的事情
899
00:45:11,243 --> 00:45:13,643
we're gonna say. Let, we can if,
对 operations[symbol]
900
00:45:13,645 --> 00:45:17,414
if let operation equal operations sub-symbol.
进行 if let 操作
901
00:45:17,416 --> 00:45:21,250
Okay? But, now this operation is going to be one of these.
但是现在这个操作,将要变成这其中之一
902
00:45:21,252 --> 00:45:23,252
Okay? It's going to be one of these enums, right?
变成这些 enum 中的一个
903
00:45:23,254 --> 00:45:23,385
If I click on it,
如果我点击它
904
00:45:23,387 --> 00:45:25,587
you see, it's a calculator brain dot operation.
它是 CalculatorBrain.Operation 类型的
905
00:45:25,589 --> 00:45:29,824
Yeah, notice also I defined this enum inside this class so
注意到,我把这个 enum 定义在了类中
906
00:45:29,826 --> 00:45:33,828
its full name is calculator brain dot operation.
所以它的全称是 CalculatorBrain.Operation
907
00:45:33,830 --> 00:45:35,196
You can nest these things.
你可以嵌套这些东西
908
00:45:35,198 --> 00:45:38,132
You can even put classes inside classes if you want and
如果你想你可以在一个类中内置另一个类
909
00:45:38,134 --> 00:45:40,000
they'll just, it's just a namespace thing right?
只是命名空间的区别
910
00:45:40,002 --> 00:45:44,838
The names will be whatever dot whatever dot whatever. Okay?
它的名字可能是 XXX.XXX.XXX
911
00:45:44,840 --> 00:45:45,805
So, I've got the operation there.
所以我有了这些操作
912
00:45:45,807 --> 00:45:48,507
Now, I'm going to switch on this operation, and
对它们进行 switch 操作
913
00:45:48,509 --> 00:45:52,211
I know that the cases can be constant. Okay?
我知道这些 case 是常量
914
00:45:52,213 --> 00:45:54,713
And, I'll just break on all these for now. So,
我先在这里加一个 break
915
00:45:54,715 --> 00:45:59,484
it could be a constant. It could be a unary operation.
它可能是一个常量,也可能是一元操作
916
00:45:59,953 --> 00:46:05,356
It could be a binary operation. Or
也可能是二元操作
917
00:46:05,358 --> 00:46:08,559
it could be equals. Okay?
也可能是等号操作
918
00:46:08,561 --> 00:46:12,029
And remembering switch I have to define every single option,
还记得我得为每一种可能的情况写 case 吗?
919
00:46:12,031 --> 00:46:14,664
but I don't need default here because there are only
但这里我不需要写 default
920
00:46:14,666 --> 00:46:17,767
four possible things that an operation could be. So,
因为这里只有四种可能
921
00:46:17,769 --> 00:46:21,671
I've got a case for all of them in my switch.
我已经在 switch 中全都处理了
922
00:46:21,673 --> 00:46:23,305
Question? >> Two things.
有问题吗?>> 两个问题
923
00:46:23,307 --> 00:46:26,141
Why is operation, are we not referring to the same
为什么是 operation?
924
00:46:26,143 --> 00:46:28,543
operation as the enum operation method?
我们为什么不用跟 enum 中相同的 Operation
925
00:46:28,545 --> 00:46:32,313
Because it's not capitalized. >> Yeah,
因为它没有大写
926
00:46:32,315 --> 00:46:33,247
this operation? >> Yeah.
这个 operation 吗?
927
00:46:33,249 --> 00:46:34,014
>> Not capitalized makes it
没有大写是因为它是一个局部变量
928
00:46:34,016 --> 00:46:36,450
a local variable, we're making it a local variable here yeah.
我们把它定义为了局部变量
929
00:46:36,452 --> 00:46:38,251
And, actually, that's a really good opportunity for
正好是个机会给你们讲讲
930
00:46:38,253 --> 00:46:41,054
me to talk about how you capitalize okay?
什么时候需要大写
931
00:46:41,056 --> 00:46:45,257
All types you want to be capitalized, like Calculator,
所有的类型都要大写, Calculator
932
00:46:45,259 --> 00:46:47,459
Brain, Dictionary, Operation, String, Double.
Brain, Dictionary, Operation, String, Double.
933
00:46:47,461 --> 00:46:49,094
Do you notice they all are capitalized?
注意到它们都是大写了的吗?
934
00:46:49,096 --> 00:46:51,663
Operate, everything, okay, is capitalized.
类型都是大写的
935
00:46:51,665 --> 00:46:55,767
All local variables and vars are lowercase first letter and
局部变量和变量的第一个字母都是小写的
936
00:46:55,769 --> 00:47:00,104
then capital letter for all the subsequent words in there.
后面每一个单词的开头都要大写
937
00:47:00,106 --> 00:47:01,672
So it's called Camel Case, you guys know of,
这种方式叫作驼峰式命名法
938
00:47:01,674 --> 00:47:05,909
have heard of that before? So, that's how you want to do
你们之前听说过吗?
939
00:47:05,911 --> 00:47:06,943
all your naming. If you don't do that,
你们最好都这么命名。但如果你不这么做
940
00:47:06,945 --> 00:47:09,479
you're going to get in trouble with me. Okay? So, I know some
我可能就会找你的麻烦了
941
00:47:09,481 --> 00:47:11,947
people like to use lower case for class names, forget it.
我知道有些同学喜欢用小写命名类的名字,以后不要这么做了
942
00:47:11,949 --> 00:47:13,582
You can't do it in Swift. Just don't do it, okay.
你不能在 Swift 中这么做。不要这么做了
943
00:47:13,584 --> 00:47:15,517
It'll be allowed, but you'll get in trouble, so
你可以不用这种方式,但你会遇到麻烦的
944
00:47:15,519 --> 00:47:17,519
don't do it. Okay? Well,
所以不要这么做了。好吗?
945
00:47:17,521 --> 00:47:21,155
you had a second question? >> Yeah.
你还有第二个问题?
946
00:47:21,157 --> 00:47:21,722
>> Why are we using the dot in
为什么在 constants 中使用 . 语法
947
00:47:21,724 --> 00:47:23,991
constants? Are we referring to operation dot,
我们是在引用 Operation.xx 吗?
948
00:47:23,993 --> 00:47:26,960
okay that's my confusion. >> So, why did I say dot
这就是我的问题
949
00:47:26,962 --> 00:47:30,096
constant here instead of just saying constant?
为什么要在 Constant 前面加 . 呢?
950
00:47:30,098 --> 00:47:33,499
And the answer is yeah, really we're doing operation dot
其实我们做的事情是 Operation.Constant
951
00:47:33,501 --> 00:47:36,969
constant, but Swift can infer that it must be operation
只不过 Swift 可以推导出来它是 Operation 类型的
952
00:47:36,971 --> 00:47:40,606
because it knows this is an operation. Okay?
因为它知道它就是个 Operation
953
00:47:44,845 --> 00:47:47,946
Is that because it's within the operation's Dictionary?
- 是因为它在一个 Operation 的字典中吗?
954
00:47:47,948 --> 00:47:48,846
>> Its part of the enum for
- 它是 Operation 中的一部分
955
00:47:48,848 --> 00:47:51,615
operation, you see, operation is not really,
不......
956
00:47:51,617 --> 00:47:53,584
we're not inside the Dictionary here. We pulled it
我们现在不在一个字典中
957
00:47:53,586 --> 00:47:56,853
already out of the Dictionary. >> So, how does it know,
我们把它从字典中取出来了。>>它怎么知道呢?
958
00:47:56,855 --> 00:47:59,222
is it intelligent enough Oo distinguish even though you
它足够智能到可以识别出它是 Operation 类型的吗?
959
00:47:59,224 --> 00:48:00,590
computed a lowercase operation,
即使你给它命名为小写的 operation
960
00:48:00,592 --> 00:48:02,458
that it's referring to the enum with
它和大写的 Operation 之间
961
00:48:02,460 --> 00:48:03,325
the uppercase operation? >> Okay.
也可以建立关联吗?
962
00:48:03,327 --> 00:48:06,828
It knows that this lowercase operation is a capital
Swift 知道这个小写的 operation 是大写的 Operation 类型的
963
00:48:06,830 --> 00:48:10,231
operation because I pulled it out of this Dictionary, and
是因为我把它从一个字典中取了出来
964
00:48:10,233 --> 00:48:13,634
it knows that that Dictionary has operations as its value.
并且它知道这个字典是以 Operation 作为 value 的
965
00:48:13,636 --> 00:48:15,302
So, when I pulled out its value, it knew it.
所以当我取值出来的时候,它知道它的类型
966
00:48:15,304 --> 00:48:19,907
Okay? There you go. All right. So this is all going good
懂了吧?代码很好
967
00:48:19,909 --> 00:48:23,310
except for, again, we don't have the pi and the e and
只不过我们还没有把
968
00:48:23,312 --> 00:48:24,411
the square root and the cosine in here.
pi,e,根号,cos 放进来
969
00:48:24,413 --> 00:48:26,979
So how are we gonna get those things in there? And
我们怎么才能把它们放进来呢?
970
00:48:26,981 --> 00:48:29,215
the answer is, you actually already know it.
答案是,你们其实已经知道了
971
00:48:29,217 --> 00:48:34,186
You've heard it before. Associated values. Okay?
你们之前就听过。使用 Associated values(关联值)
972
00:48:34,188 --> 00:48:37,322
Remember optional has that associated value. All
还记得 Optional 也有关联值吗?
973
00:48:37,324 --> 00:48:41,226
enums have associated value. In fact, optional is an enum.
enum 也可以有。实际上 optional 就是个 enum 类型
974
00:48:41,228 --> 00:48:45,930
Okay? This is what optional looks like if you were to look
Optional 就长这个样子
975
00:48:45,932 --> 00:48:51,902
at it. Enum Optional, case None, that's the nil case,
这是 nil 的情况
976
00:48:51,904 --> 00:48:56,173
case Some with associated value, T. And then,
这是有值的情况
977
00:48:56,175 --> 00:48:59,576
the optional is generic type. Just like Dictionary,
Optional 支持泛型,就像 Dictionary 一样
978
00:48:59,578 --> 00:49:03,246
it has this generic type. So this T could be any type, and
它也有泛型,T 可以是任何类型的
979
00:49:03,248 --> 00:49:05,782
that's how optional works. Okay?
这就是 Optional 的原理
980
00:49:05,784 --> 00:49:08,551
So, we can do the same thing down here, we could associate,
我们也可以在这里做相同的事情
981
00:49:08,553 --> 00:49:12,521
for example, a Double with constants. Okay?
我们可以把 Double 和 constants 关联起来
982
00:49:12,523 --> 00:49:14,189
Because constants need a Double, M under broad pi,
因为 Constants 需要 Double 类型的 M_PI
983
00:49:14,191 --> 00:49:17,459
we need that thing. Okay? And so, we're doing the same thing
在这里和 Optional 做相同的事情
984
00:49:17,461 --> 00:49:20,828
that optional does. Associating a value
给 Constants 关联上一个值
985
00:49:20,830 --> 00:49:24,065
with our constants. So, we have this constant Double,
所以我们已经关联上了一个 Double
986
00:49:24,067 --> 00:49:27,067
then here, when we declare the constant we have to provide
在这里,声明的时候
987
00:49:27,069 --> 00:49:29,236
the associated value which is m under bar pi.
把这个值和 M_PI 关联起来
988
00:49:29,238 --> 00:49:32,872
Okay? Now we can get rid of our comment there.
现在可以把注释删掉了
989
00:49:32,874 --> 00:49:35,942
Same thing here, we can take this M under bar E, and
这也一样,把 M_E 关联起来
990
00:49:35,944 --> 00:49:41,814
associate it with this constant, oops. Okay,
和这个 constant
991
00:49:41,816 --> 00:49:44,883
see how we're doing that association? Now,
这就是关联的方式
992
00:49:44,885 --> 00:49:48,453
how do we get this associated value out when we're
但是我们怎么把这个关联的值给取出来呢?
993
00:49:48,455 --> 00:49:51,756
looking at a constant down here? Right, here we switched
在 switch 这里
994
00:49:51,758 --> 00:49:54,959
on the operation, we know that this is a Constant, right?
我们已经知道它是一个 Constant 了
995
00:49:54,961 --> 00:49:57,561
We looked it up in the operations Dictionary.
我们从字典中取到了它
996
00:49:57,563 --> 00:49:59,163
And we found that it's a constant,
发现它是一个 constant
997
00:49:59,165 --> 00:50:01,298
let's say, like this one. How do we get it?
我们怎样获取它的值呢?
998
00:50:01,300 --> 00:50:07,237
You do that by right here saying, let associated,
你可以在这里,通过 let
999
00:50:07,239 --> 00:50:10,206
you know, constant, value, or whatever you want to call this
随便起一个名字
1000
00:50:10,208 --> 00:50:12,708
is, this is just a local variable, but you can call it
它只不过是一个局部变量
1001
00:50:12,710 --> 00:50:17,212
anything you want. Okay? That will make this local variable
你可以给它起任何名字,这会让这个局部变量
1002
00:50:17,214 --> 00:50:21,683
glom onto this associated value. okay? And
与这个关联值连接起来
1003
00:50:21,685 --> 00:50:26,388
so, now we can say accumulator equals the associated constant
所以现在我们可以说 accumulator 等于 associated constant
1004
00:50:26,390 --> 00:50:30,792
value. Okay? So, that's why I said switch is really
这就是为什么我说swift很强大
1005
00:50:30,794 --> 00:50:35,596
powerful it does this kind of pattern matching to get these
它能够处理这样一些模式匹配的工作
1006
00:50:35,598 --> 00:50:39,800
associated values out, so you do that with switch. Okay.
来取出关联的值,所以善加利用
1007
00:50:39,802 --> 00:50:42,469
Now, associated constant value, it's kinda yucky.
现在,这个名字有点冗长,我改成 value
1008
00:50:42,471 --> 00:50:44,838
I'm just gonna call it value. Okay. I only called it that
我改成value,只是为了展示
1009
00:50:44,840 --> 00:50:47,306
long thing just to show you it can be called anything and
它可以被取很多名字
1010
00:50:47,308 --> 00:50:50,142
that it is the associated value, but you can probably
但是的确是一个关联值,但是你可以叫它value
1011
00:50:50,144 --> 00:50:54,980
call it value. Okay, you got that? All right. Let's run and
知道了吗?运行一下
1012
00:50:54,982 --> 00:50:58,050
see if this works. It's only going to work for constants,
看是否工作,但它只对常数有用
1013
00:50:58,052 --> 00:51:00,819
cuz that's the only one we're, we've done any associated
因为我们目前只做了常数这个功能
1014
00:51:00,821 --> 00:51:03,488
values for yet. But here we go, this is still working,
看见了吗,它照常工作
1015
00:51:03,490 --> 00:51:07,692
Pi works, okay, square root, not implemented yet.
Pi起作用了,平方根,我们还没实现
1016
00:51:07,694 --> 00:51:11,929
All right? So, let's do square root, okay? So square root,
现在我们来做平方根
1017
00:51:11,931 --> 00:51:13,330
what would be the associated
那么平方根应该关联于
1018
00:51:13,332 --> 00:51:19,469
value of a unary operation? Don't be shy. What?
一个一元操作数? 不要害羞,什么?
1019
00:51:21,840 --> 00:51:25,241
A function yes. It's a function. Okay? So,
一个函数,对,它是一个函数
1020
00:51:25,243 --> 00:51:29,144
how do we make a function be associated value here? Well,
那么我们如何让一个函数称为关联值呢?
1021
00:51:29,146 --> 00:51:30,912
the lucky thing is that in Swift,
在swift种,幸运的是
1022
00:51:30,914 --> 00:51:35,150
functions are types just like any other type. Okay? There's
函数类型如同其他类型一样
1023
00:51:35,152 --> 00:51:38,953
no differences in Swift's mind between a function type and
在 swift 脑袋里,函数类型和 Double 类型没有任何区别
1024
00:51:38,955 --> 00:51:42,223
a Double. Exactly the same. It can be used in all the same
完全一样,在任何场景下都能这样认为
1025
00:51:42,225 --> 00:51:45,526
circumstances, arguments to functions, associated values,
函数的参数,关联值
1026
00:51:45,528 --> 00:51:49,529
local variables, anything can be of type, a function. And
局部变量,一个函数,任何东西都有类型
1027
00:51:49,531 --> 00:51:51,898
not only that, it's not a generic function,
不止如此,它不是一个普通函数
1028
00:51:51,900 --> 00:51:54,967
it's a function with certain arguments and return values.
而是一个拥有特性参数类型和返回值类型的函数
1029
00:51:54,969 --> 00:51:56,268
And how do you declare such a type?
那么我们如何声明这种类型呢?
1030
00:51:56,270 --> 00:52:00,072
How do you say that that's a type here? You just type it.
你如何表示这种类型? 你只需要打出来
1031
00:52:00,074 --> 00:52:04,009
So, this is a function that takes a Double and
那么这个函数有一个 Double 类型的输入
1032
00:52:04,011 --> 00:52:05,810
returns a Double. Okay?
和一个 Double 返回值
1033
00:52:05,812 --> 00:52:08,579
That's the associated value of unary operation.
这就是一元参数的关联值
1034
00:52:08,581 --> 00:52:12,516
It's a function. So, here when we want to associate a value,
它是个函数,这里我们想要一个关联值
1035
00:52:12,518 --> 00:52:16,487
we have to put In here, just like we put a Double here for
我把它放在这, 就像我放了一个 Double 在那一样
1036
00:52:16,489 --> 00:52:20,323
this one. All right? Here we have to put a function that
所以在这里我们放一个函数
1037
00:52:20,325 --> 00:52:24,561
takes a Double and returns a Double like, I don't know,
有一个 Double 输入, 返回一个 Double,我不知道
1038
00:52:24,563 --> 00:52:30,266
square root. Okay? Or maybe,
平方根,或者
1039
00:52:31,002 --> 00:52:37,506
cosine. Okay? Everybody got that?
cos ?大家都懂了吗?
1040
00:52:37,508 --> 00:52:38,774
Now, same thing down here.
现在同样的操作
1041
00:52:38,776 --> 00:52:41,176
We got to grab that associated value.
我们要取出关联值
1042
00:52:41,178 --> 00:52:45,446
So, here I'm going to say let and again I can say associated
在这我再 let 一个 asscociatedfuntisn
1043
00:52:45,448 --> 00:52:50,017
function, but I'm just going to call this function. Okay?
但我只打算叫它function
1044
00:52:50,019 --> 00:52:54,321
Now I have this is a local variable of type function that
现在我有了这个局部变量
1045
00:52:54,323 --> 00:52:57,524
takes a Double and returns a Double. That's its type. Okay?
它被输入一个 Double 然后返回一个Double,这就是它的类型
1046
00:52:57,526 --> 00:52:59,225
That's the type of this function. In fact, watch,
这就是这个函数的类型,事实上
1047
00:52:59,227 --> 00:53:04,964
I'll click on it. Look at it's type. It's a function that
看,我点击一下,看这个类型,就是
1048
00:53:04,966 --> 00:53:11,103
takes a Double and returns a Double.
输入一个 Double 返回一个 Double 的函数类型
1049
00:53:11,105 --> 00:53:15,207
How do I use a variable like that? Accumulator.
我怎么使用这个变量呢? Accumulator
1050
00:53:15,209 --> 00:53:17,242
Oops, not accessor. Accumulator. Okay?
搞错了,不是 accessor,是 Accumulator
1051
00:53:17,244 --> 00:53:21,780
Now, again this is just a local variable.
它只是一个局部变量
1052
00:53:21,782 --> 00:53:24,515
I could call this foo and then I would put foo here. Okay?
我可以叫它foo,然后我把foo放在这
1053
00:53:24,517 --> 00:53:26,717
Its just a local variable. That's all it is. And
它就只是一个局部变量
1054
00:53:26,719 --> 00:53:29,286
it happens to be, its type is a function that takes
它也只能是一个局部变量, 这个变量的类型
1055
00:53:29,288 --> 00:53:32,356
a Double, returns a Double. All right. Everybody cool with
就是 (Double)->Double,大家都懂了吗
1056
00:53:32,358 --> 00:53:36,760
that? All right, let's run again, see if this is working.
现在我们再运行一下,看看能否工作
1057
00:53:39,431 --> 00:53:44,433
All right so 81 square root, excellent. Okay?
81的平方根,太好了
1058
00:53:44,435 --> 00:53:47,670
Executing this associated value, it looked up that
执行了这个关联值
1059
00:53:47,672 --> 00:53:52,240
square root, found that it was an Unary Operation with this
它查询了平方根,发现平方根是对关联值的一元操作
1060
00:53:52,242 --> 00:53:55,710
associated value, went down here and performed operation.
走下来,执行这个操作
1061
00:53:55,712 --> 00:53:58,346
Found it here, grabbed that associated value,
看这里,提取了关联值
1062
00:53:58,348 --> 00:54:02,950
and then I used it to update my accumulator.
接着我用它更新了我的 accumulator
1063
00:54:02,952 --> 00:54:03,784
Question? >> So, so
什么问题?
1064
00:54:03,786 --> 00:54:07,888
you just specified the types and I'm surprised that you're
你只是说明了类型,但我惊讶的是
1065
00:54:07,890 --> 00:54:11,991
in, operation does not require [INAUDIBLE] because your
operation不需要 [听不见]
1066
00:54:11,993 --> 00:54:15,928
Dictionary could potentially just pull anything,
因为字典可以提取出任意类型
1067
00:54:15,930 --> 00:54:18,197
any kind of- >> [COUGH]
任意值吗
1068
00:54:18,199 --> 00:54:18,564
>> We know that Dictionary can
我们知道字典里只有一种值
1069
00:54:18,566 --> 00:54:21,400
only have an operation in it, right. You can only have one
那就是 operation,你只能有一种类型
1070
00:54:21,402 --> 00:54:25,203
of these and this only has four possible cases.
但这种类型有4种可能的情况
1071
00:54:25,205 --> 00:54:27,939
Even though any given case might have any associated
就算在任何情形下,可能有任意的关联值
1072
00:54:27,941 --> 00:54:30,841
value, it's still the actual case of that operation.
但它也必定属于这四种基本操作之一
1073
00:54:30,843 --> 00:54:32,009
There's only these four.
这就四种
1074
00:54:32,011 --> 00:54:33,443
So down here, when I switch on it,
所以看下来,我用下 switch
1075
00:54:33,445 --> 00:54:38,281
I only have to cover those four cases. No more. Okay?
我只需要搞定这四种情况
1076
00:54:38,283 --> 00:54:40,950
All right, what about binary operation?
那么二元操作呢?
1077
00:54:40,952 --> 00:54:45,121
Okay, well, binary operation, a little more complicated and
二元操作,更加复杂一点
1078
00:54:45,123 --> 00:54:48,023
why is it more complicated? Because if you think about
为什么更复杂? 因为你想想
1079
00:54:48,025 --> 00:54:51,893
the way a binary operation works like multiply, 3 times
比如乘法的操作,3*5
1080
00:54:51,895 --> 00:54:56,331
5 equals. Okay, when I press times I don't have enough
等于,okay,当我按下乘号的时候,我没有足够的信息
1081
00:54:56,333 --> 00:54:59,634
information to update the accumulator yet. I need the 3
来更新我的 accumulator,我需要那个3
1082
00:54:59,636 --> 00:55:02,436
and then the equals, it's only when the equals is hit that I
然后那个等号,只有当等号被按下
1083
00:55:02,438 --> 00:55:05,072
have enough information to actually do it. So,
我才有足够的信息来进行运算
1084
00:55:05,074 --> 00:55:06,106
in binary operation here,
那么对二元操作来说
1085
00:55:06,108 --> 00:55:11,044
I'm still going to grab that binary function out of there.
我还是需要取出二元函数
1086
00:55:11,846 --> 00:55:12,011
I can't actually perform that function like I can here. So
我不能像以前那样去实现
1087
00:55:12,013 --> 00:55:14,346
Okay? But,
1088
00:55:14,348 --> 00:55:18,617
I'm going to have to salt away that function and the operands
但我仍需要记录下函数和操作数
1089
00:55:18,619 --> 00:55:22,053
so far and wait til equals happens then I can do it.
目前为止,只有按下等号键我们才能开始运算
1090
00:55:22,055 --> 00:55:24,522
Okay? But, I still need, just like unary operation,
但我依然需要,像一元操作一样
1091
00:55:24,524 --> 00:55:27,491
I need to have an associated value with a binary operation.
我需要二元操作的关联值
1092
00:55:27,493 --> 00:55:32,896
What do you think that looks like? Another function,
你觉得应该看起来像什么?其他函数形式?
1093
00:55:32,898 --> 00:55:38,435
right, that takes two Doubles and returns a Double. Okay?
对了,接收2个 Double 返回一个 Double
1094
00:55:38,437 --> 00:55:39,235
Like multiply.
就像乘法一样
1095
00:55:39,237 --> 00:55:42,972
So, that's just a different kind of function. Okay?
不同地方是函数的类型不同
1096
00:55:42,974 --> 00:55:45,908
And so now I can go up here and add multiply, so
所以我现在上来添加乘法
1097
00:55:45,910 --> 00:55:50,412
let's go ahead and get the, a, the mathematical symbol for
让我们继续,从这个 emoji and symbols 中
1098
00:55:50,414 --> 00:55:52,948
multiply out of my emoji and symbols.
找出我们的乘号
1099
00:55:52,950 --> 00:55:56,551
Here it is right here. Multiply, okay? And
就在这,乘号
1100
00:55:56,553 --> 00:56:00,755
that is an operation tha's BinaryOperation.
这是一个二元操作
1101
00:56:00,757 --> 00:56:03,924
And look it wants a function that takes two Doubles and
它需要一个函数,两个 Double 输入
1102
00:56:03,926 --> 00:56:07,261
returns a Double. So I'm gonna put a function there called
返回一个 Double,所以我在这放一个函数
1103
00:56:07,263 --> 00:56:10,864
multiply, which doesn't exist in Swift, so I'm gonna have to
取名 multiply,swift没有自带,所以我打算写在这
1104
00:56:10,866 --> 00:56:14,901
go write that. By the way, we have another thing here
顺便提一下,这里还有个其他东西
1105
00:56:14,903 --> 00:56:19,105
which is equals, which is Operation.Equals, okay?
叫做等式,它也是一种操作
1106
00:56:19,107 --> 00:56:22,074
So i's a kind of a special operation there,
但是它有点特殊
1107
00:56:22,076 --> 00:56:25,444
okay? So, it's complaining here because multiply doesn't
这里报错是因为 mutiply 并不存在
1108
00:56:25,446 --> 00:56:28,780
exist, all right? So, I'm going to write multiply.
在这,我就写下 multiply 了
1109
00:56:28,782 --> 00:56:31,016
Here it is, I'm gonna make it a global function even,
这里,我打算把它写成全局函数
1110
00:56:31,018 --> 00:56:32,984
just like square root. Func multiply,
就像平方根函数一样,func multiply
1111
00:56:32,986 --> 00:56:35,753
okay, takes one argument that is a Double.
接收一个 Double 参数
1112
00:56:35,755 --> 00:56:37,721
Takes another argument tha's a Double.
再接受另一个 Double 参数
1113
00:56:37,723 --> 00:56:42,459
Returns a Double, and it just returns op1* op2,
返回一个 Double,返回的事 op1*op2
1114
00:56:42,461 --> 00:56:45,729
okay? So I've created this new function, multiply,
所以我写了这个新函数,mutiply
1115
00:56:45,731 --> 00:56:50,633
here it is, and I can now use it right here. Sound good,
就在这里,放在这我就可以用,很不错
1116
00:56:50,635 --> 00:56:55,371
you understand that? Question? >> Why is that
你明白了吗? 什么问题?
1117
00:56:55,373 --> 00:56:56,972
outside. >> Yeah, so
为什么写在外面?
1118
00:56:56,974 --> 00:56:57,539
why did I put this outside?
为什么我写在外面?
1119
00:56:57,541 --> 00:57:00,241
Because I wanted it be a function, a global function.
因为我想要一个函数,全局函数
1120
00:57:00,243 --> 00:57:04,345
Not a method in this class, okay? So I just wanted its
不是类中的一个方法,我希望它的作用范围
1121
00:57:04,347 --> 00:57:07,048
scope to be wider. >> So it's more of a style?
更大一点,那么这是一个编程风格问题?
1122
00:57:07,050 --> 00:57:07,848
>> Yeah, it's kind of a style.
对,是一种风格
1123
00:57:07,850 --> 00:57:09,850
A little more of a style thing. All right,
更多是一种编程风格的事儿
1124
00:57:09,852 --> 00:57:14,187
so so now we have this binary operation here,
那么现在我们有二元操作了
1125
00:57:14,189 --> 00:57:18,057
we have to salt away the binary function like times,
二元函数需要等待才能执行,比如乘法
1126
00:57:18,059 --> 00:57:22,561
and the accumulator so far, the 5, in 5 times 3 equals,
现在 accumulator 是5,然后把5乘上3
1127
00:57:22,563 --> 00:57:26,965
the 5 and the times we have to wait, salt them away. So
在“5”和乘号的时候我们必须等待,把它们存起来
1128
00:57:26,967 --> 00:57:29,768
I'm gonna salt them away in a data structure, and I'm,
我打算用一个数据结构来存储
1129
00:57:29,770 --> 00:57:32,837
gives me a chance to teach you another data structure.
我将教你们另一种数据结构
1130
00:57:32,839 --> 00:57:33,237
You know class,
你们知道类
1131
00:57:33,239 --> 00:57:37,041
you know enum, here's another one called struct. Okay?
知道枚举,还有一种叫做结构体
1132
00:57:37,043 --> 00:57:39,309
Now you know struct from other languages, of course.
你当然知道其它语言中的结构体
1133
00:57:39,311 --> 00:57:44,014
I'm gonna call this struct PendingBinaryOperationInfo,
我打算把这个结构体叫做 PendingBinaryOperationInfo
1134
00:57:44,016 --> 00:57:47,150
okay? And it's just gonna contain these two things I
他只包含我想要的2个东西
1135
00:57:47,152 --> 00:57:51,187
want. One of them is the binary function that I'm going
1136
00:57:51,189 --> 00:57:55,124
to do. What's the type of this? Something that
它是什么类型?
1137
00:57:55,126 --> 00:57:58,794
takes two Doubles and returns a Double, that's it's type.
接受2个 Double 返回一个 Double,这就是类型
1138
00:57:58,796 --> 00:58:00,095
See, I'm just declaring, that is a type,
看,我正在声明它,这就是类型
1139
00:58:00,097 --> 00:58:03,498
it's a type like any other type, like int, okay?
这种类型和其他类型,比如int,没有区别
1140
00:58:03,500 --> 00:58:06,000
We also need to keep track of the firstOperand for
对这个二元操作来说
1141
00:58:06,002 --> 00:58:09,403
this binary function, which is gonna be the accumulator so
我们需要记下第一个操作数,把它赋给 accumulator
1142
00:58:09,405 --> 00:58:12,106
far. And that's gonna be of type Double, okay?
它是 Double 类型的
1143
00:58:12,108 --> 00:58:16,510
Now, what is a struct? Okay, we know class, we know enum,
现在,什么是结构体?我们知道类,枚举
1144
00:58:16,512 --> 00:58:20,113
what's struct? Okay, struct is very much like class.
结构体呢? 结构体非常像类
1145
00:58:20,115 --> 00:58:24,050
Almost identical, okay? It can have vars, stored vars, and
几乎是一样的,它能有变量,存储变量
1146
00:58:24,052 --> 00:58:28,888
computed vars, no inheritance, okay? But the big difference
计算变量,但没有继承,但是
1147
00:58:28,890 --> 00:58:33,625
between struct and class, is that structs, like enums,
结构体和类最大的区别是,结构体和枚举一样
1148
00:58:33,627 --> 00:58:37,729
are passed by value, whereas classes are passed by
是按值传递的,类是按引用传递的
1149
00:58:37,731 --> 00:58:41,333
reference, okay? What does that mean? All right, so
这是什么意思?
1150
00:58:41,335 --> 00:58:45,003
passing something by reference means that that thing lives in
按引用传递就是它存储在堆里
1151
00:58:45,005 --> 00:58:47,972
the heap, okay, lives in memory somewhere, and
内存中的某个地方
1152
00:58:47,974 --> 00:58:49,707
when you pass it around to methods or
当你把它传递给某个方法
1153
00:58:49,709 --> 00:58:52,743
something like that, you're really passing a pointer
或其他什么的,你实际上传递的是它的指针
1154
00:58:52,745 --> 00:58:55,345
to it. And so, when you give it to someone else, they have
你把指针传递了给去
1155
00:58:55,347 --> 00:58:58,147
the same one you have, because you both just have a pointer
他们和你有同一个类,因为你们拥有的是同一个指针
1156
00:58:58,149 --> 00:58:59,849
to the same thing that lives in the heap.
都指向堆里存储的这个东西
1157
00:58:59,851 --> 00:59:02,384
That's passing by reference, okay? Hopefully you know that
这就是按引用传递,希望你有了解过这些计算机知识
1158
00:59:02,386 --> 00:59:04,853
much of computer science, that that's pass by reference,
这就是按引用传递
1159
00:59:04,855 --> 00:59:06,755
okay, and that's what it means in this scenario. So
这就是一种按引用传递的场景
1160
00:59:06,757 --> 00:59:08,322
if I had a class like calculator brain,
如果我有一个类,像 calculator brian
1161
00:59:08,324 --> 00:59:10,858
and I pass that brain around, I'm talking about the same
我到处传递它,那么我一直说的是同一个
1162
00:59:10,860 --> 00:59:14,028
calculator brain all the time. Now I can instantiate another
calculator brain,现在我可以实例化另一个
1163
00:59:14,030 --> 00:59:14,861
one in the heap and have a different one,
存储在堆中的类
1164
00:59:14,863 --> 00:59:18,665
but, but I'm pointing, when I create one I'm pointing to it,
但当我实例化时,指针指向了它
1165
00:59:18,667 --> 00:59:21,834
and I'm passing the pointer to it around. Pass by
传递时也传递的指针
1166
00:59:21,836 --> 00:59:26,038
value means that when you pass it, it copies it, okay?
按值传递的意思就是,当你传递时,它会进行拷贝
1167
00:59:26,040 --> 00:59:29,074
Some would think of it as it's being passed on this stack,
有些人会认为它在栈中被传递
1168
00:59:29,076 --> 00:59:30,208
the call stack of the function.
执行函数时会调用栈
1169
00:59:30,210 --> 00:59:32,577
But that's not necessarily how Swift implements it.
但这不是swift的实现方式
1170
00:59:32,579 --> 00:59:35,413
But the semantics of it, are that it is copied.
从语义上来说,是的它被拷贝了
1171
00:59:35,415 --> 00:59:38,816
So if you have a, let's say an array, which is a struct,
那么如果你有一个,比如说数组,它是一个结构体
1172
00:59:38,818 --> 00:59:42,453
okay? A Double is a struct, it turns out. An int is a struct.
Double 是一个结构体,实际上 Int 也是一个结构体
1173
00:59:42,455 --> 00:59:45,188
A String is a struct. These are all structs, okay?
String 也是结构体,它们全是结构体
1174
00:59:45,190 --> 00:59:48,525
And so if I passed an array to some other method, and
如果我把一个数组传递给一个方法
1175
00:59:48,527 --> 00:59:49,992
then I added something to that array,
接着我在数组上添加些什么
1176
00:59:49,994 --> 00:59:53,262
it would not be added back in the caller's array. The caller
它不会添加到原先的数组里
1177
00:59:53,264 --> 00:59:56,131
would have that array without that thing added, okay,
原先的数组不会被添加任何东西
1178
00:59:56,133 --> 00:59:59,368
cuz it would get a copy of it. Now you would think, whoa,
因为方法得到的是一个拷贝,现在回也许会想
1179
00:59:59,370 --> 01:00:00,769
this is gonna be really low performance,
哇,这样做真没效率
1180
01:00:00,771 --> 01:00:03,371
because what if I had an array of 10,000 items and
因为,如果我有一个10,000长的数组
1181
01:00:03,373 --> 01:00:06,474
I passed it, it's gonna copy 10,000 things. My God,
然后我传递它了,它岂不是要拷贝10,000个元素,我的天哪
1182
01:00:06,476 --> 01:00:09,877
my code is just gonna grind to a halt. No, Swift is really
我的代码会相当慢了,实际上
1183
01:00:09,879 --> 01:00:12,646
smart about when you pass a bi-valued struct,
当你按值传递的时候,swift相当聪明
1184
01:00:12,648 --> 01:00:15,915
it doesn't actually make a copy of it until you try and
它不会真的进行拷贝,直到你真的试图使用它为止
1185
01:00:15,917 --> 01:00:19,452
touch it, okay? If you try and mutate it, then it'll make
如果你试图更改,那么它会进行必要的拷贝
1186
01:00:19,454 --> 01:00:22,455
a copy as necessary, maybe not even a full copy, but
甚至不会拷贝全部元素
1187
01:00:22,457 --> 01:00:26,225
it'll mutate it. So if you're passing something and
接着它会被改变,所以如果你传递了某些东西但又不使用
1188
01:00:26,227 --> 01:00:29,027
you don't touch it, then you are gonna be sharing it, okay?
那么它不会被拷贝的
1189
01:00:29,029 --> 01:00:32,464
But, all of that is behind the scenes performance
不过,你们的确不知道
1190
01:00:32,466 --> 01:00:34,232
enhancement, you don't know anything about it.
很多场景背后的性能优化
1191
01:00:34,234 --> 01:00:37,301
From your point of view it copies it. Structs always get
你们会认为他会拷贝,结构体总是被拷贝
1192
01:00:37,303 --> 01:00:40,871
copied, okay? Understand the difference there?
知道其中区别了吗
1193
01:00:40,873 --> 01:00:43,173
A very important difference between structs and classes.
结构体和类的这个差别相当重要
1194
01:00:43,175 --> 01:00:45,141
And enums are like structs. All right, so
枚举于结构体相似
1195
01:00:45,143 --> 01:00:49,178
I've got this right here. Now, notice that I didn't set these
现在我们看看她,发现,我没有初始化
1196
01:00:49,180 --> 01:00:52,348
equal to anything, but I didn't get that warning,
但也没给我任何警告
1197
01:00:52,350 --> 01:00:57,553
no initializers. Now usually if I put a var in a class,
没有 initializer,通常在类里如果我的一个 var
1198
01:00:57,555 --> 01:00:58,587
if I don't put initial, then it says,
不进行初始化,那么会报错说
1199
01:00:58,589 --> 01:01:01,423
no initializers, right? So, why is it not saying here?
没有 initializer,那么为什么这里没报错?
1200
01:01:01,425 --> 01:01:05,560
That's because for structs, unlike classes, classes we got
这也是结构体不同于类的一个地方,类里我们有一个
1201
01:01:05,562 --> 01:01:09,230
a free initializer. What were the arguments to it? Nothing,
默认 initializer,它有什么参数呢?没有参数
1202
01:01:09,232 --> 01:01:11,865
right? Like calculator brain, parenthesis, no arguments. So,
如同 calculation brian,括号,没有参数
1203
01:01:11,867 --> 01:01:13,433
that's the free initializer you get for classes.
这就是类里默认的一个 initializer
1204
01:01:13,435 --> 01:01:16,302
For struct, the free initializer you get,
在结构体中,你的 initializer
1205
01:01:16,304 --> 01:01:20,940
is an initializer that, whose arguments are all of its vars,
的参数是这个结构体里所有的变量
1206
01:01:20,942 --> 01:01:24,176
every one of its vars, okay? So let's go ahead and
每一个变量,现在我们继续
1207
01:01:24,178 --> 01:01:26,245
call that, because here in BinaryOperation,
调用他,因为这里是一个二元操作
1208
01:01:26,247 --> 01:01:29,348
I need to create one. So I'm gonna create a private var
我需要新建一个,所以我新建一个私有变量
1209
01:01:29,350 --> 01:01:32,951
here. I'm gonna call it pending. It's gonna be of type
我打算叫它 pending,它的类型是
1210
01:01:32,953 --> 01:01:38,756
PendingBinaryOperationInfo, and it's gonna be an optional.
PendingBinaryOperationInfo,并且是一个可选类型
1211
01:01:38,758 --> 01:01:40,691
So here I am creating my first optional.
那么我建立了第一个可选类型
1212
01:01:40,693 --> 01:01:45,462
It's an optional struct, okay? Why am I making this optional?
是一个可选结构体,为什么我要让他是可选类型?
1213
01:01:45,597 --> 01:01:47,997
Because this PendingBinaryOperationInfo is
因为这个 PendingBinaryOperationInfo 只在我执行二元操作
1214
01:01:47,999 --> 01:01:51,234
only there if I have a pending binary operation.
的时候才有值
1215
01:01:51,236 --> 01:01:53,502
If I haven't typed times or divide or something,
如果我没有输入乘号或者除号什么的
1216
01:01:53,504 --> 01:01:56,739
I don't have one of these, so I want this to be nil, okay?
我没有这些东西我的话,我希望它是 nil
1217
01:01:56,741 --> 01:02:00,375
I want this pending var that's holding this pending stuff,
我希望这个变量在这个时刻
1218
01:02:00,377 --> 01:02:02,677
to be nil at that point. And then when I have one,
是 nil 的,来表示一种没有输入的状态
1219
01:02:02,679 --> 01:02:04,612
I'll set it to something, okay? And
当我有了输入后,我希望它被设置成某些值
1220
01:02:04,614 --> 01:02:06,714
that's exactly what I'm gonna do here in BinaryOperation,
这就是接下来我要做的事
1221
01:02:06,716 --> 01:02:11,518
I'm gonna say, pending. That's this thing right here, okay,
我叫它 pending,就是这个东西
1222
01:02:11,520 --> 01:02:13,520
= PendingBinaryOperationInfo.
等于 PendingBinaryOperationInfo
1223
01:02:13,522 --> 01:02:19,592
And when I open parentheses, look. I got a constructor for
当我输入括号,看,我有了构造函数
1224
01:02:19,594 --> 01:02:23,629
this PendingBinaryOperation that has these two things
这里有两个参数
1225
01:02:23,631 --> 01:02:26,432
as its two arguments. See, binaryFunction, and
看, binaryFunction
1226
01:02:26,434 --> 01:02:30,736
firstOperand. So now I can apply these values, this is
和 firsyOperand,现在我传入 function
1227
01:02:30,738 --> 01:02:36,108
function, and the firstOperand is the accumulator. Okay, so
给 firstOperand 传入 accumulator
1228
01:02:36,110 --> 01:02:40,478
now I've created one of these pending hoo-has right here,
所以我建立起了这样的功能
1229
01:02:40,480 --> 01:02:43,014
and that's all I've done. I pressed time, but
我就做了这些,我按下乘号
1230
01:02:43,016 --> 01:02:46,183
I'm doing 5 times 3 equals, I press the times, all I did was
我计算5*3,等号,我按下乘号,我所做的
1231
01:02:46,185 --> 01:02:49,086
create one of these structs, and put the times and
就是建立了这样一个结构体,按下乘号
1232
01:02:49,088 --> 01:02:52,889
the 5 in there. Now in Equals, right here,
再按下5,现在看这里的 Equals
1233
01:02:52,891 --> 01:02:56,926
I'm gonna say if pending != nil. So
我想说,如果 pending 不为 nil
1234
01:02:56,928 --> 01:03:00,963
if I have a pending operation, then I'm going to evaluate it.
那么我就在做 pending 操作,接着我再计算
1235
01:03:00,965 --> 01:03:04,000
So 5 times 3 equals works, but if I just say 5 equals, I
所以5*3起作用,但如果我按下5和等号
1236
01:03:04,002 --> 01:03:07,903
don't have any pending times, so I'm just gonna ignore this.
我没有输出乘号,那么我就选择忽略它
1237
01:03:07,905 --> 01:03:10,172
So I'm only gonna do this if I have a pending one, and
所以如果我在 pending 的话
1238
01:03:10,174 --> 01:03:14,242
what am I gonna do? Well, I'm gonna set my accumulator =,
我会怎么做呢?我会把 accumulator设置成
1239
01:03:14,244 --> 01:03:18,813
evaluating that pending function, which is pending!,
pending 函数的结果,就是给 pending 拆包
1240
01:03:18,815 --> 01:03:22,283
because it's an optional, .binaryFunction.
因为它是可选类型,.binaryFunction
1241
01:03:22,285 --> 01:03:27,921
Called with the arguments of the pending!.firstOperand, and
用 pending!.firstOperand 和
1242
01:03:27,923 --> 01:03:33,093
my current accumulator. Okay,
和当前 accumulator 作为参数调用
1243
01:03:33,095 --> 01:03:35,228
and now pending is nil, because I no longer,
现在 pending 是nil,因为
1244
01:03:35,230 --> 01:03:36,796
I just handled that pending thing, so
我不再需要处理接下来的输入
1245
01:03:36,798 --> 01:03:40,266
now I no longer have a pending operation anymore.
所以我这里不再有 pending 操作了
1246
01:03:40,901 --> 01:03:44,870
That make sense? Okay, so let's go take a look and
理解吗? 现在看看它是否工作
1247
01:03:44,872 --> 01:03:52,811
see if this works. All right, here we go. Let's try,
看,看这里,我们试试
1248
01:03:52,813 --> 01:03:55,446
we don't have a times button. Let's go back to our UI and
我们还没有乘号按键,回到我们的UI
1249
01:03:55,448 --> 01:03:59,150
add a times button [LAUGH]. In fact, we'll add of our binary
加上乘号,事实上,我们应该把所有二元操作按键加上
1250
01:03:59,152 --> 01:04:04,054
operations here. Okay, so, I'm gonna just copy and paste.
那么接下来我就是复制粘贴
1251
01:04:04,056 --> 01:04:08,225
Put this here, paste another one. Here, we'll put all my
放在这里,粘贴一个,我打算把所有二元操作
1252
01:04:08,227 --> 01:04:13,029
binary operations across the top here, paste, okay.
都放在顶上这一栏
1253
01:04:13,031 --> 01:04:17,300
Go here, we'll go to our emoji and symbols things.
在这里,emoji and symbols
1254
01:04:17,302 --> 01:04:21,904
Here's times. We'll put divide right next to times.
这里是乘号,我们把除号放在旁边
1255
01:04:21,906 --> 01:04:27,443
We'll put plus right here. We'll put minus right here.
加号放在这里,减号放在这里
1256
01:04:27,445 --> 01:04:30,145
We can put some other buttons down here too, like maybe
下面也可以放一些按键
1257
01:04:30,147 --> 01:04:34,215
we'll put, cosine, yeah, let's put cosine in there.
比如,放一个 cos,放在那
1258
01:04:34,217 --> 01:04:39,520
Let's cosine. Did I have another one? E I guess I had,
再放一个吧,我觉得应该再放一个 E
1259
01:04:39,522 --> 01:04:44,592
right? Put E in there too. E.
这? 把 E 也放在这
1260
01:04:44,594 --> 01:04:48,696
Let's also put a equal sign. Gotta have that.
还要放一个等号
1261
01:04:48,698 --> 01:04:52,799
We'll put it down here. Equal sign. And here, in this empty
就这里了,等号,在这里
1262
01:04:52,801 --> 01:04:55,702
space is just begging for me to put something there.
这个空白简直要逼死强迫症
1263
01:04:55,704 --> 01:04:59,572
I'm gonna put period. Because in your homework,
我打算放一个”.”在这,因为在你们的作业里
1264
01:04:59,574 --> 01:05:01,540
you're gonna have to add the capability to be able to
你们需要给计算器加上使用浮点数
1265
01:05:01,542 --> 01:05:04,209
enter floating point numbers. So you'll need this one.
这个功能,所以你需要它
1266
01:05:04,211 --> 01:05:07,779
So I'll put it there for you. Okay? All right, so here's our
我就把它放在这了,这就是我们的UI
1267
01:05:07,781 --> 01:05:12,283
nice UI. Looks really pretty. And let's run it.
是挺漂亮的,运行一下
1268
01:05:16,856 --> 01:05:22,026
All right, let's try 4 x 5 =, woo hoo, it works.
现在我们试试 4 x 5 =,哇,起作用了
1269
01:05:22,028 --> 01:05:23,660
A miracle the first time. Okay,
没有 bug 的奇迹啊
1270
01:05:23,662 --> 01:05:24,728
let's try something a little complicated.
现在试试更复杂一点的
1271
01:05:24,730 --> 01:05:26,563
How about this? Well, let's do some other things.
这个怎么样,比如说
1272
01:05:26,565 --> 01:05:29,065
The square root's still working, cosine, let's play pi
平方根也可以使用,cos,我们算算 pi
1273
01:05:29,067 --> 01:05:33,803
cosine. That worked, cool. All right, there's E, 2.71, nice.
的cos值,也是对的,酷,好的,现在是 E,2.71,漂亮
1274
01:05:33,805 --> 01:05:36,538
Here's something that doesn't work though, watch this.
但是还是有些运算不支持,看
1275
01:05:36,540 --> 01:05:38,874
7 x 8 x 2 x 3, uh-oh,
7 x 8 x 2 x 3,啊哦
1276
01:05:38,876 --> 01:05:43,244
this is not working. And this is not working because it's
不起作用了,原因是因为
1277
01:05:43,246 --> 01:05:46,581
requiring me to press equals to evaluate minor operations.
它需要我按下等号键来计算等式
1278
01:05:46,583 --> 01:05:51,252
So I have to go 4 x 7= x3 = x8 =, really, what I want is
所以我需要这样 4 x 7= x3 = x8 =,我想要的是
1279
01:05:51,254 --> 01:05:55,823
an automatic equals anytime I press a binary operation.
在我做二元运算的任意时刻,它都能自动替我按下等号
1280
01:05:55,825 --> 01:06:00,927
So I can go 4 x 5, and when I go times, it doesn't equals.
那么我就可以做 4 x 5,接着按乘号,不用按等号
1281
01:06:00,929 --> 01:06:04,898
And then let me do it. So let's just do that real quick.
让我来飞快的完成它吧
1282
01:06:04,900 --> 01:06:09,202
Let's go back to our brain. I'm just gonna take this
回到我们的 brain,我打算把一块
1283
01:06:09,204 --> 01:06:11,070
little thing that equals uses and
equals的实现,用一个函数来完成
1284
01:06:11,072 --> 01:06:15,207
make it into a function, private func, we'll call it
并且是私有函数,我们打算叫它
1285
01:06:15,209 --> 01:06:20,045
executePendingBinaryOperation. It's just gonna,
executePendingBinaryOperation,只是
1286
01:06:20,047 --> 01:06:21,913
I'm just pasting in the exact same thing.
单纯的复制粘贴
1287
01:06:21,915 --> 01:06:26,084
I'm gonna call that here, executePendingBinaryOperation.
我在这调用它,executePendingBinaryOperation.
1288
01:06:26,086 --> 01:06:33,124
And I'm also gonna call it here. Okay, I personally like,
也在这调用它,因为我个人习惯
1289
01:06:33,126 --> 01:06:35,359
if I have any of my cases that go onto second line,
如果有任何一个 case 在第二行
1290
01:06:35,361 --> 01:06:37,928
I like to put them all there. I just think it looks a little
我就会把所有 case 都放在第二行,比起全部堆在一行里
1291
01:06:37,930 --> 01:06:40,630
nicer than having sum on the end. It doesn't really matter
更好看,这在swift里没多重要
1292
01:06:40,632 --> 01:06:44,600
to SWF, but I just think this looks a little nicer. Okay, so
我只是觉得好看一些而已
1293
01:06:44,602 --> 01:06:47,470
we did that. So that'll fix that case.
所以我要更改所有的 case
1294
01:06:48,739 --> 01:06:53,108
All right, the last two things I wanna do,
最后两件我要做的
1295
01:06:53,110 --> 01:06:58,747
okay let's take this 4 x 7 x 8 x 9 = okay.
就是做 4 x 7 x 8 x 9 =
1296
01:06:58,749 --> 01:07:01,115
Now the last thing, two things I wanna do is one,
最后两件我想要做的事情
1297
01:07:01,117 --> 01:07:05,453
I'm gonna show you how to make, divide and plus and
就是教给你怎么如何实现除法,加法,减法
1298
01:07:05,455 --> 01:07:08,922
minus work without creating a whole ton of these little
甚至不需要一堆
1299
01:07:08,924 --> 01:07:12,626
extra functions like multiply cuz that's really gross. And
像乘法这样的函数,因为这么做很恶心
1300
01:07:12,628 --> 01:07:15,161
then second, I'm gonna make our UI stretchy, so
第二个,我要让UI更灵活
1301
01:07:15,163 --> 01:07:16,429
it works with landscape and portrait.
让它在横屏和竖屏都能工作
1302
01:07:16,431 --> 01:07:19,098
Okay, those are the two things I'm gonna do. All right,
这就是最后的两件事情
1303
01:07:19,100 --> 01:07:23,936
so let's go ahead and make our other four operations here,
我们继续,实现另外几个操作
1304
01:07:23,938 --> 01:07:25,270
which is divide, plus and minus.
除法,加法,和减法
1305
01:07:25,272 --> 01:07:29,541
So I have to bring up our emoji again. Okay, so
我需要吧 emoji 调出来
1306
01:07:29,543 --> 01:07:34,412
this one will make the divide. We'll make this one be plus.
这个是除法,这个做成加法
1307
01:07:34,414 --> 01:07:39,050
And we'll make this one be minus. Okay,
这一个是减法
1308
01:07:39,052 --> 01:07:42,686
now here I could have a function called divide, and
现在我可以写一个函数叫做 divide
1309
01:07:42,688 --> 01:07:46,323
another one called add, another one called subtract.
再写一个叫做 add,再写一个叫做 substract
1310
01:07:46,325 --> 01:07:49,092
And then, I could go up here and make one of these for
接着,我可以走上来,实现这几个操作
1311
01:07:49,094 --> 01:07:51,127
divide, and one of these for add and one. Okay, but
一个除法,一个加法
1312
01:07:51,129 --> 01:07:54,263
if I start doing that, what a mess. Okay, I-I've hardly even
但如果我真的这么做了,一团糟,这样做
1313
01:07:54,265 --> 01:07:56,799
gained anything by having this nice table of operations,
似乎并没有善加利用这张 operations 表
1314
01:07:56,801 --> 01:07:58,400
if I also have to have a separate function for
如果我必须要有不同的函数去实现不同功能
1315
01:07:58,402 --> 01:08:02,404
everything I want to do. Well, SWF is gonna take care of us
swift有办法解决这样的需求
1316
01:08:02,406 --> 01:08:04,939
on that front because it implements closures.
因为swift里实现了闭包
1317
01:08:04,941 --> 01:08:06,340
How many people know what closures are,
你们多少人知道闭包是什么?
1318
01:08:06,342 --> 01:08:08,842
the computer science term closures? Okay,
计算机科学术语,闭包?
1319
01:08:08,844 --> 01:08:12,279
hardly anybody, whoo, okay. So a closure is basically,
好吧,几乎没有,其实闭包基本上
1320
01:08:12,281 --> 01:08:15,015
you can think of it as an inline function,
可以被看作是一个内嵌函数
1321
01:08:15,017 --> 01:08:17,483
okay? But it's an inline function that captures
但这个内嵌函数捕捉环境中的状态
1322
01:08:17,485 --> 01:08:20,319
the state of it's environment. And we're gonna see why
我们会看到
1323
01:08:20,321 --> 01:08:21,553
that's important later in the quarter. But, for
在课程后期它为什么那么重要
1324
01:08:21,555 --> 01:08:25,524
now, you can just focus on the inlined function part of it.
现在,我们只关注内嵌函数这部分
1325
01:08:25,526 --> 01:08:29,527
So, I can actually take this multiply function, okay? I'm
现在我选中 multiply 函数的这部分
1326
01:08:29,529 --> 01:08:32,964
just gonna select the function without the word multiply. And
只是选中它们,除了 multiply
1327
01:08:32,966 --> 01:08:39,670
I'm gonna cut, and I'm gonna paste it right in here. Okay?
然后剪切,然后粘贴在这里
1328
01:08:39,672 --> 01:08:43,407
Now, I can't quite do that. I have to do two things. One, I
实际上我不能直接这样,还有两件事要做
1329
01:08:43,409 --> 01:08:47,911
have to take this curly brace and put it at the beginning so
第一,我要把大括号放在开头
1330
01:08:47,913 --> 01:08:51,981
that the arguments here is inside the curly brace,
让参数被包含在大括号里
1331
01:08:51,983 --> 01:08:56,052
because the whole closure has to have curly braces beginning
因为闭包是以大括号开始和结束的
1332
01:08:56,054 --> 01:08:59,021
to end. And then, since I need to separate this
接着,为了区分前面和后面
1333
01:08:59,023 --> 01:09:03,325
from the rest, I also put the word in right there. Okay, so
我需要在中间放入一个关键词 in
1334
01:09:03,327 --> 01:09:04,359
that's how you make a closure.
这就是如何使用闭包
1335
01:09:04,361 --> 01:09:05,727
It's exactly the same as a function, except for
和函数是一样的,除了
1336
01:09:05,729 --> 01:09:08,596
the curly brace starts before the arguments and you put in
大括号从参数前开始,并且参数后你要放一个 in
1337
01:09:08,598 --> 01:09:13,701
after the arguments. Got that? Now, this doesn't look, so
知道了吗?所以
1338
01:09:13,703 --> 01:09:15,402
that I don't need function multiply any more.
我不再需要这个 multiply 函数了
1339
01:09:15,404 --> 01:09:18,138
So this doesn't look that much better. It's still kind
这个看起来好多了,但还是有点乱
1340
01:09:18,140 --> 01:09:23,143
of a mess. But we're gonna use type inference, yeah, to make
但是我们可以使用类型推断,让它看起来
1341
01:09:23,145 --> 01:09:27,413
this look a lot better. Now, remember that SWF knows that
更漂亮,记住,swift 知道这个二元操作
1342
01:09:27,415 --> 01:09:30,883
this binary operation takes a Double, two Doubles, and
需要两个 Double 输入,两个 Double
1343
01:09:30,885 --> 01:09:36,388
returns a Double. So this is all redundant, okay?
然后返回一个 Double,所以这是多余的
1344
01:09:36,390 --> 01:09:39,991
SWF can infer that that's a Double.
swift能推断这是个 Double
1345
01:09:39,993 --> 01:09:40,658
It can infer that's a Double.
也能推断它是 Double
1346
01:09:40,660 --> 01:09:43,728
And it can infer that it re-returns a Double. So
也能推断返回类型是一个 Double
1347
01:09:43,730 --> 01:09:47,131
this is wow, all of a sudden looking a lot better already,
那么,哇,突然间看起来好多了
1348
01:09:47,133 --> 01:09:51,702
okay? We can make this look all in one line. Okay,
我们可以写成一行了
1349
01:09:51,704 --> 01:09:55,272
that's looking pretty good. I mean, that alone is probably
看起来很不错,这看起来
1350
01:09:55,274 --> 01:09:59,142
really, really good. However, it gets better than that,
依旧相当不错了,但是呢,我们还可以精简
1351
01:09:59,144 --> 01:10:02,445
because closers also can have default arguments.
因为闭包拥有默认参数名
1352
01:10:02,447 --> 01:10:06,682
The default argument names are $0, $1, $2,
默认参数名是,$0,$1,$2
1353
01:10:06,684 --> 01:10:09,218
$3, however many arguments it has.
$3,表示有多少个参数
1354
01:10:09,220 --> 01:10:12,054
So you can put those as the names of the arguments
比可以把它们当作参数名是用
1355
01:10:12,056 --> 01:10:15,724
instead of having op one or op two whatever you
而不是 op1 或者 op2
1356
01:10:15,726 --> 01:10:16,657
want to call it. Okay?
这种你起的名字
1357
01:10:16,659 --> 01:10:21,028
And if you do this, then you don't even need that. Okay?
如果你这么做,你甚至不需要它了
1358
01:10:21,030 --> 01:10:26,200
If you use the $0 and $1, and since this is a Double, and
如果你用 $0 和 $1,因为它们是 Double
1359
01:10:26,202 --> 01:10:29,469
SWF can infer that this return's a Double, you don't
swift能推断它返回了一个 Double
1360
01:10:29,471 --> 01:10:33,106
need return right here. >> [LAUGH]
你不需要返回了 >>[笑]
1361
01:10:33,108 --> 01:10:33,673
>> Okay?
Okay?
1362
01:10:33,675 --> 01:10:34,740
>> [LAUGH]
[笑]
1363
01:10:34,742 --> 01:10:34,973
>> So we've cleaned up our
我们收拾干净了这代码
1364
01:10:34,975 --> 01:10:40,912
code quite a bit. And in fact, now divide is just this. And
事实上,除法也如此,加法亦然
1365
01:10:40,914 --> 01:10:47,118
add is just this. And subtract is just this.
减法也是一样
1366
01:10:47,253 --> 01:10:51,055
Okay? So that's closures. Super powerful.
这就是闭包,非常强大
1367
01:10:51,057 --> 01:10:53,390
Used a lot in the iOS API. You're going to be
iOS API里用了很多,你们以后
1368
01:10:53,392 --> 01:10:56,893
able to use it in your code to your heart's content.
在代码中会把它当做心头好的
1369
01:10:56,895 --> 01:11:02,799
It's very fun. Let's make sure it actually
非常有趣,验证一下它们是否工作
1370
01:11:02,801 --> 01:11:07,203
works. All right, 7 x 8 = all right,
7 x 8 = 对的
1371
01:11:07,205 --> 01:11:12,908
divided by 5 equals? Looks good. minus nine equals?
除以5等于? 对的,减去9等于?
1372
01:11:12,910 --> 01:11:17,713
All right, so all of our things here are working. Okay?
对的,所以所有东西都能工作了
1373
01:11:17,715 --> 01:11:21,483
We also could, so we can do that for
我们也能对一元操作
1374
01:11:21,485 --> 01:11:23,485
our UnaryOperations as well. What if we wanted, for
这样做,如果我们想的话
1375
01:11:23,487 --> 01:11:27,188
example, something like change sign. Let's go find something
比如,反号,我来找一下这个符号
1376
01:11:27,190 --> 01:11:30,458
to be change sign. How about this? That's not really
这个怎么样,虽然并不是
1377
01:11:30,460 --> 01:11:33,360
[LAUGH] a change sign symbol, but I'm gonna use it for that.
真正的反号,但就这样吧
1378
01:11:33,362 --> 01:11:36,563
I could say change sign is Operation.constant, or
这个反号是 Operation.constant
1379
01:11:36,565 --> 01:11:39,499
.UnaryOperation, sorry. UnaryOperation and
或者.UnaryOperation,不好意思 UnaryOperation
1380
01:11:39,501 --> 01:11:43,503
I need a function that takes a Double returns a Double,
我需要一个函数接受一个 Double,返回一个 Double
1381
01:11:43,505 --> 01:11:48,574
how about -$0. Okay, that changes the sign of
-$0 如何? 这可以让参数反号
1382
01:11:48,576 --> 01:11:53,679
the one argument. And Swift is smart enough to know that this
swift 相当聪明,知道这只有一个参数
1383
01:11:53,681 --> 01:11:58,550
has one argument. And that it is returning that argument and
也知道返回参数,这个一元操作
1384
01:11:58,552 --> 01:12:00,752
that unary operation is Double Double, so
是一个 Double -> Double
1385
01:12:00,754 --> 01:12:05,790
it knows that this must be a Double. It'll even infer that.
它知道这必然是 Double,能被推断出来
1386
01:12:05,792 --> 01:12:09,326
Okay. All right, so that's it for our calculator brain.
好了,这就是我们的 calculator brain 了
1387
01:12:09,328 --> 01:12:11,962
And if we look back at our calculator brain and
回头看看我们的 calculator brain
1388
01:12:11,964 --> 01:12:13,864
the code in it. All the code
看下它的代码
1389
01:12:13,866 --> 01:12:15,532
here has nothing to do with UI.
这些代码都与 UI 无关
1390
01:12:15,534 --> 01:12:19,969
It's purely about calculating and it's super-extensible.
只是单纯地计算,并且非常易扩展
1391
01:12:19,971 --> 01:12:22,805
If you want to add more operations here, all you need
你如果想加入更多操作
1392
01:12:22,807 --> 01:12:26,308
to do is to provide the type of operation and
只需要提供操作类型
1393
01:12:26,310 --> 01:12:28,110
what's specific to that operation.
和操作的一些细节了
1394
01:12:28,112 --> 01:12:30,845
All the calculation is done is this very simple
这些计算都是非常简单的函数
1395
01:12:30,847 --> 01:12:33,881
function right here, the only complexity of which is this
唯一复杂的就是二元操作 pending 这部分
1396
01:12:33,883 --> 01:12:37,351
pending binary operation thing we have to do. By the way,
不过它是很必要的,另外
1397
01:12:37,353 --> 01:12:41,889
this right here, this struct, should also be private. Okay,
这里的 struct,也因该是私有的
1398
01:12:41,891 --> 01:12:44,458
this struct which is calculatorBrain.PendingBinary-
这个 calculatorBrain.PendingBinaryinfo 结构体
1399
01:12:44,460 --> 01:12:47,127
Info, that's its full name, that should be private as
它的全名,它该是私有的
1400
01:12:47,129 --> 01:12:50,063
well, because we're only using that internally.
因为我们只在内部使用
1401
01:12:50,065 --> 01:12:52,398
Same thing with this operation.
这个 Operation 也是一样
1402
01:12:52,400 --> 01:12:52,664
It should be private.
应该是私有的
1403
01:12:52,666 --> 01:12:55,634
Cuz we're not using it in our public API and same thing
因为我们无需公布它的 API,
1404
01:12:55,636 --> 01:12:59,437
with this operation, should be private. Okay, should make
这个 operations 也是一样,私有的
1405
01:12:59,439 --> 01:13:01,472
everything private that you can make private, okay?
尽量将所有能私有化的东西私有
1406
01:13:01,474 --> 01:13:04,909
Make the things public that you intend to support forever
对于你要永久支持的对象可以公有
1407
01:13:04,911 --> 01:13:09,513
in your object. Okay? So let's do that UI thing I was telling
现在我们来做UI
1408
01:13:09,515 --> 01:13:12,315
you about. Let's go back to our story board here, and
回到 storyboard
1409
01:13:12,317 --> 01:13:14,050
we want to make this thing so that when we,
我们想让他变得
1410
01:13:14,052 --> 01:13:20,490
let's see what it looks like now, actually. Okay,
看看它现在的样子,实际上
1411
01:13:20,492 --> 01:13:22,224
so our UI, we know it doesn't look very good,
我们的 UI 不是很好
1412
01:13:22,226 --> 01:13:24,927
this is not lined up. This is kind of nice right here but
他们没有连接起来,看起来是不错但
1413
01:13:24,929 --> 01:13:27,195
it's not lined up. But what happens if we
没有连接,如果我们横屏会
1414
01:13:27,197 --> 01:13:29,898
rotate to landscape? The way we do that is Hardware,
发生什么呢? 我们从 Hardware 里去找
1415
01:13:29,900 --> 01:13:33,368
in the simulator, Hardware > Rotate Left and Right, okay?
在 Simulator,Hardware,Rotate Left 或者 Right
1416
01:13:33,370 --> 01:13:38,572
I'm gonna use command keys to do it. Cmd+arrow. That really
我用 command 键来操作,Cmd+方向键
1417
01:13:38,574 --> 01:13:43,344
looks bad because I can't even say equal six times four.
看起来很糟,我用 6*4 都找不到等号键
1418
01:13:43,346 --> 01:13:45,312
Okay. I can't even use this UI, it's so
我不能使用这个UI
1419
01:13:45,314 --> 01:13:48,748
bad, okay? So, we need to fix this UI so
太糟糕了,我们需要修改UI
1420
01:13:48,750 --> 01:13:51,784
that when it's in portrait, it's using the whole space.
当它横屏的时候也能使用整个屏幕空间
1421
01:13:51,786 --> 01:13:54,653
Laying the buttons out to make it work and,
跑到外面去的按键能工作
1422
01:13:54,655 --> 01:13:57,022
when it's in landscape, it's using the whole space and
横屏的时候也能使用整个空间
1423
01:13:57,024 --> 01:13:58,557
the buttons are a different shape. Okay,
只是按键的形状不同
1424
01:13:58,559 --> 01:14:02,060
how are we gonna do that? Well I'm gonna do that by taking
怎么做呢?我们要把
1425
01:14:02,062 --> 01:14:05,697
each of these and putting them in a little stack. And then
按键们弄成一个小整块
1426
01:14:05,699 --> 01:14:08,699
I'm gonna take the five stacks and stack them together.
再把5个小整块变成一个大整块
1427
01:14:08,701 --> 01:14:12,503
And then I'm gonna stack this whole thing with this, okay,
然后把大整块和这个变成一个整块
1428
01:14:12,505 --> 01:14:16,406
and create a stack of stacks. And then I'm going to bind
用整块堆整块,然后
1429
01:14:16,408 --> 01:14:19,709
the left, top, right and bottom edges of that whole
整块的左边,上面,右边和下面的边缘与 UI
1430
01:14:19,711 --> 01:14:23,046
thing to the outer edges of my UI. That way,
的边缘绑定,这样
1431
01:14:23,048 --> 01:14:25,781
when the outer edges of my UI change, that thing will
当 UI 外部边缘改变的时候,它也会改变
1432
01:14:25,783 --> 01:14:27,616
change. And the stacks automatic gonna how to,
整块会自动知道它如何调整
1433
01:14:27,618 --> 01:14:30,886
you know, reallocate the space. Okay, simple as that.
你知道的,重新分配空间,就这么简单
1434
01:14:30,888 --> 01:14:33,154
So that's what we're gonna do. So let's make stacks here.
我们将这么做,现在我们来做一个整块
1435
01:14:33,156 --> 01:14:35,723
The way we do that, we select the things we want to stack.
做的方法就是,选中我们需要的元素
1436
01:14:35,725 --> 01:14:39,460
We go to editor Embed In > Stack View. Okay, and that's
点击 editor Embed > Stack View
1437
01:14:39,462 --> 01:14:43,263
gonna put it in a stack view here. Now, we can also go over
这样就放在了一个 stack view 里面,我们还能
1438
01:14:43,265 --> 01:14:45,799
to the inspector and inspect some things about this stack
在视察窗口里面改变一些 stack view 的设置
1439
01:14:45,801 --> 01:14:50,036
view like I want some spacing, 10 points between each one.
比如我要一些空格,每个之间间隔 10 points
1440
01:14:50,038 --> 01:14:52,972
Also, you see how the cosine one is wider then the dot?
再有,你看 cos 比其他的更宽
1441
01:14:52,974 --> 01:14:55,107
I don't want that, I want them all the same, so
我不想这个,我要它们看起来都一样
1442
01:14:55,109 --> 01:15:00,346
I want it to distribute its space equally. Okay, so
我希望他们的空间分布相同
1443
01:15:00,348 --> 01:15:01,947
now they're all equal. Okay,
现在他们一样了
1444
01:15:01,949 --> 01:15:06,751
same thing here. Okay, 10 points, and
这也是同样,10 points
1445
01:15:06,753 --> 01:15:10,321
fill equally. Now, by the way, there is no command key for
fill equally,现在这个没有快捷键
1446
01:15:10,323 --> 01:15:13,791
this, but you could go to Preferences over here,
但你可以到 Preferences 里面
1447
01:15:13,793 --> 01:15:16,126
Xcode > Preferences, and go to the key bindings and
Xcode > Preferences 自定义你的快捷键
1448
01:15:16,128 --> 01:15:17,260
give it a command key if you wanted.
如果你需要的话
1449
01:15:17,262 --> 01:15:19,195
If you were using stacking a lot, like I am,
或者你常常使用 stack
1450
01:15:19,197 --> 01:15:23,166
you could do that. So let's put these in here.
你可以这么做,现在把它们放这
1451
01:15:23,168 --> 01:15:29,739
10, fill equally. This one. Oops.
10,fill equally,这个,Oop
1452
01:15:32,276 --> 01:15:36,812
10, fill equally, and this last one. [BLANK
10,fill equally,最后一个
1453
01:15:36,814 --> 01:15:42,884
AUDIO] All right. Now I have these five stacks right here.
好了,现在我们有5个 stacks 了
1454
01:15:42,886 --> 01:15:46,254
Okay, horizontal stacks. Now I'm going to take them and
水平的 stack 现在我们要把它放
1455
01:15:46,256 --> 01:15:47,822
put them in a stack. Okay? So
在一个更大的 stack 里
1456
01:15:47,824 --> 01:15:51,558
I'm going to put them in a vertical stack. [NOISE] Okay?
这个 stack 是垂直的
1457
01:15:51,560 --> 01:15:56,029
Now, these, I want, here, to all be spread out.
我想让它们整齐分布
1458
01:15:56,031 --> 01:15:58,231
So right now you see the alignment is leading, so
这边你可以看见 Alignment 是 leading
1459
01:15:58,233 --> 01:16:00,600
it's putting all these things on the leading edge?
就是把所有东西与最左边的框对其
1460
01:16:00,602 --> 01:16:05,037
I want them to fill instead, so they fill the whole width.
我想让它填充,所以,这样就一个宽度了
1461
01:16:05,039 --> 01:16:07,906
Okay? I also want spacing here, okay?
在这我也想要一些空间
1462
01:16:07,908 --> 01:16:10,942
10 between all of them, so I've got kind of a nice
中间隔开10,现在我有了好看的
1463
01:16:10,944 --> 01:16:14,045
little key pad. Now let's stack this with this.
小键盘了,现在把它与这个 stack
1464
01:16:14,047 --> 01:16:18,449
So I'm going to select both of these and stack. Okay,
选中这两个,然后 stack
1465
01:16:18,451 --> 01:16:22,086
put them in a stack together. Again, I want spacing. I want,
把它们变成一个 stack,这里要隔开
1466
01:16:22,088 --> 01:16:24,888
do definitely do not want fill equally here, because that
这里不要使用 fill equally
1467
01:16:24,890 --> 01:16:28,325
would make this blue thing the same height as this big stack.
这样会让蓝色区域和小键盘一样高
1468
01:16:28,327 --> 01:16:31,561
So we don't want that, we just want Fill. That means they're
我们不需要这样,只是 Fill 就可以了
1469
01:16:31,563 --> 01:16:35,331
gonna be their natural size, okay? [COUGH] So for this,
意味着它是一个合适的尺寸
1470
01:16:35,333 --> 01:16:38,401
it's gonna be the size that fits this text and for this,
这是文本框的尺寸
1471
01:16:38,403 --> 01:16:39,201
it's gonna be a size for
这是所有的 stack
1472
01:16:39,203 --> 01:16:42,637
all those stacks to fit their contents. All right,
会填充的尺寸
1473
01:16:42,639 --> 01:16:46,841
now I'm gonna finally use the blue lines. Okay, because I'm
最后我终于要用到蓝色线条了
1474
01:16:46,843 --> 01:16:50,812
gonna put this thing up in the upper-left corner right here.
我要把这个放在左上角
1475
01:16:50,814 --> 01:16:53,581
Okay? And I'm gonna anchor it to that corner and
把它定位到这个角落
1476
01:16:53,583 --> 01:16:55,949
here's how we do that. We use the Ctrl key,
如何做呢? 使用 Ctrl 键
1477
01:16:55,951 --> 01:16:58,518
just like we did when we were dragging to the code.
就像我们拖动 UI 到代码里一样
1478
01:16:58,520 --> 01:17:01,287
We can also drag between elements in the UI. So,
在 UI 里也能拖动各种元素
1479
01:17:01,289 --> 01:17:05,925
I'm gonna drag between this, stack thing and this
所以我打算把这个 stack
1480
01:17:05,927 --> 01:17:09,328
outer container. So, I'm just dragging up to its top edge.
拖动到这个 container 边缘,把它拖到顶部边缘
1481
01:17:09,330 --> 01:17:12,064
Now when I do, when I Ctrl+drag between things,
我拖动的时候,要按下 Ctrl 键
1482
01:17:12,066 --> 01:17:15,233
I can constrain them to be related in some way.
这样让 UI 与 container 边缘联系起来
1483
01:17:15,235 --> 01:17:18,203
Like I could make them be equal widths. I can make this
就可以使它们同等宽
1484
01:17:18,205 --> 01:17:20,705
thing be the same width as the container view. Or
可以让 UI 和我的 container 一样宽
1485
01:17:20,707 --> 01:17:25,209
I can do what I want, which is constrain the vertical spacing
或者选择我要的,就是 Vertical Spacing to Top Layout
1486
01:17:25,211 --> 01:17:27,478
of this to the top layout. In other words,
换句话说
1487
01:17:27,480 --> 01:17:30,514
kind of attach that to that, so I'm gonna create that.
像把它们固定在一起,所以我点击它
1488
01:17:30,516 --> 01:17:32,749
And you can see it creates this little I-beam,
你看一看见这个小“I”柱
1489
01:17:32,751 --> 01:17:34,717
this little tiny I-beam right there. Okay,
这里的小“I”柱
1490
01:17:34,719 --> 01:17:37,520
I'm gonna do the same thing to this edge, right here.
同样的,对这条边也是这样
1491
01:17:37,522 --> 01:17:41,523
I'm gonna attach the leading space to the container margin,
我们点击 Leading Space to Container Margin
1492
01:17:41,525 --> 01:17:43,125
okay? And I can do the same thing. Now,
同样的,这条边也是
1493
01:17:43,127 --> 01:17:46,695
by the way, when you do this, be careful when you Ctrl+drag,
提一句,当你这样做的时候一定要小心
1494
01:17:46,697 --> 01:17:49,630
you wanna make sure the thing you're dragging from
确保你拖住啊的是整块 stack
1495
01:17:49,632 --> 01:17:52,667
is the entire stack. Don't be,
而不是
1496
01:17:52,669 --> 01:17:55,436
you know, just Ctrl+dragging from this eight or it'll
你知道的,这个8
1497
01:17:55,438 --> 01:17:58,238
actually pin the eight to the edge. Okay, you want to pin
不然它会把“8”拼在边缘上面
1498
01:17:58,240 --> 01:18:00,807
this whole stack view and I'm going to show you how you can
我给你展示下,如何选中整块stack
1499
01:18:00,809 --> 01:18:03,543
select the whole stack view in a second here. Let's drag
在一秒钟之内
1500
01:18:03,545 --> 01:18:06,278
this over, this is going to be the trailing space.
拖动它,这个选trailing space
1501
01:18:06,280 --> 01:18:09,314
Okay. And now, here I'll show you how to, if I click on
现在我教你们
1502
01:18:09,316 --> 01:18:12,284
this thing right here, it's selecting the two. But I want
如果我点了这个东西,它选中了“2”
1503
01:18:12,286 --> 01:18:15,053
to select the whole thing, so I'm going to do Ctrl+Shift.
但我想要一整块,按下 Ctrl+Shift
1504
01:18:15,055 --> 01:18:17,755
Ctrl+Shift, okay, see it down in the lower left there,
Ctrl+Shift,看屏幕下面
1505
01:18:17,757 --> 01:18:20,658
Ctrl+Shift? Ctrl+Shift-click. When you do that,
Ctrl+Shift,按住 Ctrl+Shift 再点击
1506
01:18:20,660 --> 01:18:24,294
it says, what thing under the mouse do you want to select?
这样的做的时候它会说,你的鼠标想选中什么?
1507
01:18:24,296 --> 01:18:27,864
Do you want to select that outer container, the big stack
你想选中Outer container, stack view
1508
01:18:27,866 --> 01:18:30,734
view, or this little, interior stack view? So here, I want
还是这个小的内部 stack ?
1509
01:18:30,736 --> 01:18:34,137
the big stack view, the one that contains the whole thing.
在这我选择 stack view,它包含了全部按键
1510
01:18:34,238 --> 01:18:37,406
All right, so and then when I Ctrl+drag, I'm being careful
所以当我按下 Ctrl 拖拽的时候
1511
01:18:37,408 --> 01:18:39,474
not to Ctrl+drag from one of these buttons.
要非常小心,不要选择一个按键
1512
01:18:39,476 --> 01:18:42,110
And here, I'm Ctrl+dragging from one the spaces there,
现在我把它拖到这里
1513
01:18:42,112 --> 01:18:45,246
okay. So this is to vertical space into the bottom.
这里选 vertical space into the bottom
1514
01:18:45,248 --> 01:18:48,282
And so now I've tied them to the edges. Unfortunately,
现在我绑定了它们的边缘,不幸的是
1515
01:18:48,284 --> 01:18:52,386
I've tied these two edges too far away from the edges.
这两条边相隔太远了
1516
01:18:52,388 --> 01:18:55,489
Okay. I wanna tie these two edges to right up next to it.
我想让它们在彼此的旁边
1517
01:18:55,491 --> 01:18:59,325
And the way I do that is, I can do it via the Inspector
在视察窗口里就可以做到
1518
01:18:59,327 --> 01:19:02,462
right here, by clicking on this I-beam, you see.
点击这个小”I”柱,你可以看到
1519
01:19:02,464 --> 01:19:04,030
This constant saying how far it is.
这个常数就是距离
1520
01:19:04,032 --> 01:19:06,832
I can also Double-click on this I-beam. And
我可以双击这个”I”
1521
01:19:06,834 --> 01:19:09,735
it puts up a little thing here. So, I don't want her
这里出现了一些设置
1522
01:19:09,737 --> 01:19:12,904
to be 338 points away, I want her to be either some standard
我不希望它们 338 那么远,想设置成一个标准值
1523
01:19:12,906 --> 01:19:15,740
value, or if a standard value doesn't make sense here,
如果标准值不行的话
1524
01:19:15,742 --> 01:19:17,608
which it doesn't, that's why it's grayed out,
事实上不能用,这就是为什么它是灰色
1525
01:19:17,610 --> 01:19:22,413
then I'm gonna put it 0 points away. Bam. Okay?
那么我就在这输入0
1526
01:19:22,415 --> 01:19:25,582
Same thing I can do down here. Let's Double-click this one.
下面这我们可以做相同的事,双击它
1527
01:19:25,584 --> 01:19:27,817
Here, a standard value is available, so
这里标准值可以用
1528
01:19:27,819 --> 01:19:29,318
I'm gonna click standard value. And
所以我点击 standard value
1529
01:19:29,320 --> 01:19:32,655
now it's putting its standard value from the bottom. Okay.
现在它把距离设置成了标准值
1530
01:19:32,657 --> 01:19:35,324
Now, when it's stretched there, it made these tall.
当设置成功后,这一部分变高了
1531
01:19:35,326 --> 01:19:38,894
Okay, so that means we did something bad with our,
这说明我们之前做错了
1532
01:19:38,896 --> 01:19:41,730
you know, spacing of the things, which is,
你知道的,设置距离什么的
1533
01:19:41,732 --> 01:19:45,833
what did we do wrong here? Those are all fill equally.
我们做错了什么? 它们 fill equally
1534
01:19:45,835 --> 01:19:49,837
Yes. How about this guy right here? Maybe this,
对,是这个小东西吗?可能是这个
1535
01:19:49,839 --> 01:19:54,008
this guy fill equally. Okay we want this internal one.
这个 fill equally,好了我们想要的就是它
1536
01:19:54,010 --> 01:19:59,012
Okay, this internal stack view, to be fill equally.
中间的整个 stack 就是 fill equally
1537
01:19:59,014 --> 01:20:00,914
Glad I made that mistake, so I show you how to do that,
挺高兴翻了这种错,这样展示给你们看
1538
01:20:00,916 --> 01:20:04,017
okay? So, we've got this all equally spaced out. This
现在整个 UI 分布好了
1539
01:20:04,019 --> 01:20:06,852
looks pretty kind of funny in a square, but I bet it's gonna
看起来还不错的样子
1540
01:20:06,854 --> 01:20:08,654
look pretty good in portrait and landscape,
我觉得横屏竖屏下应该都很好看
1541
01:20:08,656 --> 01:20:17,763
let's go take a look. All right here's portrait. Hey,
我们看一眼,好了,这是竖屏
1542
01:20:17,765 --> 01:20:22,067
that looks pretty darn good. 4 times 8, you know, plus 9
看起来还不错,4*8,加上9
1543
01:20:22,069 --> 01:20:27,238
equals. Square root, okay, cosine, pi, cosine. Excellent,
等号,根号,好的,cos,pi,cos非常不错
1544
01:20:27,240 --> 01:20:31,709
let's take a look at landscape, woohoo! It worked,
看看横屏,哇哦,不错哦
1545
01:20:31,711 --> 01:20:36,213
okay. So, very little work here. And we can make our UI
做了点微小的工作,就可以让 UI
1546
01:20:36,215 --> 01:20:39,349
stretchable, okay? Now, later in the quarter, we're gonna
伸缩自如,课程后面我们会学到
1547
01:20:39,351 --> 01:20:41,751
have more sophisticated UIs than just these stack things,
更多更复杂的 UI 技巧,不仅仅是这种 stack
1548
01:20:41,753 --> 01:20:45,188
but we'll still be using that Ctrl+dragging to the edges.
但会一直用到 Ctrl 加拖拽
1549
01:20:45,190 --> 01:20:48,424
Now, your homework assignment is to reproduce everything
现在,作业就是重复我
1550
01:20:48,426 --> 01:20:49,992
I've done in these two days.
在过去两节课中的所有工作
1551
01:20:49,994 --> 01:20:51,493
Add that floating point number,
并且加上浮点数
1552
01:20:51,495 --> 01:20:54,329
add a little text field that shows a history of all
再加上一个文本框,可以显示所有
1553
01:20:54,331 --> 01:20:55,963
the things that have been typed in, and
的输入信息的历史
1554
01:20:55,965 --> 01:20:58,332
add some more buttons. So you're gonna be doing outlets,
添加更多的按键,所以也要做 outlet action
1555
01:20:58,334 --> 01:21:00,834
actions, and a little bit more. And that's
还有
1556
01:21:00,836 --> 01:21:04,705
basically your entire homework okay? It's all posted.
这基本就是所有作业了,现在都发布了
1557
01:21:04,707 --> 01:21:06,873
See ya next week. >> For
下个星期再见
1558
01:21:06,875 --> 01:21:06,906
more, please visit us at stanford.edu
更多信息,请访问 stanford.edu
================================================
FILE: subtitles/3. More Swift and Foundation Framework.srt
================================================
1
00:00:06,000 --> 00:00:09,060
Stanford University. >> Okay,
斯坦福大学
2
00:00:09,070 --> 00:00:13,740
well welcome to lecture number three of Stanford CS193P.
欢迎来到斯坦福 CS193P 课程第三讲
3
00:00:13,740 --> 00:00:17,410
This is spring quarter 2016. And today we
这是 2016 年春季
4
00:00:17,410 --> 00:00:22,140
are going to mostly talk about the layer under the UI.
今天我们主要会介绍UI 层
5
00:00:22,150 --> 00:00:24,450
Okay, so we're gonna talk about Swift the language.
我们会讨论Swift 这门语言
6
00:00:24,450 --> 00:00:26,950
We're gonna talk about this foundation framework that
我们将会讨论位于核心服务层顶部的foundation 框架
7
00:00:26,950 --> 00:00:29,550
lives on top of Core services. So we're not going to be
所以我们不会讨论一整天非常多关于UI 的内容
8
00:00:29,550 --> 00:00:32,920
talking about the UI very much at all today. I do have a demo
在接近尾声的时候会有一个demo
9
00:00:32,920 --> 00:00:36,490
towards the end. So I will finish off the slides and
我会完成这些幻灯片然后我们把这个演示做完
10
00:00:36,490 --> 00:00:40,090
then we'll do the demo to finish. Okay, all right, so
好的
11
00:00:40,100 --> 00:00:43,200
first thing I want to talk about is optional's,
首先我想介绍的是 optional
12
00:00:43,200 --> 00:00:46,030
by the way, everything I am talking about today is
顺便说下 我今天介绍的所有东西
13
00:00:46,040 --> 00:00:49,340
in your reading assignments. Okay, I'm just, the things I
都在你们的阅读作业里。好的
14
00:00:49,340 --> 00:00:52,040
am talking about today I want to emphasize. I mean these
我想强调我今天介绍的东西
15
00:00:52,040 --> 00:00:54,280
are things that you really really want to understand.
我的意思是这些东西你们真的真的需要理解
16
00:00:54,280 --> 00:00:56,880
So if you see me mention it today and then you're reading
如果你在你的阅读作业里读到我今天提到的东西
17
00:00:56,880 --> 00:00:59,950
it in the reading assignment, be sure to pay close attention
确保密切关注好吗?
18
00:00:59,950 --> 00:01:02,920
all right? So as we learned in the last lecture,
那在我们上节课学的内容中
19
00:01:02,920 --> 00:01:07,020
optional is just an enum, okay? It's a generic type kind
optional 就是一个 enum 是吧?它是一个有点像数组的泛型
20
00:01:07,020 --> 00:01:09,190
of like array. It's got this little angle bracket T,
它有个小尖括号
21
00:01:09,190 --> 00:01:11,530
which just means an Optional can be an Optional of any
这意味着 Optional 可以选择任何类型
22
00:01:11,530 --> 00:01:14,000
type. And we know that to be the case. We can have
并且我们知道的情况
23
00:01:14,000 --> 00:01:17,000
Optional strings or Optional even Optional optionals.
我们可以有Optional sting 甚至是Optional 的 Optional
24
00:01:17,000 --> 00:01:19,530
We can have any kind of Optional we want. And so
我们可以选择任何我们想要的 Optional
25
00:01:19,540 --> 00:01:22,400
it's an enum with two cases. The None case which is the not
所以这是一个有两种情况的 enum。
26
00:01:22,410 --> 00:01:25,710
set case and then the Some case, which is the set case.
None 情况是没有设置东西,其它情况是设置了一些东西的
27
00:01:25,710 --> 00:01:27,780
And in the Some case it has an associated value. And you
在一些情况下它有关联值
28
00:01:27,780 --> 00:01:31,450
learned all about enums last time. So this, syntax should
你们上次学了的关于枚举的内容
29
00:01:31,450 --> 00:01:37,220
look completely, familiar to you. So let's look at some
所以这个语法对你们来说应该是完全熟悉的
30
00:01:37,220 --> 00:01:40,250
of the little question mark, exclamation point stuff. And
让我们来看看这些小问号,惊叹号
31
00:01:40,260 --> 00:01:43,060
what that means in terms of optional behind the scenes. So
在这些场景背后它们和optional 的关系是什么
32
00:01:43,060 --> 00:01:47,060
if you had a constant x which was of type optional string,
如果你有一个常量 x 它的类型是optionl 字符串
33
00:01:47,060 --> 00:01:48,460
right, string question mark.
对,字符串问号
34
00:01:48,470 --> 00:01:53,330
And I set it to nil, then that's the same as saying that
然后我把它设置为nil ,这等同于是说 x = Optional.None
35
00:01:53,340 --> 00:01:58,310
x = Optional.None, right? Obviously, and same
对吗?显然, 的情况一样
36
00:01:58,310 --> 00:02:00,940
thing if I had an optional string that was set to hello,
如果我有一个可选字符串设置成hello
37
00:02:00,940 --> 00:02:04,880
that'd be the same as x=Optional.Some
那就等于x=Optional.Some
38
00:02:04,880 --> 00:02:07,150
with the associated value, hello. Okay,
关联的值是hello。 好的
39
00:02:07,150 --> 00:02:10,690
everybody cool with that? And similarly, if I unwrap,
大家都觉得这很酷吧?类似的,假如我拆包
40
00:02:10,690 --> 00:02:15,120
if I said, var y=x unwrapped, that just means switch x,
如果我说,var y=x!,就等于switch x
41
00:02:15,130 --> 00:02:17,390
because we know that the way we unwrap Optionals and
因为我们知道我们那样子拆包Optionals
42
00:02:17,390 --> 00:02:19,560
get their associated value is with switch, so
所以我会打开x 并用开关得到相关的值
43
00:02:19,560 --> 00:02:22,400
I'm gonna switch on x. And in the case of Some, I'm gonna
在一些情况下,我会用let 值
44
00:02:22,400 --> 00:02:25,670
do that let value thing so I can grab the associated value.
因为这样我可以抓取相关的值
45
00:02:25,670 --> 00:02:28,470
And then I'm just gonna set y equal to that value.
然后我要设置y 等于那个值
46
00:02:28,470 --> 00:02:30,100
In the case None, I'm gonna crash.
在空的情况下,我的程序会crash
47
00:02:30,110 --> 00:02:32,740
I'm gonna raise an exception, okay, because that's what
我要提出一个列外
48
00:02:32,740 --> 00:02:35,540
unwrapping an Optional that's not said is supposed to do,
因为拆包一个Optional 不意味着它应该这么做
49
00:02:35,550 --> 00:02:40,450
it's supposed to crash, okay? And of course, in the case
它应该让程序崩溃。
50
00:02:40,450 --> 00:02:44,420
where I'm doing if let, then I'm just gonna switch on x,
如果我用let 这么做,然后我去switch x
51
00:02:44,420 --> 00:02:47,190
and I'm, the case of .Some, I'm gonna grab that y, and
在 .Some 情况下我用y 拿出 x 的值
52
00:02:47,190 --> 00:02:49,720
then I'm just gonna put some code, whatever my code is in
然后就会执行我放在下面的代码
53
00:02:43,731 --> 00:02:46,899
there, and if let to happen there. And in the case None,
然而在None 的情况下
53
00:02:52,900 --> 00:02:54,660
I just break out of the switch, I do nothing.
我会直接跳出switch 什么都不做
54
00:02:54,660 --> 00:02:59,700
Okay, sound good? All right, so that's Optionals.
听起来不错? 这就是Optionals
55
00:02:59,700 --> 00:03:01,370
Hopefully Optional now should start feeling very,
希望现在开始对Optional 的感觉好点了
56
00:03:01,370 --> 00:03:03,340
very comfortable to you cuz, as you found out,
在这些API 里面
57
00:03:03,340 --> 00:03:07,640
it's everywhere in these, in the API. Now Optionals have
你会发现到处都有舒服的提示
58
00:03:07,640 --> 00:03:10,710
some more syntax, okay. That question mark and exclamation
现在Optionl 有更多的语法了。你看见的那些问号和感叹号
59
00:03:10,710 --> 00:03:13,880
point stuff, as you're seeing, is just syntactic sugar.
都是语法糖
60
00:03:13,880 --> 00:03:16,720
It's just to make the, your typing it in easier and
这是为了让你写起来更舒服并且更好看
61
00:03:16,720 --> 00:03:20,590
look nicer. It has a little bit more syntactic sugar.
它还有更多的语法糖
62
00:03:20,590 --> 00:03:24,660
One thing is that Optionals can be chained. Okay, so
比如Optional 可以是链式的
63
00:03:24,660 --> 00:03:26,460
this is the case where, for example,
如同这个例子的情况
64
00:03:26,460 --> 00:03:29,530
in our calculator, we have the display, and
在我吗的计算器例子里,我们有显示,
65
00:03:29,530 --> 00:03:34,340
lets say we want to get the text out of the display. And
让我们把一些想要得到的文本显示出来
66
00:03:34,340 --> 00:03:38,070
then we want to send another message like hash value which,
然后我们想传递一些信息,比如哈希值,
67
00:03:38,070 --> 00:03:40,110
strings understand this message hash value,
这些消息哈希后的字符串
68
00:03:40,110 --> 00:03:43,380
how would we do that? Well of course we would say,
我们该如何做呢?
69
00:03:43,380 --> 00:03:47,180
if let label equal display to unwrap the display. And
我们会这样,如果让 label 等于 display 这样去拆包 display
70
00:03:47,180 --> 00:03:50,020
they we would say if let text equal the labels text,
然后我们会让text 等于label 的 text
71
00:03:50,020 --> 00:03:54,020
and unwrap that. Then we would say let x equal the text hash
拆包之后,我们在让x 等于text 哈希后的值
72
00:03:54,020 --> 00:03:56,820
value, okay? So that is how we would unwrap it.
所以,这就是我们如何去拆包这些信息
73
00:03:56,830 --> 00:03:59,730
Well there is a lot easy way to do it which is this,
当然还有很多更容的方法来做
74
00:03:59,730 --> 00:04:02,700
you just say display?.text?.hashvalue,
你可以这样display?.text?.hashvalue
75
00:04:02,700 --> 00:04:08,540
okay? So if you use question marks when you're, not when
如果你在这个时候用 ? 而不是在声明optional 类型时使用
76
00:04:08,540 --> 00:04:11,640
you're declaring the optional but when you're actually using
这基本上意味着
77
00:04:11,640 --> 00:04:14,840
it, that basically means, try and unwrap this and
尝试去把它们拆包
78
00:04:14,840 --> 00:04:18,380
if you can then use that value to go to the next thing.
拆包后的值是否可以让你去到下一部分
79
00:04:18,380 --> 00:04:21,650
If you can't, just return nil from this whole expression.
如果不行,整个表达是就返回nil
80
00:04:21,650 --> 00:04:23,150
Okay, this whole display question mark,
这个dispaly 后面所有的问号,
81
00:04:23,150 --> 00:04:24,590
that typed question mark, that hash value,
那个哈希值
82
00:04:24,590 --> 00:04:27,790
that whole expression will return nil if at any point
如果它们当中有任何一点在拆包的时候返回了nil
83
00:04:27,790 --> 00:04:31,630
any of these things that were changing unwrap turns out to
这整个表达式就会返回nil
84
00:04:31,630 --> 00:04:35,000
be nil. So, if the display is nil or if the display's text
所以,如果display 是空的,或者display 的text 是空的
85
00:04:35,000 --> 00:04:37,470
is nil then this whole thing is gonna be nil.
那这整个就会变成nil
86
00:04:37,470 --> 00:04:41,870
So you see let x equal up there? What type do you think
所以你看见让x 等于它们
87
00:04:41,870 --> 00:04:46,970
x is right there? >> [INAUDIBLE].
这里的x 是什么类型的 [讨论]
88
00:04:46,980 --> 00:04:48,780
>> Int? almost.
Int? 差不多。
89
00:04:48,780 --> 00:04:51,780
Optional int, exactly. It has to be optional int, because
对,是optional int 类型
90
00:04:51,780 --> 00:04:54,520
any of those things could fail and it would return nil.
因为任何其它东西都可能失败并返回nil
91
00:04:54,520 --> 00:04:55,520
So it has to be able to take nil. So
所以它可以是nil 值
92
00:04:55,520 --> 00:04:59,320
that would be an optional int, cuz hash value returns an int,
因为它可以返回一个int 的哈希值,所以它是一个可选int
93
00:04:59,320 --> 00:05:03,120
okay. So you're definitely gonna wanna use this,
你肯定会想用这种链条一样的方式去编程
94
00:05:03,130 --> 00:05:05,990
chaining, it really makes your code read beautifully, okay.
它真的让你的代码看起来漂亮了
95
00:05:06,000 --> 00:05:09,000
And the starts make Optionals make a lot of sense why we use
当我们开始使用可选类型的时候会有很多场景去使用它们
96
00:05:09,000 --> 00:05:11,630
them so much. Okay, but there's more.
好的,但是还有别的用法
97
00:05:11,630 --> 00:05:14,640
Here's another Optional syntaxtiture which is ??
这里是Optional 的另一种用法叫做 ??
98
00:05:14,640 --> 00:05:18,970
Okay, and this is basically providing a default value
它基本上是在 case 为 nil 的时候提供一个默认的值
99
00:05:18,980 --> 00:05:22,640
in case something is nil. Okay, so let's add an optional
来添加一个可选字符串s
100
00:05:22,650 --> 00:05:27,380
string here s, okay. I could say if s is not nil,
我可以说,如果s 不是空
101
00:05:27,380 --> 00:05:30,120
then I'm gonna set my display to be s, okay.
然后我会把我的display 设置成 s
102
00:05:30,120 --> 00:05:32,920
So let's say I'm setting the display in my calculator, and
这意味我在我的计算器上设置了显示
103
00:05:32,920 --> 00:05:36,060
I never want it to be nil. At worst, I want it to be just
而且我不想让它是空的,最差也得是个空格字符
104
00:05:36,060 --> 00:05:38,690
a space character. Because you might have noticed,
因为你可能注意到了
105
00:05:38,700 --> 00:05:40,490
some of you when you're doing your homework,
当你在做你的一些作业的时候
106
00:05:40,500 --> 00:05:43,000
that if you put nil into a label,
如果你往 label 里面放了 nil
107
00:05:43,000 --> 00:05:45,670
its size will go to zero. Okay,
它的尺寸会变成0
108
00:05:45,670 --> 00:05:50,070
cuz there's no text in there to have a natural size for. So
因为没有文本它就不会有一个默认的尺寸
109
00:05:50,070 --> 00:05:52,870
sometimes you might want to have a code like this where if
所以有时候你可能想要一些这样的代码
110
00:05:52,880 --> 00:05:54,310
you want to put nothing in the display,
如果你往display 里面放了空的东西
111
00:05:54,310 --> 00:05:57,080
you put a space character, so at least there's something in
你就放一个空格字符,因为这样至少这里有东西让 label 有高度
112
00:05:57,080 --> 00:06:00,920
there for it to have a height, okay? Otherwise your display
否则你设置成nil 的话
113
00:06:00,920 --> 00:06:03,980
would keep disappearing if you kept setting it to nil. So
你的display 可能会消失
114
00:06:03,990 --> 00:06:06,890
we can do this, if s does not equal nil then display equals
那么我们可以这样,如果s 不等于nil 就让display 等于s
115
00:06:06,890 --> 00:06:09,460
s, that should be s exclamation point right there,
用 != 表示不等于
116
00:06:09,460 --> 00:06:12,260
otherwise display equals a space. Okay, well,
否则就 display 就等于一个空格
117
00:06:12,260 --> 00:06:15,000
a much simpler way to put that is with this,
有一个更简单的方法
118
00:06:15,000 --> 00:06:19,430
defaulting operator, which is to say the display.text
让display.text = s
119
00:06:19,440 --> 00:06:24,170
= s Okay? And that means if s is not nil then unwrap it and
这表示如果s 不为空,就把它拆包
120
00:06:24,170 --> 00:06:27,680
use that value, otherwise use space.
然后就使用拆包后的值,否则就使用空格
121
00:06:28,710 --> 00:06:31,850
Got that? Everyone understand? I don't see any nodding heads
懂了吗?每个人都懂了吗?我没有看就有人点头
122
00:06:31,850 --> 00:06:34,680
on that one. Make sense, okay? So this is, again, just
懂了吗?再说一遍
123
00:06:34,680 --> 00:06:38,920
a simple way to have a default value in case an optional is
这是让 optional 在 nil 的情况下有一个默认值的简单的方法
124
00:06:38,920 --> 00:06:43,490
nil. All right so let's talk about another thing, tuples.
继续介绍另一东西,元组。
125
00:06:43,490 --> 00:06:47,960
All right tuples are a type, okay? In Swift they're really,
元组是一种类型,在 Swift 中它们非常酷
126
00:06:47,960 --> 00:06:51,670
really cool. They're basically a way to build a type out of
它们是一种构建类型外的类型的基本方法
127
00:06:51,670 --> 00:06:54,170
other types by grouping them, okay? And
好的
128
00:06:54,170 --> 00:06:56,840
you can use it anywhere you can use a type, a tuple can be
你可以在任何地方用一个类型
129
00:06:56,840 --> 00:07:01,640
used anywhere types are valid, okay? So, here's an example.
一个元组在任何地方使用都是可以的,好的。这里有一个例子
130
00:07:01,640 --> 00:07:06,010
I'm going to create x, okay? This let x, x is a constant.
我要生成一个x 好的。这是let x,x 是一个常量
131
00:07:06,020 --> 00:07:09,950
It's type is a tuple, with a string, int, and
这是一个元组类型,含有 string, int, 和 double
132
00:07:09,950 --> 00:07:14,020
double. So, even though tuple has the sound, to, in it,
尽管元组听起来有 two 的声音
133
00:07:14,020 --> 00:07:17,190
it's just not two things. Any number of things can be in
但并不是他有只有两个东西。一个元组里面可以有任何数量的东西
134
00:07:17,190 --> 00:07:22,460
a tuple okay? So x there is a tuple with string int and
好的,所以 x 是一个含有 string int double 的元组
135
00:07:22,470 --> 00:07:24,700
double. And I'm even setting it to a value,
我甚至可以往里面设置值
136
00:07:24,700 --> 00:07:28,240
which is just parenthesis, a string, an int, and a double,
用括号包起来一个 string 一个 int 和一个 double
137
00:07:28,240 --> 00:07:33,070
okay? Now how do I get those values out of the tuple?
好的。现在我该如何从这个元组里面获得值呢
138
00:07:33,080 --> 00:07:34,480
Well there is two ways to do it.
嗯。有两种方法
139
00:07:34,480 --> 00:07:38,950
One is to say, let and then three, identifiers. These
一种,分配三个id
140
00:07:38,950 --> 00:07:41,920
are basically three local variable names, equals x,
它们是基于三个本地变量来命名的,等于 x
141
00:07:41,920 --> 00:07:45,320
okay? And that's gonna extract the three values and put them,
好的,然后会取出三个值并保存到它们里面去
142
00:07:45,320 --> 00:07:48,320
assign them to word, number, and value, which are gonna
分别放在 word,number,和 value 里
143
00:07:48,320 --> 00:07:51,330
be local variables in this context, okay? Now, if you
它们会在这个上下文里得到本地变量。好的,现在
144
00:07:51,330 --> 00:07:55,730
tried to say let word, comma, number, closed parentheses,
如果你尝试 let (word, number) = x
145
00:07:55,730 --> 00:08:00,170
equal x, the compiler will complain, because word, comma,
编译器是会出错的
146
00:08:00,170 --> 00:08:04,040
number can't match a string in double tuples. Okay? So
应为 word, number 无法和元组里的 double 关键字匹配。好的
147
00:08:04,040 --> 00:08:07,610
this particular syntax is just putting names on the things in
这一部分的语法就是把这些东西放到这些命名里面
148
00:08:07,610 --> 00:08:11,510
the Tuple so you can use them, so now you can print them out.
你可以这样使用元组,所以你现在可以把它们打印出来了
149
00:08:11,510 --> 00:08:12,580
Print word. Print number. Print value.
Print word. Print number. Print value.
150
00:08:12,580 --> 00:08:16,520
Value would be of type string. Number would be of type int
word 会是 string 类型,Number 会是 int 类型
151
00:08:16,520 --> 00:08:18,120
and value would be of type double. Okay,
还有 value 会是 double 类型,好的
152
00:08:18,120 --> 00:08:22,090
another way to do it is when you create the tuple, okay,
另一种这么做的方法是,当你在生成元组时,好的。
153
00:08:22,090 --> 00:08:24,830
you can name each of the things in the tuple. So
你可以为元组里的每个东西命名。
154
00:08:24,830 --> 00:08:31,070
here I'm letting x, this time be w: String i: Int v: Double.
这里我对x 的string 命名为w,Int 命名为i,Double 为v
155
00:08:31,070 --> 00:08:34,640
I'm giving the names w, i and v to the things inside
我在元组里面分别起了几个名字叫 w,i 和 v
156
00:08:34,640 --> 00:08:37,240
the tuple. I'm still assigning it, just like I did in
我继续给它赋值,就好像我在之前做的一样
157
00:08:37,240 --> 00:08:40,340
the version above. But now if I want to get at the values,
但是现在,如果我想要得到一个值
158
00:08:40,340 --> 00:08:45,550
I can just say x.w, x.i, and x.v to get at the tuple
我可以直接说 x.w,x.i,还有 x.v 来获得元组中的值
159
00:08:45,550 --> 00:08:48,250
values. See the difference between those two cases?
看出这两种方法的不同之处了吗?
160
00:08:48,250 --> 00:08:51,090
One, you're kind of naming it when you declare the tuple,
一种,在你声明元组的时候定义一种命名
161
00:08:51,090 --> 00:08:53,920
the other one is you're taking a tuple that you got and
另一种是你拿到一个元组
162
00:08:53,920 --> 00:08:56,620
you're just extracting the values. And you can mix them.
然后你就取出值。并且你可以混合使用它们
163
00:08:56,630 --> 00:08:59,690
For example I could say let = x.
比如我可以说 let = x
164
00:08:59,700 --> 00:09:02,100
Even though I defined it x to have w, i and v,
尽管我给 x 定义了 w, i 和 v
165
00:09:02,100 --> 00:09:05,370
I could ignore the w, i and v and instead just do
我可以忽略它们
166
00:09:05,370 --> 00:09:10,800
the syntax from the top okay and call it wrd, num and val.
用 word, number 和 value 执行上面那种语法
167
00:09:10,810 --> 00:09:15,340
Okay? So, tuples are cool because you can return
好的。所以,tuple 是很酷的
168
00:09:15,340 --> 00:09:18,750
multiple values from a function with a tuple. Okay?
因为你可以从一个 function 中返回一个有多个值的 tuple
169
00:09:18,750 --> 00:09:22,750
So, we know that returned values are arrow and a type.
所以,我们知道返回的值指向一个类型
170
00:09:22,750 --> 00:09:25,520
Well, since a tuple can be a type, you can go arrow and
好的,当 tuple 可以是一种类型的时候
171
00:09:25,520 --> 00:09:27,660
a tuple, and return values. Yeah?
你可以去指向这个类型,并返回它们的值,对吗?
172
00:09:27,660 --> 00:09:28,260
>> Can you ignore some of
[学生提问]
173
00:09:28,260 --> 00:09:29,320
the values? >> So, question,
所以,问题是
174
00:09:29,330 --> 00:09:30,560
can you ignore some of the values?
你可以屏蔽这些值中的一部分吗?
175
00:09:30,560 --> 00:09:33,590
Absolutely you can, if you use underbar. Underbar and
显然是可以的,你可以使用 underbar
176
00:09:33,600 --> 00:09:37,230
swift is the universal I'm ignoring this character, okay,
swift 里的 underbar 是普遍的,在这一章我先不讲,好的
177
00:09:37,230 --> 00:09:41,670
so you can put underbar in there. So yeah, so
所以你可以把值放在这里,
178
00:09:41,670 --> 00:09:44,070
here I'm returning this tuple weight and
返回一个有宽度和高度的tuple
179
00:09:44,070 --> 00:09:47,210
height, obviously straight forward how we do that.
显然我们可以直接坐到
180
00:09:47,210 --> 00:09:50,010
So tuples are perfectly valid return values, okay? So
所以tuple 用来返回值是非常有效的,好的。
181
00:09:50,010 --> 00:09:52,910
you can return multiple things from functions. All right,
所以你可以从函数返回多个值
182
00:09:52,920 --> 00:09:56,850
range. So range is quite important, actually,
好的,range 。range 在Swift 里是非常重要的
183
00:09:56,850 --> 00:10:00,960
in Swift. It's essentially just two end points,
它基本上就是两个结束点
184
00:10:00,960 --> 00:10:05,330
okay, of anything that can be representative consecutively,
好的,它可以代表任何连续的东西
185
00:10:05,330 --> 00:10:10,560
okay? So range, the type, is generic, like array,
所以,range ,这种类型是普通的,就好像数组
186
00:10:10,570 --> 00:10:13,230
so you can have a range of ints. You can have
所以你可以有一个 int 的 range。
187
00:10:13,240 --> 00:10:16,900
a range of indexes into something or whatever it
你可以有一个某些东西内部的索引范围
188
00:10:16,910 --> 00:10:20,240
really conceptually just has two things, a startIndex and
或者其它只要有两个东西的连续的东西。有一个起始索引和一个结束索引
189
00:10:20,240 --> 00:10:25,310
a last index, okay, endIndex. An array's range, okay.
一个数组的 range,好的
190
00:10:25,310 --> 00:10:27,950
If you wanted to get an array, arrange into an array,
如果你想要得到一个数组中部分范围的数组
191
00:10:27,950 --> 00:10:28,880
it would be a range of ints,
可以用一个 int 型的 range
192
00:10:28,880 --> 00:10:30,950
because an array is indexed by ints.
因为数组的索引是根据int 来的
193
00:10:30,950 --> 00:10:34,220
So you would have a range of ints. In fact you know,
所以你应该有一个int 的range。
194
00:10:34,220 --> 00:10:36,720
you, there are methods in array where you can say, give
实际上你知道,在array 里你可以调用一些方法
195
00:10:36,730 --> 00:10:41,900
me this range of yourself, and you specify it as a Range.
提供它们你要指定的范围
196
00:10:41,900 --> 00:10:47,600
A string's range is not Int. Okay, if you wanna substring,
一个字符串的范围不是Int。 好的,若果你想要子字符串
197
00:10:47,600 --> 00:10:50,440
you cannot use a range of Int. It's actually a range of
你不能使用Int 的range。它实际上是一个String.Index 的range
198
00:10:50,440 --> 00:10:53,740
String.Index, which is a different type than Int. And
它有着和Int 不同的类型
199
00:10:53,740 --> 00:10:57,680
you're gonna need to read all about that in the assignment,
而且你都会在你的作业里读到这些的
200
00:10:57,680 --> 00:10:59,310
okay? One of the section's in assignment is
好的,作业里面面有一章讲的是
201
00:10:59,320 --> 00:11:01,720
indexing into arrays in the ring assignment.
环形数组中的索引
202
00:11:01,720 --> 00:11:03,550
And you're gonna wanna understand that, okay?
你们要去理解那个,好吗
203
00:11:03,550 --> 00:11:05,790
It's a little bit complicated. I'm not gonna spend
这有一点点复杂。我不能花太多课上的时间
204
00:11:05,790 --> 00:11:07,420
lecture time but just I'm pointing it out to you,
但是我会为你们指出它来
205
00:11:07,420 --> 00:11:10,820
so you could go look at it. Okay? Now, there's special,
所以你们可以去看看,好吗?现在
206
00:11:10,830 --> 00:11:12,790
just like with optionals we've got question marks and
有点特殊,想好像optional 的问号和叹号
207
00:11:12,800 --> 00:11:16,360
exclamation points. There's some special syntax for
range 有一些特殊的语法
208
00:11:16,370 --> 00:11:20,070
ranges, okay, which is this ...and ..<.
比如 ... 和 ..<
209
00:11:20,070 --> 00:11:24,910
You see them right here? Okay the... .. 00:11:28,810
This means a range right here, okay, that goes from 2 to 3.
这在这里表示一个 range,那个是从 2 到 3
211
00:11:28,810 --> 00:11:33,980
This is a range that goes from 2 to 3 but does not include 3.
这是从 2 到 3 但不包含三
212
00:11:33,980 --> 00:11:37,420
Okay? So that would be just the number 2, right there.
好的,所以那里讲会有两个数字
213
00:11:37,420 --> 00:11:40,050
Okay? Now, see this 4 right here?
好的,现在,看这第四行
214
00:11:40,060 --> 00:11:45,130
4 loops in swift all are 4 in like this.
Swift 中的循环都可以向第四行这样
215
00:11:45,130 --> 00:11:50,500
So if you wanted to go for I equals 1, 2, 10 I plus plus or
如果你想要从 1 加到 2 加到 10 或者加到任何你想要的数值
216
00:11:50,500 --> 00:11:54,000
whatever you would so that with these, ranges like this,
你就可以这样使用range
217
00:11:54,000 --> 00:11:58,810
okay, for IN 27 to 104 or whatever. Okay? And
好的,从27 遍历到104 或者任何其他范围。好的
218
00:11:58,810 --> 00:12:03,040
you can make ranges that are more powerful than just direct
比起直接使用range 你可以用更有力的方法起使用range
219
00:12:03,050 --> 00:12:06,510
ranges. I can't talk about that really on time wise, but
我不能花太多时间讨论这个
220
00:12:06,520 --> 00:12:09,080
again check the documentation and
但是在次检查文档的时候你会看到range 的超出
221
00:12:09,090 --> 00:12:13,050
you'll see out of the ranges, okay? So that's when you see
所以,到那时你就会看到了
222
00:12:13,060 --> 00:12:17,120
that. Okay, let's talk about data structures in Swift.
好的,来谈一谈Swift 中的结构体
223
00:12:17,130 --> 00:12:19,160
I'm talking about classes, structures and enums.
我在讨论类,结构体,和枚举
224
00:12:19,160 --> 00:12:22,500
Okay, you've already seen them in the example. I'm gonna
好的,你们已经在例子里看到它们了
225
00:12:22,500 --> 00:12:25,030
talk about the similarities and the differences.
谈一谈它们的相同和不同
226
00:12:25,030 --> 00:12:27,500
So what's similar between class, struct and enum,
所以,在class,struct 和enum 之间有什么相似之处呢
227
00:12:27,500 --> 00:12:30,270
they're declared the same way, right? Just a keyword,
它们都用相同的方式声明对吧。
228
00:12:30,270 --> 00:12:33,270
the name of the thing and then curly braces.
就同这个东西的名字作为关键字,然后收起它们
229
00:12:33,280 --> 00:12:35,980
They all serve like that way. They're also similar and
它们都提供类似的方法。
230
00:12:35,980 --> 00:12:37,310
that they can also have properties and
它们也都可以有属性和方法
231
00:12:37,310 --> 00:12:40,250
functions. Okay? So you can have functions on there,
所以你可以在这里有方法和属性
232
00:12:40,250 --> 00:12:43,280
and you can have properties. Enums cannot have stored
Enum 不能保存属性
233
00:12:43,290 --> 00:12:46,150
properties. Only structs and classes can have stored
只有结构体和类可以存储属性
234
00:12:46,160 --> 00:12:50,160
properties but enums can have computed properties. Okay?
但是枚举可以包含计算属性
235
00:12:50,160 --> 00:12:52,830
The storage of an enum remember is the cases.
enum 存储的是case
236
00:12:52,830 --> 00:12:55,730
It's a discrete value thing so it's the cases and
所以它的case 和它们的相关值
237
00:12:55,730 --> 00:12:58,530
their associated values that's the storage.
是分开存储的
238
00:12:58,870 --> 00:13:02,140
All right they all can have initializers.
当然它们都可以初始化
239
00:13:02,140 --> 00:13:03,940
Okay, we haven't talked much about initializers yet.
好的,我们还没讨论很多关于初始化的东西
240
00:13:03,940 --> 00:13:07,540
We're gonna get that today. Eh, so they're all allowed
我们今天会来说一说。额
241
00:13:07,540 --> 00:13:09,910
to have initializers except enum. Okay,
所以它们除了enum 都可以初始化
242
00:13:09,910 --> 00:13:12,280
obviously enum doesn't need an initializer because you just
显然enum 不需要初始化
243
00:13:12,280 --> 00:13:16,320
set it to the discrete value with its associated values.
应为你是是设置了一些离散的值和它们相关的值
244
00:13:16,320 --> 00:13:20,450
Okay? All right. Differences. One, inheritance. Okay?
好的。当然,不同之处,一:继承
245
00:13:20,460 --> 00:13:24,860
With classes, you can inherit, structs and enum you can't.
在类中,你可以继承,结构体和枚举不行
246
00:13:24,860 --> 00:13:27,930
Value types, I talked about this before, okay? Structs and
我在这之前介绍过值的类型
247
00:13:27,930 --> 00:13:30,860
enum are value types. They're passed around by value.
结构体和枚举是值的类型。它们通过值来传递
248
00:13:30,870 --> 00:13:33,970
Class is a reference type. You pass pointers to it around. It
类是一种引用类型。通过指针来传递
249
00:13:33,970 --> 00:13:37,910
lives in the heap. Okay? Let's talk a little more about value
它们保存在堆中。好的,来讨论更多关于值和引用
250
00:13:37,910 --> 00:13:42,780
versus reference. Value means that it's copied when passed
值意味着当它作为参数传递个方法是它是个拷贝
251
00:13:42,780 --> 00:13:45,980
as argument to a function, that's kind of obvious. But
这是显而易见的
252
00:13:45,980 --> 00:13:50,480
it also copies when you assign it to another variable.
当你把它分配个另一个变量时它也会拷贝
253
00:13:50,490 --> 00:13:54,250
If I say var x = y if y is a value type,
如果我说var x = y 如果y 是一个值类型
254
00:13:54,260 --> 00:13:55,560
x will be a copy of y.
x 会是一个y 的拷贝
255
00:13:55,560 --> 00:14:00,260
A copy, so if y is an array y might be an array. Okay? And
所以如果y 是一个array
256
00:14:00,260 --> 00:14:04,430
you say, x equals y. And then you say, x append this thing,
然后你说x = y 接着用x 拼接一些东西
257
00:14:04,430 --> 00:14:07,370
that thing will not be appended to y. Because x
这些东西不会被拼接到 y 上去
258
00:14:07,370 --> 00:14:11,070
was a copy of y. You see what I'm talking about there? Okay.
应为 x 是 y 的一个拷贝。你们明白我在说什么吗?好的
259
00:14:11,070 --> 00:14:12,910
So that's a big difference with value semantics.
所以这是它们之间的一个特别大的不同
260
00:14:12,910 --> 00:14:17,880
Even just assigning them, is a copy of them. A value symantec
甚至个它们赋值也是对它们的拷贝
261
00:14:17,880 --> 00:14:22,780
thing is immutable if it's signed to a let variable.
一个用let 分配的值是不可变的
262
00:14:22,790 --> 00:14:24,480
Okay? So if you have an array okay,
如果你有一个数组
263
00:14:24,490 --> 00:14:26,990
since that value symmentic cuz an array is a struct.
因为array 是一个 struct
264
00:14:26,990 --> 00:14:30,220
If you assigned to a let, let x equal an array you cannot
如果你给一个let 赋值,让let x 等于一个数组
265
00:14:30,230 --> 00:14:33,930
append things on to that array cuz it is immutable. Okay,
那你就不能往这个数组上面拼接东西,因为 x 是不可变的
266
00:14:33,930 --> 00:14:38,170
same thing with the dictionary or whatever. Okay. Remember
dictionary 或者其它东西在这一点上也是相同的
267
00:14:38,170 --> 00:14:40,300
the function parameters. All of them are constants so
记住那些方法的参数,它们都是常量
268
00:14:40,300 --> 00:14:44,670
of course you'd copy them into there, you can't modify them.
所以你得把它们拷贝进来,你不能修改它们
269
00:14:45,110 --> 00:14:48,580
Because of the way copy symmentics work, Swift makes
由于copy 的工作方式
270
00:14:48,580 --> 00:14:52,650
you when you do have a struct or enum you have to mark all
Swift 会让你在那些struct 或者enum 里面
271
00:14:52,650 --> 00:14:55,920
functions that might change that thing. Mutating functions
把可能会改变的方法标记出来
272
00:14:55,920 --> 00:15:00,590
with the keyword mutating. So like mutating func whatever.
用mutating 关键字来表示可变方法。
273
00:15:00,590 --> 00:15:02,820
Okay? If that function could change that struct.
如果那个方法可以改变他的结构体
274
00:15:02,830 --> 00:15:05,790
And the reason for that is when Swift copies it
当Swift 拷贝它的时候
275
00:15:05,790 --> 00:15:08,430
it doesn't want to actually make a copy. Okay?
它实际上是不会制作一个备份的
276
00:15:08,430 --> 00:15:11,430
It gets another pointer to it but as soon as you try to
在你试图去改变它的时候
277
00:15:11,430 --> 00:15:15,370
mutate it then it's gonna have to copy it. You see? So
会用一个指针指向它并拷贝这个指针
278
00:15:15,370 --> 00:15:17,640
it's kind of a performance enhancement. So
所以这是一种性能提升
279
00:15:17,640 --> 00:15:20,310
any time you have a struct that has a function that
一旦你的结构体里的方法改变了它的值
280
00:15:20,310 --> 00:15:22,910
changes the values, any of the values of the struct,
这结构体里的任何一个值
281
00:15:22,910 --> 00:15:25,580
you have to put mutating in front so Swift knows
你必须在 Swift 知道你这么做之前将它变为可变的
282
00:15:25,580 --> 00:15:30,220
that you're doing that. Okay, reference types, okay.
好的,引用类型
283
00:15:30,220 --> 00:15:30,850
This is what you're used to,
可能这就是你所用到的
284
00:15:30,850 --> 00:15:32,390
probably. Things are stored in the heap.
东西存放在了堆里
285
00:15:32,390 --> 00:15:35,790
You have a reference to them. Those references are counted
你有一个他们的引用,那些引用会被自动计数
286
00:15:35,790 --> 00:15:39,730
automatically. That means there's no garbage collection
这意味着在Swift 中是没有垃圾回收机制的
287
00:15:39,730 --> 00:15:42,430
in Swift. All right, there's no mark and sweep in
当然,它们没有标记的时候回从堆里面被清除
288
00:15:42,430 --> 00:15:45,370
the heap. Every single time you create a new pointer to
每当你创建一个新的指针指向堆里的某个东西时
289
00:15:45,370 --> 00:15:47,200
something in the heap Swift keeps track of that and
Swift 会把这个痕迹保存下来增加一个计数值
290
00:15:47,200 --> 00:15:49,970
it keeps incrementing a count. And when that count goes down
然后当计数值成为零的时候
291
00:15:49,970 --> 00:15:53,070
to zero because maybe the last pointer that's pointing to it
可能是最后一个指针不再指向它了
292
00:15:53,080 --> 00:15:56,140
goes out of scope or maybe you assign that last pointer to
或者这最后的指针指向了其它什么东西
293
00:15:56,150 --> 00:15:58,910
point to something else. Nothing points to it.
这块内存不再被指向的时候
294
00:15:58,910 --> 00:16:00,510
It immediately removes that from the heap.
它会立马从堆里面被删除
295
00:16:00,520 --> 00:16:03,850
Okay, predictably removed from the heap immediately.
好的,可以预见会很快的从堆里面移除
296
00:16:03,850 --> 00:16:06,320
So, it's very different than garbage collection, where
所以,这和垃圾回收机制非常不同
297
00:16:06,320 --> 00:16:08,960
garbage collection is kind of going on in the background,
垃圾回收是一种在后台运行
298
00:16:08,960 --> 00:16:10,960
and it might collect a whole bunch from the heap,
一次性手机一整串堆里的东西
299
00:16:10,960 --> 00:16:13,390
all at once. That's been sitting around for a while.
那会等上一段时间
300
00:16:13,400 --> 00:16:13,830
This is predictable,
这是可以预测的
301
00:16:13,830 --> 00:16:16,460
memory management and it's all managed for you. Okay,
这是你可以管理的内存管理
302
00:16:16,470 --> 00:16:19,200
there's only one way that you participate in that, which is
你要参与其中只有一种方法
303
00:16:19,200 --> 00:16:22,740
the weak and strong, which I'll be talking about later,
使用weak 和strong。我过会向你们解释
304
00:16:24,370 --> 00:16:26,970
okay? Let's see, constant pointers.
好的,来看指针常量
305
00:16:26,980 --> 00:16:28,680
Okay, if you have a constant pointer to a class,
如果你在一个类里有一个指针常量
306
00:16:28,680 --> 00:16:31,680
obviously it's a pointer, so you're still mutating it,
显然这是一个指针,所以你依然能修改它
307
00:16:31,680 --> 00:16:35,520
right? It's not like a struct where if I say var y = x and
对吗? 这不像我说的var y = x 的那种结构
308
00:16:35,520 --> 00:16:38,190
then I add something to y, it doesn't modify x.
当我想要给 y 加点什么的时候,这并不会修改 x
309
00:16:38,190 --> 00:16:41,560
If I say var y = x and they're classes, then if I send
若果我说 var y = x 而且它们都是类
310
00:16:41,560 --> 00:16:44,220
a message to y, it's sending a message to x because they're
然后如果我想 y 发送消息,这会个 x 发送一个消息,因为它们是同一个东西
311
00:16:44,230 --> 00:16:46,630
the same thing. Right they, that same thing lives in
同一个在堆里保存的东西
312
00:16:46,630 --> 00:16:50,700
the heat. There's no copying, so let, all let means is that
没有拷贝,所以let
313
00:16:50,700 --> 00:16:53,430
pointer's not going to change. It doesn't
所有的let 意味着那个指针是无法被修改的
314
00:16:53,440 --> 00:16:56,370
mean what the pointer points to won't change.
这不意味着指针指向的东西无法被修改
315
00:16:56,810 --> 00:17:01,380
Okay obviously so when you pass a pointer to a class
显然,当你把指针当做一个参数传递给类的时候
316
00:17:01,380 --> 00:17:04,440
as an argument, then it does not make a copy, it's just
它不会产生一个拷贝
317
00:17:04,450 --> 00:17:08,580
passing a pointer. Okay. Now how do you know which to use?
它就传递了一个指针。好的,现在你知道该怎么用了吗?
318
00:17:08,580 --> 00:17:10,120
Struct versus class, specially.
特别的,结构和类
319
00:17:10,120 --> 00:17:13,890
Okay, enum is pretty obvious. But struct versus class well.
enum 是很明显的。除了结构和类
320
00:17:13,890 --> 00:17:15,890
Usually you're gonna choose class over struct.
通常你会在类和结构中选一个
321
00:17:15,890 --> 00:17:17,790
Okay. Because this is object oriented program.
因为这是面向对象编程
322
00:17:17,790 --> 00:17:19,630
You use to do it and that's going to be fine.
你这样使用会更好一点
323
00:17:19,630 --> 00:17:22,360
Struct is gonna be used for more fundamental types.
Struct 更多被用作基础类型
324
00:17:22,360 --> 00:17:25,370
Okay, things like strings and doubles, and ints and arrays
向是string 和double ,int 和array 在diction 里
325
00:17:25,370 --> 00:17:28,500
in dictionaries, and also for drawing, points, rectangles.
也会为了绘图有点,矩形
326
00:17:28,500 --> 00:17:30,740
Do you see what I'm saying, smaller things that
你知道我在时候什么,一个更小的独立的东西
327
00:17:30,740 --> 00:17:33,840
are self-contained, that it makes sense to copy by value.
用在需要复制值的场景上
328
00:17:33,840 --> 00:17:36,340
You want the value semantic, that's gonna be your primary
你要这值的语言。当你在传递这值的时候
329
00:17:36,350 --> 00:17:38,110
reason for choosing struct as you want values
你想要这些值的语义
330
00:17:38,110 --> 00:17:41,950
semantics when you're passing this thing around. Otherwise,
会成为你选在struct 的主要原因
331
00:17:41,950 --> 00:17:45,090
you're gonna choose classes, okay? Anything big is almost
否则你就选类
332
00:17:45,090 --> 00:17:50,660
certainly gonna use the class, all right? Okay.
任何大型的东西肯定会使用类,对吗?
333
00:17:50,660 --> 00:17:53,730
On the methods. So now we are gonna talk about the syntax
好的。在这些方法里,我们要来说说这些方法的语法
334
00:17:53,730 --> 00:17:57,360
of methods. Okay, I showed you this in class a little bit but
在这个类里我会展示一点
335
00:17:57,370 --> 00:18:00,370
I'm gonna talk about the full semantics here of it.
我还会介绍它在这里的完整的语义
336
00:18:00,370 --> 00:18:04,140
So interesting about the naming of functions, so
这些方法的命名挺有趣的
337
00:18:04,140 --> 00:18:06,870
here is someone defining a method and here is someone
这里定义了一个方法
338
00:18:06,880 --> 00:18:10,440
calling this method. Okay? Okay, so watch this colored
这里调用了这个方法。好的?好,在我讨论这几种东西的时候
339
00:18:10,450 --> 00:18:15,280
stuff going on as I talk about these various things going on.
注意这些高亮的部分
340
00:18:15,280 --> 00:18:19,650
So all parameters to functions have an internal name and
所有方法的参数都有一个内部的名字和一个外部的名字
341
00:18:19,660 --> 00:18:21,620
an external name, every single parameter.
是每一个参数都有
342
00:18:21,620 --> 00:18:24,590
So here's the first parameter, here's the second parameter,
那么这里是第一个参数,这里是第二个参数
343
00:18:24,590 --> 00:18:28,300
they both have an external name and an internal name.
它们都有一个外部的名字和一个内部的名字
344
00:18:28,300 --> 00:18:31,200
Okay? The external name comes first. Internal name comes
好的。先说外部名字,在说内部名字
345
00:18:31,200 --> 00:18:35,640
second. The internal name is the name of the local
内部名字是在你的方法里面的本地变量的命名
346
00:18:35,640 --> 00:18:39,770
variable that's gonna be used inside your method.
347
00:18:39,780 --> 00:18:41,540
All right? So that's this one right here,
所以这就是内部命名
348
00:18:41,540 --> 00:18:44,880
internal name. Shows up right before the colon. Okay?
就是出现在这冒号之前的东西
349
00:18:44,880 --> 00:18:46,750
First and second. You can see how first and
第一个和第二个
350
00:18:46,750 --> 00:18:50,720
second are the names I used for those inside my function.
你可以看到在我的方法里我是如何给第一个和第二个起名的
351
00:18:50,720 --> 00:18:53,090
Don't appear anywhere here. Notice first and
不要出现在别的地方。
352
00:18:53,090 --> 00:18:56,660
second do not appear in the funk bar call. Okay,
注意第一个和第二个不在方法块后面出现
353
00:18:56,660 --> 00:19:00,190
cuz those are the internal names. The external name
因为它们是内部的名字
354
00:19:00,200 --> 00:19:04,760
is what the caller uses when they call this method. Okay,
外部名字是这个方法的调用者使用的。
355
00:19:04,770 --> 00:19:09,200
so you can see bar down there. It says externalFirst: 123,
你可以看到这里有个下划线。这表示 外部第一个参数:123,
356
00:19:09,200 --> 00:19:14,170
externalSecond: 5.5, right? Okay, it's using the external
外部第二参数:5.5,好的
357
00:19:14,180 --> 00:19:16,110
names. first and second never appear but
first和second并没有出现
358
00:19:16,110 --> 00:19:19,480
first and second are still used in the implementation,
但first和second在代码实现中仍然被使用了,
359
00:19:19,480 --> 00:19:22,850
those in the internal names. Okay,
可以在方法里用它们的内部名字
360
00:19:22,850 --> 00:19:26,020
you can put an under bar if you don't want callers
如果你不想让你的传递参数使用外部的名字
361
00:19:26,020 --> 00:19:30,560
to use an external name at all for a given parameter. Okay,
你可以使用 _ 来替代它们
362
00:19:30,560 --> 00:19:34,690
so if you put underbar as the external name then there will
所以你要是在外部名字的地方放了一个 _ 那这里有不会有外部参数
363
00:19:34,700 --> 00:19:39,330
be no external parameters. So you see it says foo(123), not
比如你调用 foo(123)
364
00:19:39,330 --> 00:19:44,170
foo(externalFirst or anything, it's just nothing, 123. And
不用调用 foo(外部第一个参数名或者其它什么东西,什么都不用,就写123
365
00:19:44,170 --> 00:19:49,140
in fact, this is the default for the first parameter.
实际上,第一个参数默认就是这么做的
366
00:19:49,140 --> 00:19:51,850
The first parameter defaults to under bar, you don't need
第一个参数默认就是 _
367
00:19:51,850 --> 00:19:54,510
to put an under bar there when you're creating a function.
当你创建一个方法的时候你不需要再在那里放 _
368
00:19:54,520 --> 00:19:57,850
Just by default an under bar would be put there for you,
那里默认就为你放了一个 _
369
00:19:57,850 --> 00:20:01,790
okay? Now you could specify an external name for the first,
现在你指定第一个外部参数的名字,这是合法的
370
00:20:01,790 --> 00:20:04,820
it's legal. Okay, I'll show you that in a second. But
我会展示第二个怎么做
371
00:20:04,830 --> 00:20:07,730
by default under bar is the external name for the first
默认的 _ 只有第一个外部命名才有
372
00:20:07,730 --> 00:20:11,900
parameter only. All the other ones. Okay? The default is
那么所有其它的是什么呢?
373
00:20:11,900 --> 00:20:15,500
the internal name. So, if you don't have an external name,
它们默认都是内部名字。所以如果你不想要一个外部名字
374
00:20:15,500 --> 00:20:21,680
then it defaults to being the internal name. Got it?
它们默认会是一个内部的名字。明白了吗?
375
00:20:21,680 --> 00:20:25,380
Okay. Any parameter's external name can be changed, okay?
好的。任何参数的外部名字都是可以修改的
376
00:20:25,380 --> 00:20:28,580
You can change even the second one to be underbar, so
你甚至可以把第二个改成 _
377
00:20:28,580 --> 00:20:32,320
that the second one has no name, okay?
这样第二个就没有名字了,对吗?
378
00:20:32,320 --> 00:20:35,690
But don't do this, okay? It's very anti Swift
但是不要这样做,好吗?
379
00:20:35,690 --> 00:20:39,890
to either remove the second parameter name or
把第二个参数移除或者添加到第一个参数,是很反 Swift 的
380
00:20:39,900 --> 00:20:43,500
to add it back to the first. Adding to the first is
添加到第一个可能不是很反 Swift
381
00:20:43,500 --> 00:20:46,400
probably less anti Swift, there is probably some case
可能在某些场景里会需要这么做
382
00:20:46,400 --> 00:20:49,200
where that might actually make some sense. Removing them for
但是后面要把它们删掉也是非常麻烦的
383
00:20:49,200 --> 00:20:51,740
subsequent ones is probably really bad. Okay,
384
00:20:51,740 --> 00:20:55,310
I don't think I've ever had to do that in my entire life of
在我用 Swift 写程序的时候,我不记得我曾经这么做过
385
00:20:55,310 --> 00:20:55,680
writing swift so.
386
00:20:55,680 --> 00:21:00,350
All right now, methods are obviously overriddeable,
现在,方法是可以被覆盖的
387
00:21:00,350 --> 00:21:04,280
this is object orientated programming. Right and the way
因为这是面向对象编程
388
00:21:04,290 --> 00:21:07,190
you do it, you just put the word override in front of
你只需要在 func 或者 var 前面加 override 关键字
389
00:21:07,190 --> 00:21:10,120
the func or var. Both properties and functions can
属性和方法都是可以被覆盖的
390
00:21:10,130 --> 00:21:12,960
be overridden right, so you put override in there. It is
所以你把 override 放在这里
391
00:21:12,960 --> 00:21:18,200
possible to mark a method or a property final in which case
它可以在子类的情况里用来标记方法和属性
392
00:21:18,200 --> 00:21:21,270
no one can subclasses. Okay, there won't be a list,
一个子类不能在类前放 override
393
00:21:21,270 --> 00:21:24,740
a subclass can't say override that thing. The compiler won't
编译器不允许这么做
394
00:21:24,740 --> 00:21:29,110
let them. Okay, you can also mark an entire class final,
你也可以标记整个类为final
395
00:21:29,110 --> 00:21:33,980
and then the whole thing is not subclassable. All right?
然后整个类就不能有子类了
396
00:21:33,980 --> 00:21:39,090
Now, both types and instances can have methods and
现在类和对象都可以有方法和属性
397
00:21:39,090 --> 00:21:42,060
properties. What does that mean? So, so
这意味着什么呢
398
00:21:42,060 --> 00:21:43,560
far in all the demos we've done,
现在我们已经完成了所有的演示
399
00:21:43,560 --> 00:21:45,990
all you've seen is instance methods and
你看到的都是对象方法和对象属性
400
00:21:45,990 --> 00:21:47,330
instance properties. In other words,
换言之
401
00:21:47,330 --> 00:21:50,530
they are properties that you get from an instance of
它们是你从一个对象中拿出来的属性
402
00:21:50,530 --> 00:21:53,100
the thing. Okay, an instance of the calculator brain.
好的,一个计算器大脑的对象
403
00:21:53,100 --> 00:21:58,070
Someone actually created a calculator brain in the heap
实际上你已经在堆里创建了一个计算器的大脑并且给它发送消息
404
00:21:58,070 --> 00:22:01,510
and you send it messages, okay. Those are instances
那些事对象消息
405
00:22:01,510 --> 00:22:05,910
messages, Instances methods and instances properties. But
对象方法和对象属性
406
00:22:05,910 --> 00:22:07,180
it is possible to have methods and
然而在它的类里它还能有自己的方法和属性
407
00:22:07,180 --> 00:22:10,420
properties on the type itself, so calculator brain,
所以在计算器大脑这个类里面它可以有方法,对吗
408
00:22:10,420 --> 00:22:14,250
the type can have functions, okay? And here's what
这里是调用他们时的样子
409
00:22:14,260 --> 00:22:17,620
it looks like to call them. So here I have a little example.
我这里有一个小例子
410
00:22:17,630 --> 00:22:20,890
Here I have a var d which is a double, okay?
我这里有有一个 double 的变量 b
411
00:22:20,900 --> 00:22:24,460
And it's set to some value. So this d is an instance of
然和我给它设置一些值,所以这个d 是一个double 的实例
412
00:22:24,470 --> 00:22:26,830
a double. It is an actual double that has some value,
这个double 实际上有一些值
413
00:22:26,840 --> 00:22:30,740
all right? And here I'm checking by sending a message,
我通过发送一个消息来检查这一点
414
00:22:30,740 --> 00:22:34,240
an instance method right here. Or probably a property
它是一个对象的方法,也可能是一个属性
415
00:22:34,240 --> 00:22:38,380
actually. Instance property on D, where I'm asking
对象的 D 属性如果是负数的
416
00:22:38,380 --> 00:22:41,380
if it's signed is the minus sign, I'm just making this up,
我就把它变成正数
417
00:22:41,380 --> 00:22:43,820
there's probably not even such a method on double.
在double 里面可能没有这样一个方法
418
00:22:43,820 --> 00:22:47,920
But that would be an instance property or method and
这会是属性或对象
419
00:22:47,920 --> 00:22:48,760
I would be sending it to D,
并且我会把它发给D
420
00:22:48,760 --> 00:22:52,260
which is an instance. If it does have a minus sign,
如果它没有负号
421
00:22:52,260 --> 00:22:55,700
I'm going to use a type property. Or type function,
我会使用一个类属性
422
00:22:55,700 --> 00:23:00,970
rather, of double here, to change its probably absolute
或者在 double 里的这个类方法里会把它变成绝对值
423
00:23:00,970 --> 00:23:03,800
value, so it'd probably change its sign there. Okay.
所以我可能在这里改变它的符号
424
00:23:03,810 --> 00:23:07,140
Notice the syntax here, Double.abs, okay.
好的。注意这里的语法 Double.abs
425
00:23:07,140 --> 00:23:09,640
This is the name of the function abs, okay, and
这个方法的名字叫 abs
426
00:23:09,640 --> 00:23:13,080
I'm accessing it by saying Double. So in other words, I'm
然后我通过向 Double 发消息来访问它
427
00:23:13,080 --> 00:23:17,320
putting the name of the class. Not an instance of a class,
换句话说,我把这个类名放在这里。不是一个类的对象
428
00:23:17,320 --> 00:23:19,850
not a variable of that class, but the actual name of
也不是一个类的变量,而是一个类的名字
429
00:23:19,860 --> 00:23:24,460
the class, dot abs. Okay, that's it's gonna be defined,
.abs 好的。那个 abs 会被定义
430
00:23:24,460 --> 00:23:29,700
that abs, will be defined like this. Static func abs.
会用这种方法定义 Static func abs
431
00:23:29,700 --> 00:23:32,800
This would be in the class double. And you put this word
这会在 double 类的里面
432
00:23:32,800 --> 00:23:37,270
static and that says that you don't send this to double
在这里放关键字 static 意味着你不会把它发送给 double 对象
433
00:23:37,270 --> 00:23:41,040
instances. You send it to the Double class by saying capital
你会用大写的 D ,Double 点,发送给 Double 这个类
434
00:23:41,040 --> 00:23:45,680
D Double dot. Okay? Why do we have these things? Okay these
为什么我们会有这些东西?
435
00:23:45,680 --> 00:23:49,880
are usually utility functions kind of global functions but
我们经常会需要一些全局的功能函数
436
00:23:49,890 --> 00:23:52,820
they're really strongly associated with the class. So
但是他们和这些类有很强的关联的
437
00:23:52,820 --> 00:23:54,350
we put them as class methods, okay?
所以我们把它们放到类方法里面,好吗?
438
00:23:54,360 --> 00:23:58,090
That's what they're for, mostly. Maybe you have a class
这是它们存在的主要原因
439
00:23:58,090 --> 00:24:02,530
method that creates Shared instances of a class, okay.
或许你会有个类方法来创建一个类的共享对象
440
00:24:02,530 --> 00:24:05,630
Something like that, we'll see a few of these.
像这样,我们会看到这些
441
00:24:05,630 --> 00:24:06,530
Once you start to see them in action,
一旦你在实践中看见他们
442
00:24:06,540 --> 00:24:08,270
you'll start to understand. But I just wanna make sure you
你就会明白的。
443
00:24:08,270 --> 00:24:10,800
understand that there is a difference between these two
但是我想确保你们理解这两者之间是不同的
444
00:24:10,810 --> 00:24:13,470
and this static that makes a difference on declaration,
这个static 在声明的时候是不同的
445
00:24:13,480 --> 00:24:16,380
okay. That's also in the reading assignment, make sure
好的。那些也在阅读作业里面
446
00:24:16,380 --> 00:24:19,180
you read that carefully. In the homework assignment,
确保你仔细的阅读过了
447
00:24:19,180 --> 00:24:23,120
I even say be careful to read this carefully. All right,
在作业里面我甚至也说了仔细阅读
448
00:24:23,120 --> 00:24:25,420
properties, okay. Properties are really cool.
好的。属性是很酷的
449
00:24:25,420 --> 00:24:27,890
In Swift we're only just scratching the surface so
在Swift 里我们才刚刚揭开它的表面远不及它所能做的
450
00:24:27,890 --> 00:24:31,220
far of what they can do. But one really cool thing about
但是有一件关于它的很酷的是
451
00:24:31,230 --> 00:24:35,830
them is that you can observe changes to a property, okay.
你可以观察一个属性的改变
452
00:24:35,830 --> 00:24:39,430
And you need to do that by, here's your var, okay. This is
而且你需要这样做,这是你的变量。
453
00:24:39,430 --> 00:24:41,570
a stored property, this is not a computed property,
这是存储的属性,这不是一个计算的属性而是用来存储的
454
00:24:41,570 --> 00:24:45,310
it's stored, okay. It stores in ints, its value is 42. But
它存在int 里,这个值是 42。
455
00:24:45,310 --> 00:24:47,970
I'm putting the curly braces after it, not because I'm
我把大括号放在它的后面,我要去得到和设置它
456
00:24:47,980 --> 00:24:51,010
gonna do get and set, I'm not computing it. But
我不去计算它
457
00:24:51,010 --> 00:24:56,150
I'm gonna add willSet and/or didSet, okay. So willSet and
但是我会使用willSet 和didSet。好的
458
00:24:56,150 --> 00:24:59,050
didSet, this is just code in here that's gonna get called
willSet 和didSet,当调用get 和set 时就会调用者两个方法
459
00:24:59,050 --> 00:25:04,260
whenever this thing gets set. Okay, before and after. Before
好的,在这之前或在这之后。
460
00:25:04,260 --> 00:25:08,230
it gets set, after it's been set, okay. And inside there
这get 之前和set 之后,好的
461
00:25:08,230 --> 00:25:11,100
newValue is going to be the value it's going to be set to
在设置到willset 的时候newValue 就是要设置的值
462
00:25:11,100 --> 00:25:14,500
in willSet. And oldValue is this value it used to have in
当你执行didSet,oldValue 就是已经存储着的值
463
00:25:14,500 --> 00:25:19,770
did, when you're executing in didSet, okay. So, pretty cool.
好的,非常酷
464
00:25:19,770 --> 00:25:23,280
And you can do that for stored properties, you can do it for
你可以这样来存储属性,你也可以对继承的属性这么做
465
00:25:23,280 --> 00:25:25,880
inherited properties, properties you inherit even,
你甚至可以继承属性
466
00:25:25,880 --> 00:25:29,550
okay. And you can do it for computed properties as well,
你也可以用它来计算属性
467
00:25:29,550 --> 00:25:33,720
okay. Also here, this will be,
好的
468
00:25:33,720 --> 00:25:37,420
one thing to note about this is these willSets and didSets,
这些 willSet 和 didSet 要注意的是
469
00:25:37,430 --> 00:25:41,660
if you mutate a value type they will get called,
如果你改变一个值的类型 get 会被调用
470
00:25:41,660 --> 00:25:45,600
okay. So, operations here from our calculator brain,
所以我们对计算器大脑的操作放在这里
471
00:25:45,600 --> 00:25:49,100
dictionary string operation that is a value type,
字典字符串操作是一个值类型
472
00:25:49,100 --> 00:25:51,740
dictionary is a value type, right. So
字典是一个值类型
473
00:25:51,740 --> 00:25:54,010
if you add something to this dictionary,
所以如果你往字典里加了什么
474
00:25:54,010 --> 00:25:57,580
this will get called. willSet and didSet will get called,
会调用 get。willSet 和 didSet 会被调用
475
00:25:57,580 --> 00:26:00,310
okay. That's not true if it's a reference type, okay.
如果它是一个引用类型的话这么做是无效的
476
00:26:00,320 --> 00:26:03,180
If that if this property isn't a class and
如果这属性不是类而且你修改了类的某个地方
477
00:26:03,180 --> 00:26:06,120
you change the class somehow, it can't detect that, so
它是不会检测到的
478
00:26:06,120 --> 00:26:08,390
it doesn't do willSet and didSet, okay.
所以它不会执行 willSet 和 didSet
479
00:26:08,390 --> 00:26:13,930
You have to change the pointer itself. All right, okay.
你必须改变指针本身
480
00:26:13,930 --> 00:26:17,100
One very common thing to do in these willSets and didSets is
通常 willSet 和 didiSet 的用途是更新 UI
481
00:26:17,100 --> 00:26:20,700
update the UI. You got a method in your controller.
你在你的控制器里得到一个方法
482
00:26:20,700 --> 00:26:22,440
You set some value in controller that changes
你改变了控制器某处的值就会在 UI 上显示出来
483
00:26:22,440 --> 00:26:25,170
the way the UI would look. You ask your view to
你要求你的 View 重绘
484
00:26:25,170 --> 00:26:27,370
draw itself again, okay. Very, very common.
好的。非常非常常见
485
00:26:27,380 --> 00:26:30,180
We're gonna be doing that up the wazoo in the next lecture,
我们会在下一节使用它们
486
00:26:30,180 --> 00:26:35,050
okay. All right. Lazy initialization. So, this is
好的。懒加载
487
00:26:35,050 --> 00:26:38,480
a little bit of a cheat code of Swift right here.
这是 swift 种有点欺骗性的代码
488
00:26:38,490 --> 00:26:43,590
You can declare vars to be lazily instantiated.
你可以声明一个懒加载的变量
489
00:26:43,590 --> 00:26:47,690
Which means that, their value, this equals calculator brain,
这意味它们等于计算器的大脑实际上不会执行
490
00:26:47,700 --> 00:26:51,430
will not actually happen. That assignment will not happen
知道有些地方需要调用它了才会执行分配
491
00:26:51,430 --> 00:26:53,700
until someone asks for the brain, okay.
好的
492
00:26:53,700 --> 00:26:56,640
If someone tries to send a message to this brain or
若果有什么动向想个这个大脑发一个信息
493
00:26:56,640 --> 00:26:57,970
get a property to this brain,
或者从这个大脑里拿到一个信息
494
00:26:57,970 --> 00:26:58,870
then it's gonna get initialized.
然后它就会被初始化
495
00:26:58,870 --> 00:27:01,370
So in other words, it's lazily instantiated, okay.
换句话说,这就是懒加载
496
00:27:01,380 --> 00:27:04,810
And you can do it like that, okay. Just lazy var brain,
而且你可以像这样做。就 lazy var brain
497
00:27:04,810 --> 00:27:08,150
calculatorbrain. You can do down here, okay.
你可以在这里做
498
00:27:08,150 --> 00:27:12,250
Lazy var myProperty = self.initializedMyProperty.
Lazy var myProperty = self.initializedMyProperty.
499
00:27:12,250 --> 00:27:16,220
So, I'm calling a method on myself to initialize one of my
所以我告诉我自己去初始化我的一个属性
500
00:27:16,220 --> 00:27:20,030
properties. Normally, this would be illegal
501
00:27:20,030 --> 00:27:22,730
without the lazy. The reason this would be illegal
502
00:27:22,730 --> 00:27:25,500
without the lazy is that you cannot send any messages to
503
00:27:25,500 --> 00:27:28,800
yourself or access any of your properties until you're fully
504
00:27:28,800 --> 00:27:32,840
initialized, okay. And this is part of your initialization,
505
00:27:32,840 --> 00:27:34,710
so it's a catch 22, you can't do it. But
506
00:27:34,710 --> 00:27:38,780
if you make this lazy, now you can. Because it's not actually
507
00:27:38,780 --> 00:27:41,150
going to happen until after you're fully initialized and
508
00:27:41,150 --> 00:27:44,680
someone comes along and tries to access myProperty, okay. No
509
00:27:44,690 --> 00:27:47,750
one can access myProperty for your initialize anyway. So,
510
00:27:47,760 --> 00:27:50,590
once you're fully initialized, I'm gonna access myProperty,
511
00:27:50,590 --> 00:27:54,830
lazy, boom. This method will get called to initialize you,
512
00:27:54,830 --> 00:27:57,460
got it? And how about this one in the middle here,
513
00:27:57,470 --> 00:28:02,570
some property of this given type equals a closure
514
00:28:03,040 --> 00:28:04,340
with open parentheses,
515
00:28:04,340 --> 00:28:07,640
closed parentheses on the end. This just means,
516
00:28:07,640 --> 00:28:13,080
initialize this by executing the code inside this closure,
517
00:28:13,080 --> 00:28:15,420
okay. It's very similar to doing
518
00:28:15,420 --> 00:28:18,080
self.initializeMyProperty but you're putting the code
519
00:28:18,090 --> 00:28:20,320
in this closure instead of putting it in initialize
520
00:28:20,320 --> 00:28:24,060
myProperty, whatever that method is, okay. Now,
521
00:28:24,060 --> 00:28:28,330
normally inside here, okay. If this weren't lazy,
522
00:28:28,330 --> 00:28:31,600
you would not be able to access self in here.
523
00:28:31,600 --> 00:28:35,070
Because again, this is a part of initialization, you can't
524
00:28:35,070 --> 00:28:37,970
do anything with self until you're fully initialized. So
525
00:28:37,970 --> 00:28:38,940
if you tried to put self in here,
526
00:28:38,940 --> 00:28:42,210
the compiler will complain. But if you make it lazy,
527
00:28:42,210 --> 00:28:46,250
now you can access self in here. Got it?
528
00:28:46,250 --> 00:28:49,080
Call your own methods or whatever you wanna do, okay.
529
00:28:49,080 --> 00:28:52,690
So that's lazy. The reason this is kind of a cheat code
530
00:28:52,690 --> 00:28:57,120
is that this lazy business satisfies the requirement
531
00:28:57,130 --> 00:29:00,690
that all your vars have to be initialized, okay. So even
532
00:29:00,700 --> 00:29:03,860
these, though these things are being initialized lazily,
533
00:29:03,870 --> 00:29:05,570
they're still being initialized for
534
00:29:05,570 --> 00:29:08,130
the purposes of that rule that all your vars have to be
535
00:29:08,140 --> 00:29:11,200
initialized. This kind of will often keep you from having to
536
00:29:11,210 --> 00:29:13,210
write an initializer. You won't need an init,
537
00:29:13,210 --> 00:29:16,010
because of this lazily do it, okay.
538
00:29:16,010 --> 00:29:19,180
That's why I say it's kind of a cheat code a little bit,
539
00:29:19,180 --> 00:29:22,220
okay. One thing about lazy, they always have to be var.
540
00:29:22,220 --> 00:29:26,120
So there's no such thing as lazy let, okay. Lazy var
541
00:29:26,120 --> 00:29:30,760
only, okay. You can also use laziness to get around some
542
00:29:30,760 --> 00:29:34,090
dependency issues, if you have one var depends on another. If
543
00:29:34,100 --> 00:29:36,960
you can make one of them lazy, then that'll be fine, right.
544
00:29:36,970 --> 00:29:40,400
Cuz the lazy one will call the one that gets pre-initialized,
545
00:29:40,400 --> 00:29:45,270
okay. All right, let's talk about array.
546
00:29:45,270 --> 00:29:48,240
Okay, I'm not gonna spend much time on this cuz array you're
547
00:29:48,240 --> 00:29:49,640
used to array, you know what array is.
548
00:29:49,640 --> 00:29:52,040
In other languages, it's pretty much the same in Swift.
549
00:29:52,050 --> 00:29:54,910
So I'm just gonna try and cover the syntax a little bit.
550
00:29:54,920 --> 00:29:57,550
One thing is we've been using this kind of syntax for
551
00:29:57,550 --> 00:30:01,790
dictionary, right, dictionary angle, extreme colon
552
00:30:01,790 --> 00:30:06,090
operation, remember that? This is the formal
553
00:30:07,200 --> 00:30:10,400
syntax for an array but this square bracket,
554
00:30:10,400 --> 00:30:13,130
string closed square bracket is more informal and
555
00:30:13,130 --> 00:30:17,900
it's the one we use, okay. So as we go forward, we're gonna
556
00:30:17,910 --> 00:30:23,010
use [string] to mean the type array of string, okay.
557
00:30:23,010 --> 00:30:28,710
And similarly for dictionary, [key:value]. That is
558
00:30:28,720 --> 00:30:34,690
dictionary with keys of this and values of that type, okay.
559
00:30:34,690 --> 00:30:35,960
Now, this is the type,
560
00:30:35,960 --> 00:30:38,690
so the open parenthesis close parenthesis on the end just
561
00:30:38,690 --> 00:30:41,590
means that I'm calling the initializer with no arguments.
562
00:30:41,600 --> 00:30:44,600
Same as up here. So don't get confused by that, okay.
563
00:30:44,600 --> 00:30:46,530
So that just means I'm initializing,
564
00:30:46,530 --> 00:30:50,140
I'm creating an instance of an array of string, okay. That's
565
00:30:50,140 --> 00:30:54,270
what both of those mean. So, I can also create arrays and
566
00:30:54,280 --> 00:30:58,240
initialize them by just saying some variable like animals
567
00:30:58,250 --> 00:31:02,110
there which is constant let animals = [ list a bunch of
568
00:31:02,120 --> 00:31:05,320
things ]. That creates an array of those things.
569
00:31:05,320 --> 00:31:08,420
And notice I don't specify any type here.
570
00:31:08,420 --> 00:31:10,790
Why, why do I get away with that? What does Swift do?
571
00:31:10,790 --> 00:31:15,900
It infers type. So, we would look at this and say string,
572
00:31:15,900 --> 00:31:16,960
string, string, string all right.
573
00:31:16,970 --> 00:31:20,430
This is an array of string. Now if I went giraffe, cow,
574
00:31:20,440 --> 00:31:24,970
three, bird, then Swift would complain, can't infer the type
575
00:31:24,970 --> 00:31:28,010
of that array, because it's not all the same type.
576
00:31:28,010 --> 00:31:30,910
Okay, so we'd infer it here. Now, notice this
577
00:31:30,910 --> 00:31:34,380
animals.append("Ostrich"), if I put an Ostrich in here,
578
00:31:34,380 --> 00:31:37,520
this will not compile, okay. Compiler will say no,
579
00:31:37,520 --> 00:31:41,420
that's because animals is let, it's a constant.
580
00:31:41,420 --> 00:31:45,020
So this is a constant array, it can not be modified.
581
00:31:45,030 --> 00:31:50,460
If I put var right here, no problem, it would work, okay.
582
00:31:50,470 --> 00:31:53,670
Now, if I say let animal = animals[5].
583
00:31:53,670 --> 00:31:57,440
That's gonna crash my program. Array index out of bounds,
584
00:31:57,440 --> 00:31:59,910
just like most Programming systems do. It's one
585
00:31:59,910 --> 00:32:03,210
of the few cases that you can crash your program in Swift.
586
00:32:03,210 --> 00:32:06,880
We know unwrapping optionals is one, this is another one.
587
00:32:06,880 --> 00:32:08,010
If you array index out of bounds,
588
00:32:08,020 --> 00:32:13,250
it will crash, okay? How do you enumerate array?
589
00:32:13,250 --> 00:32:16,020
I wanna look at everything in an array. Yeah, I could do for
590
00:32:16,020 --> 00:32:19,990
with a range 0.., the length of the array, right?
591
00:32:19,990 --> 00:32:22,400
0.., less than sign, length of the array. But
592
00:32:22,400 --> 00:32:24,260
that's not how you would do it, you would just say, for
593
00:32:24,270 --> 00:32:28,900
animal in animals, okay? This is gonna loop through, and
594
00:32:28,900 --> 00:32:30,970
this local variable is gonna be assigned each
595
00:32:30,970 --> 00:32:35,840
of the animals, okay? That's how you enumerate an array.
596
00:32:36,210 --> 00:32:38,280
All right, interesting Array methods.
597
00:32:38,280 --> 00:32:40,550
Okay, I'm not gonna spend a lot of time on this slide.
598
00:32:40,550 --> 00:32:43,050
But I really want you to understand that these methods
599
00:32:43,050 --> 00:32:45,580
are here, okay, and you're gonna wanna use them.
600
00:32:45,590 --> 00:32:46,490
They will really clean up your code.
601
00:32:46,490 --> 00:32:49,590
They'll keep you from doing a lot of code, this like for
602
00:32:49,590 --> 00:32:52,960
enumerate through my array, do this, if then, this then,
603
00:32:52,960 --> 00:32:56,200
collect it in another array, all that kinda stuff you can
604
00:32:56,200 --> 00:32:59,000
put in one liners, okay? So what do these things do?
605
00:32:59,000 --> 00:33:04,070
These things use closures to operate on arrays. So filter,
606
00:33:04,070 --> 00:33:06,710
okay, is a method in Array. You send it to it.
607
00:33:06,710 --> 00:33:11,710
You provide a closure, which takes whatever type is in the,
608
00:33:11,710 --> 00:33:14,510
in the array and returns a bool. And it just
609
00:33:14,520 --> 00:33:18,920
executes that closure on every single element of the array,
610
00:33:18,920 --> 00:33:19,820
and it includes the ones
611
00:33:19,820 --> 00:33:23,390
where you return true from your closure. And it excludes
612
00:33:23,390 --> 00:33:26,590
the ones you don't, until filter returns a new array,
613
00:33:26,590 --> 00:33:30,800
which is only the things that match your closure, okay? So
614
00:33:30,800 --> 00:33:33,670
here for example, let bigNumbers = this
615
00:33:33,670 --> 00:33:36,840
array.filter, here's my closure,
616
00:33:36,840 --> 00:33:40,640
$0 is this thing that's gonna be for each of these,
617
00:33:40,640 --> 00:33:42,880
called on each of these. If $0 is greater than 20,
618
00:33:42,880 --> 00:33:47,280
I'm gonna return true. So this returns an array, bigNumbers,
619
00:33:47,280 --> 00:33:51,380
that only has 47 and 118, cuz those are the only
620
00:33:51,390 --> 00:33:54,550
two things greater than 20, got it? Okay,
621
00:33:54,560 --> 00:33:57,520
there's another one simpler, similar, which is map.
622
00:33:57,530 --> 00:34:01,890
Map takes your closure and your closure converts each
623
00:34:01,900 --> 00:34:04,860
element in the array to something else.
624
00:34:04,870 --> 00:34:07,400
So if you had an array of ints and you wanted it
625
00:34:07,400 --> 00:34:10,370
to be an array of strings, you could do it with this,
626
00:34:10,370 --> 00:34:14,470
okay map, closure. Notice, by the way,
627
00:34:14,480 --> 00:34:16,880
no parentheses here. You see, map, okay,
628
00:34:16,880 --> 00:34:21,050
map takes an argument, which is a closure there. But I have
629
00:34:21,050 --> 00:34:23,980
no parentheses after map around the curly braces like I
630
00:34:23,990 --> 00:34:27,690
had up here with filter. I put this in here to show you that
631
00:34:27,690 --> 00:34:30,720
those parentheses are optional when a closure
632
00:34:30,730 --> 00:34:33,290
is the last argument to a function, okay?
633
00:34:33,290 --> 00:34:36,230
When a closure is the last argument to the function,
634
00:34:36,230 --> 00:34:40,000
it can be outside of the function's parentheses, okay,
635
00:34:40,000 --> 00:34:41,230
and live on its own out here.
636
00:34:41,240 --> 00:34:45,140
Okay, here's another function, okay, called reduce.
637
00:34:45,140 --> 00:34:49,680
This reduces the array to a single result. Okay, so
638
00:34:49,680 --> 00:34:52,810
this particular one, you can convince yourself of it later,
639
00:34:52,810 --> 00:34:55,180
it adds up the numbers, okay. It just reduces,
640
00:34:55,180 --> 00:34:58,620
starts with zero, and for each one it takes the amount so
641
00:34:58,620 --> 00:35:01,620
far and adds it to the next thing in there, so
642
00:35:01,620 --> 00:35:03,260
it adds up the numbers.
643
00:35:03,260 --> 00:35:05,120
Notice this one has two arguments,
644
00:35:05,130 --> 00:35:09,500
an initial and a closure. Okay, see this closure here,
645
00:35:09,500 --> 00:35:12,800
an initial? And notice the closure also is still living
646
00:35:12,800 --> 00:35:16,070
outside of the parentheses. The first argument is inside
647
00:35:16,070 --> 00:35:19,210
the parentheses, but the closure is living outside.
648
00:35:19,210 --> 00:35:20,370
This one had no first argument.
649
00:35:20,370 --> 00:35:23,440
It only had the closure, so it has no parentheses here. This
650
00:35:23,440 --> 00:35:28,180
one does, okay? This is called trailing closure syntax, and
651
00:35:28,180 --> 00:35:31,180
you almost always wanna use it, it looks really cool,
652
00:35:31,190 --> 00:35:35,890
reads really nicely. Okay, dictionary.
653
00:35:35,890 --> 00:35:38,460
All right, so dictionary, similar to array,
654
00:35:38,460 --> 00:35:40,530
it's just looking up keys and values.
655
00:35:40,530 --> 00:35:44,230
Again, we're gonna use this more informal kind of syntax,
656
00:35:44,230 --> 00:35:48,630
okay? [String:Int] means the type dictionary where
657
00:35:48,640 --> 00:35:53,110
the strings are keys and ints are values, all right?
658
00:35:53,110 --> 00:35:55,570
So if I have pac10teamRankings here, and
659
00:35:55,580 --> 00:35:59,580
I had Stanford at number 1, Cal number 10, and I let the,
660
00:35:59,580 --> 00:36:02,750
I guess it's Pac-12 now. I should update this,
661
00:36:02,750 --> 00:36:07,020
should't I? So, okay, Cal 12 and so if I say here, let
662
00:36:07,020 --> 00:36:10,890
ranking = pac10teamRankings ["Ohio State"],
663
00:36:10,890 --> 00:36:14,590
that's obviously, no Ohio State in the Pac-12.
664
00:36:14,600 --> 00:36:17,930
So the ranking here has to be an Int?, right, it's
665
00:36:17,930 --> 00:36:22,970
an optional Int, we already saw that in our, calculator.
666
00:36:22,970 --> 00:36:26,370
Okay, anytime you dereference a dictionary,
667
00:36:26,370 --> 00:36:29,980
it's gonna return an optional of whatever type is
668
00:36:29,980 --> 00:36:34,750
the dictionary's values are, okay? The way you enumerate
669
00:36:34,750 --> 00:36:38,920
through a dictionary is using a tuple, okay, for (key,
670
00:36:38,920 --> 00:36:42,620
value) in pac10teamRankings, inside here, key is
671
00:36:42,620 --> 00:36:45,320
gonna be the key. And here is gonna be the value for each of
672
00:36:45,330 --> 00:36:49,860
the things in the dictionary, okay? Pretty cool. And again,
673
00:36:49,860 --> 00:36:52,230
you could put under bar here if you just wanted the keys,
674
00:36:52,230 --> 00:36:57,100
or under bar here if you just wanted the values. All right,
675
00:36:57,100 --> 00:37:02,170
String. String is a little complicated in Swift because
676
00:37:02,180 --> 00:37:03,210
it's full Unicode.
677
00:37:03,210 --> 00:37:06,010
And I mean full Unicode. All right so,
678
00:37:06,010 --> 00:37:09,350
it supports all kinds of languages. Languages where
679
00:37:09,350 --> 00:37:13,590
one character might be many, many different codes, okay.
680
00:37:13,590 --> 00:37:18,620
So characters are no longer single codes anymore. Okay,
681
00:37:18,630 --> 00:37:20,790
so, that's why, when you index into a string,
682
00:37:20,800 --> 00:37:24,230
you're not indexing by ints. You're indexing by
683
00:37:24,230 --> 00:37:28,470
indexes that know how to move along in a string past things
684
00:37:28,470 --> 00:37:32,240
like emojis that might have multiple codes, okay?
685
00:37:32,240 --> 00:37:34,240
So you're gonna wanna read about this, so
686
00:37:34,240 --> 00:37:36,910
I'm not gonna cover it all in lecture here. But
687
00:37:36,910 --> 00:37:39,880
one interesting way to look at a string is by calling
688
00:37:39,880 --> 00:37:43,920
this, String.CharacterView, okay? If you call character
689
00:37:43,920 --> 00:37:46,820
view on a string, you'll get the characters
690
00:37:46,820 --> 00:37:50,590
in what looks like an array of character, okay? It looks like
691
00:37:50,590 --> 00:37:53,090
an array of characters, not actually array of character.
692
00:37:53,090 --> 00:37:57,130
It's a character view. But it can be indexed with ints,
693
00:37:57,130 --> 00:38:00,100
okay, and you can get at the characters. So that's one way.
694
00:38:00,100 --> 00:38:03,000
Another way, you can actually create an array,
695
00:38:03,000 --> 00:38:06,710
one of the initializers that array will take is a string,
696
00:38:06,710 --> 00:38:09,340
okay. And then it'll be an array of characters.
697
00:38:09,340 --> 00:38:11,940
Okay, not quite as efficient as this character view, but
698
00:38:11,950 --> 00:38:14,510
you could do it that way, especially with a long string,
699
00:38:14,520 --> 00:38:17,150
you probably might not wanna do that. Okay, but that's
700
00:38:17,150 --> 00:38:19,720
a little easier way to get at it, otherwise you have to
701
00:38:19,720 --> 00:38:23,220
understand what it says in the reading about string indexes,
702
00:38:23,220 --> 00:38:25,860
okay. String.index that class and
703
00:38:25,860 --> 00:38:29,060
how to use it to get things out of a string,
704
00:38:29,060 --> 00:38:34,130
okay? There are a lot of other cool methods in String,
705
00:38:34,140 --> 00:38:36,700
though, like hasPrefix will tell you whether one string
706
00:38:36,700 --> 00:38:40,040
has the prefix of another string. You can capitalize and
707
00:38:40,040 --> 00:38:43,110
lowercase and uppercase these strings.
708
00:38:43,110 --> 00:38:45,080
You can even do sophisticated things like,
709
00:38:45,080 --> 00:38:48,950
get me all the components in this string separated by
710
00:38:48,950 --> 00:38:48,980
this other string. So
711
00:38:48,980 --> 00:38:52,550
this is a method you send to a string. This might be a comma,
712
00:38:52,550 --> 00:38:56,460
let's say, and this, right here like this, and it will
713
00:38:56,460 --> 00:39:00,130
extract all the strings that are separated by a comma.
714
00:39:00,130 --> 00:39:03,360
Okay, kinda fun. So make sure you familiarize yourself with
715
00:39:03,360 --> 00:39:06,300
a lot of the methods in String so that you don't waste your
716
00:39:06,300 --> 00:39:09,230
time writing this method which already exists, okay, or
717
00:39:09,240 --> 00:39:14,070
similar methods like it. All right, other classes that
718
00:39:14,080 --> 00:39:18,310
are here that you have to think about. One is NSObject.
719
00:39:18,310 --> 00:39:23,250
This is the base class of all Objective-C classes, okay.
720
00:39:23,250 --> 00:39:26,850
Now, we know, in Swift, it doesn't have a mandatory base
721
00:39:26,850 --> 00:39:30,660
class, like CalculatorBrain, no superclass, okay.
722
00:39:30,660 --> 00:39:31,820
Perfectly legal in Swift. But
723
00:39:31,830 --> 00:39:35,890
all Objective-C ones have to be subclasses of NSObject.
724
00:39:35,900 --> 00:39:38,560
Now, there are some APIs, they're fairly rare and
725
00:39:38,570 --> 00:39:41,830
I'll tell you which ones they are when we get to them later
726
00:39:41,840 --> 00:39:45,300
in the quarter, that are Objective-C APIs, okay.
727
00:39:45,310 --> 00:39:46,670
And when you use them in Swift,
728
00:39:46,670 --> 00:39:50,180
you need to have a class that subclasses from NSObject. So
729
00:39:50,180 --> 00:39:53,550
in Swift, it's perfectly legal to subclass from NSObject,
730
00:39:53,550 --> 00:39:56,620
not required except for in these few cases I'm gonna talk
731
00:39:56,620 --> 00:39:59,690
about later in the quarter. But legal and it really won't
732
00:39:59,690 --> 00:40:02,650
hurt anything, so you could do it if you want. Some people
733
00:40:02,660 --> 00:40:04,620
just get in the habit of always sub-classing for
734
00:40:04,630 --> 00:40:07,130
NSObjects in Swift, if they're not sub-classing from
735
00:40:07,130 --> 00:40:10,400
something else, because why not, okay. Then their classes
736
00:40:10,400 --> 00:40:14,870
are always ready for whatever these weird old APIs are,
737
00:40:14,870 --> 00:40:19,140
okay? NSNumber, okay in Objective-C,
738
00:40:19,140 --> 00:40:23,110
there's no such things as well structs
739
00:40:23,110 --> 00:40:28,110
in Objective-C are not like structs in Swift.
740
00:40:28,120 --> 00:40:30,920
They're not this nice value type semantic thing.
741
00:40:30,920 --> 00:40:36,190
Okay, structs are more like C structs okay, so double and
742
00:40:36,190 --> 00:40:38,290
int, string, array,
743
00:40:38,290 --> 00:40:41,090
dictionary. In Objective-C these are classes.
744
00:40:41,100 --> 00:40:45,530
Okay, sorry, array and diction are classes.
745
00:40:45,530 --> 00:40:51,040
Double and int are C-double and int. Not even objects,
746
00:40:51,040 --> 00:40:53,540
okay? So that brings a problem in Objective-C,
747
00:40:53,540 --> 00:40:56,910
what if I wanna put a bunch of numbers into an array? Okay,
748
00:40:56,910 --> 00:40:59,710
you can't do it, because an array is an array of objects.
749
00:40:59,710 --> 00:41:03,020
And doubles and ints are C primitive types,
750
00:41:03,020 --> 00:41:05,420
in Objective-C. So this class NSNumber,
751
00:41:05,420 --> 00:41:09,050
is how you wrap a primitive type into an object.
752
00:41:09,060 --> 00:41:09,350
So you can put it in an array.
753
00:41:09,360 --> 00:41:12,860
Now in Swift we don't need that, because in Swift arrays
754
00:41:12,860 --> 00:41:16,090
and dictionaries will accept structs, and double and
755
00:41:16,100 --> 00:41:20,570
int is a struct. Okay, so this NSNumber not necessary for
756
00:41:20,570 --> 00:41:26,040
that, okay. Now there are iOS APIs that take,
757
00:41:26,040 --> 00:41:29,710
for example, arrays of ints. And
758
00:41:29,710 --> 00:41:33,280
in Objective-C, they're gonna be arrays of NSNumber and
759
00:41:33,280 --> 00:41:37,520
in Swift, they're going to be Swift arrays of Swift Ints.
760
00:41:37,520 --> 00:41:39,450
And you might ask, how is that ever gonna work?
761
00:41:39,450 --> 00:41:41,920
Cuz you're talking about a struct filled with struct
762
00:41:41,920 --> 00:41:46,890
versus an array class filled with these NSNumber classes.
763
00:41:46,890 --> 00:41:49,830
Well the answer is, those things, array, dictionary,
764
00:41:49,830 --> 00:41:54,770
then int double, and NSNumber are all bridged, magically and
765
00:41:54,770 --> 00:41:58,070
automatically, okay? As you call functions that require
766
00:41:58,070 --> 00:42:00,410
one to be one way or the other. It just automatically
767
00:42:00,410 --> 00:42:03,840
works. Okay, there's a lot of magic going on in Objective-C
768
00:42:03,840 --> 00:42:06,140
runtime to make that work. Okay, and we're gonna take
769
00:42:06,150 --> 00:42:10,180
advantage of that bridging in a couple of slides here.
770
00:42:10,180 --> 00:42:13,720
NSDate, brilliant important class to know about. If
771
00:42:13,720 --> 00:42:17,020
you wanna put a date in your UI, you need be very careful.
772
00:42:17,020 --> 00:42:19,660
Because dates are represented in a different way all over
773
00:42:19,660 --> 00:42:25,300
the Earth. Date date class knows all about those ways and
774
00:42:25,300 --> 00:42:28,670
it has a lot of companion classes like NSCalendar,
775
00:42:28,670 --> 00:42:31,670
NSDateFormatter, NSDateComponents to help you
776
00:42:31,670 --> 00:42:35,040
put dates on screen in a way that's gonna work when you
777
00:42:35,040 --> 00:42:39,580
translate your app to Chinese. So NSDate is an important
778
00:42:39,580 --> 00:42:43,050
class to get familiar with and understand what it can do.
779
00:42:43,050 --> 00:42:45,320
Okay, if you're ever gonna put a date in your UI,
780
00:42:45,320 --> 00:42:48,550
you're gonna need to know about NSDate. NSDate can also
781
00:42:48,560 --> 00:42:52,790
tell you the current date and time. All right NSData,
782
00:42:52,790 --> 00:42:57,530
that's a bag of bits. Okay just unstructured bag o' bits.
783
00:42:57,530 --> 00:42:59,600
We use it to pass things around,
784
00:42:59,600 --> 00:43:01,930
sometimes over the network like image data,
785
00:43:01,940 --> 00:43:04,700
things like that. It's pretty self explanatory,
786
00:43:04,710 --> 00:43:06,200
bag of bits doesn't have a lot of methods on it.
787
00:43:06,210 --> 00:43:09,980
It's basically just give me the bits and here's some bits.
788
00:43:10,210 --> 00:43:13,110
Okay, now let's talk about initialization, I'm gonna kind
789
00:43:13,110 --> 00:43:16,250
of blast through this pretty quick. There's a really
790
00:43:16,250 --> 00:43:19,120
good explanation of this in your homework in your reading
791
00:43:19,120 --> 00:43:23,090
assignment. For this coming week but I'm gonna try and
792
00:43:23,090 --> 00:43:25,720
give you the 50,000 foot overview of it quickly so
793
00:43:25,730 --> 00:43:28,290
that you kind of have an idea what's going on as you go and
794
00:43:28,300 --> 00:43:31,400
read that very detailed description, okay?
795
00:43:31,400 --> 00:43:35,330
So when is an init method needed? We're talking about
796
00:43:35,340 --> 00:43:39,840
initializing structs and classes okay? They're
797
00:43:39,840 --> 00:43:42,370
not needed that often because of course we can say equal
798
00:43:42,380 --> 00:43:45,410
something in the var. We coud use those lazy instantiation
799
00:43:45,410 --> 00:43:49,010
things to get around it. Properties might be optional,
800
00:43:49,020 --> 00:43:50,780
so, and they can just start off being not set.
801
00:43:50,780 --> 00:43:53,850
So there's a lot of reasons you don't need an init, okay?
802
00:43:53,850 --> 00:43:56,720
And frankly, most people try to design their classes and
803
00:43:56,720 --> 00:43:59,790
structs so they don't need an init, okay? So you can just
804
00:43:59,790 --> 00:44:01,690
create them with an init with no arguments, right,
805
00:44:01,700 --> 00:44:05,160
which you get for free. Or a string in a struct case maybe
806
00:44:05,170 --> 00:44:08,370
you just do what we did in demo where you just init it
807
00:44:08,370 --> 00:44:11,870
with the values of all the things in the struct, okay?
808
00:44:11,870 --> 00:44:15,110
So but sometimes you might find yourself needing an init
809
00:44:15,110 --> 00:44:18,140
because you just can initialize things using those
810
00:44:18,150 --> 00:44:21,710
mechanisms, all right? Now, you do get some free inits,
811
00:44:21,720 --> 00:44:24,220
okay? We know about in classes you get the init with no
812
00:44:24,220 --> 00:44:27,450
arguments for free. That's how CalculatorBrain got an init,
813
00:44:27,450 --> 00:44:29,190
so we could create one. And
814
00:44:29,190 --> 00:44:30,560
we also know that structs have a free
815
00:44:30,560 --> 00:44:33,130
initializer to let you initialize all of the,
816
00:44:33,130 --> 00:44:38,500
all of its vars. Now, One thing to know about,
817
00:44:38,500 --> 00:44:41,230
[COUGH] both of those cases is that you,
818
00:44:41,240 --> 00:44:44,740
if you start providing your own initializers,
819
00:44:44,740 --> 00:44:47,740
then you stop getting the free one, okay?
820
00:44:47,740 --> 00:44:50,840
If you provide even one initializer for your struct
821
00:44:50,840 --> 00:44:54,450
you no longer get this free one, the one that does both,
822
00:44:54,450 --> 00:44:58,920
okay? All right, what can you do inside of init?
823
00:44:58,920 --> 00:45:02,820
What is legal to do? You can set any property's value,
824
00:45:02,820 --> 00:45:05,490
even one's that already have default values.
825
00:45:05,490 --> 00:45:06,490
Ones that you said equal something,
826
00:45:06,490 --> 00:45:08,630
you can reset them to something else if you want.
827
00:45:08,630 --> 00:45:09,590
You can set any property's value,
828
00:45:09,600 --> 00:45:13,670
okay. You can even set constant properties, okay.
829
00:45:13,670 --> 00:45:16,970
So if you have let something, so you have a constant in your
830
00:45:16,970 --> 00:45:20,070
class, like a constant instance variable basically.
831
00:45:20,070 --> 00:45:22,410
You can set that in, even though it's let,
832
00:45:22,410 --> 00:45:24,980
you can set it in the initializer. Constant
833
00:45:24,980 --> 00:45:27,850
properties are fairly rare. You know, why would you have
834
00:45:27,850 --> 00:45:30,550
something that's constant? You might, though, you might have
835
00:45:30,550 --> 00:45:33,550
something that's determined in initialization. And
836
00:45:33,550 --> 00:45:33,990
then it never changes,
837
00:45:33,990 --> 00:45:38,560
okay? You can call other init methods from your init,
838
00:45:38,560 --> 00:45:42,830
you actually can call one other init method from n init,
839
00:45:42,830 --> 00:45:47,600
okay? And you can call superclass inits, okay.
840
00:45:47,600 --> 00:45:50,000
You can call your superclass initializers, but
841
00:45:50,000 --> 00:45:52,740
there are rules for calling inits and they are rather
842
00:45:52,740 --> 00:45:54,970
complicated. And I'm gonna go through them quick and
843
00:45:54,980 --> 00:45:56,110
you're gonna read the documentations,
844
00:45:56,110 --> 00:45:58,110
it's gonna give you all the details, okay? So
845
00:45:58,110 --> 00:46:01,880
there are requirements inside of init, things you must do.
846
00:46:01,880 --> 00:46:03,750
The previous slide was things you are allowed to do,
847
00:46:03,750 --> 00:46:07,550
here's the things you must do, okay? By the time any init is
848
00:46:07,550 --> 00:46:10,420
done, okay? By the time the init is finished,
849
00:46:10,420 --> 00:46:13,590
all properties must have values. We know this, right?
850
00:46:13,590 --> 00:46:16,630
But in Swift, all, by the time initialization is done,
851
00:46:16,630 --> 00:46:19,300
all properties have to have values, and again,
852
00:46:19,300 --> 00:46:20,870
optionals must have a value too, but
853
00:46:20,870 --> 00:46:25,340
it could be the value not set. That's fine, okay? Now,
854
00:46:25,340 --> 00:46:30,180
there are two types of init methods in a class.
855
00:46:30,180 --> 00:46:34,110
Convenience methods and, convenience initializers and
856
00:46:34,110 --> 00:46:37,280
designated initializers. Designated initializers
857
00:46:37,280 --> 00:46:40,590
are not marked with the word convenience. Okay,
858
00:46:40,590 --> 00:46:43,390
convenience initializers say convenience init whatever,
859
00:46:43,390 --> 00:46:48,890
okay, in their declaration. A designated initializer must,
860
00:46:48,900 --> 00:46:52,130
and can only, call a designated initializers
861
00:46:52,130 --> 00:46:55,270
in it's superclass, its immediate superclass, okay?
862
00:46:55,270 --> 00:46:57,900
So a designated initializer can't call a convenience
863
00:46:57,910 --> 00:46:59,810
initializer in its superclass, and it can't
864
00:46:59,810 --> 00:47:02,740
call a convenience super, initializers in its own class.
865
00:47:02,740 --> 00:47:05,980
It must call a superclass's designated initializer. Okay,
866
00:47:05,980 --> 00:47:10,880
you must initialize all the properties that you introduce
867
00:47:10,880 --> 00:47:16,090
in your class, before calling a superclass's init.
868
00:47:16,090 --> 00:47:19,760
Okay, so by the time you call super init to let your super
869
00:47:19,760 --> 00:47:22,460
class initialize you must have all your properties
870
00:47:22,460 --> 00:47:27,700
initialized, all right? You must call a superclass's init
871
00:47:27,700 --> 00:47:32,370
before you can touch any of your superclass's properties.
872
00:47:32,370 --> 00:47:35,270
See the order there? You do yours, call the super,
873
00:47:35,280 --> 00:47:37,740
now you can touch theirs, okay? That makes sense.
874
00:47:37,740 --> 00:47:40,380
You gotta let them initialize theirs before you can touch it
875
00:47:40,380 --> 00:47:42,880
and you want yours initialized before they come along and
876
00:47:42,880 --> 00:47:46,850
start doing their stuff. A convenience initializer must
877
00:47:46,850 --> 00:47:52,260
and can only call an init, either can be user designated,
878
00:47:52,260 --> 00:47:55,730
in its own class, cannot call super. Okay,
879
00:47:55,730 --> 00:47:58,130
a convenience initializer cannot call super initializer.
880
00:47:58,130 --> 00:48:03,030
A convenience initializer must call that in it, before it can
881
00:48:03,040 --> 00:48:07,970
set any property values. It's own, or it's super classes.
882
00:48:07,980 --> 00:48:11,840
Okay, in other words, it must let a designated initializer
883
00:48:11,850 --> 00:48:14,550
initialize your own properties first.
884
00:48:14,550 --> 00:48:16,080
And that thing is gonna call supern,
885
00:48:16,080 --> 00:48:18,820
it's gonna initialize it, so by the time a convenience
886
00:48:18,820 --> 00:48:21,990
intializer gets back from the emit, all the property values
887
00:48:21,990 --> 00:48:24,590
will be set and it can reset them to something else,
888
00:48:24,590 --> 00:48:29,730
okay. It's just a convenience. Calling other inits
889
00:48:29,730 --> 00:48:33,470
has to be complete before you can access any properties.
890
00:48:33,470 --> 00:48:36,700
Access I'm talking about, not set. Access any properties or
891
00:48:36,700 --> 00:48:40,810
methods in yourself. So you can't say self.anything.
892
00:48:40,810 --> 00:48:43,440
You know, you can't access any of the properties or
893
00:48:43,440 --> 00:48:45,980
methods in yourself until you're fully initialized.
894
00:48:45,980 --> 00:48:49,110
Okay, so you'll have to have called the other inits first.
895
00:48:49,120 --> 00:48:51,920
Yeah? >> If you're building
896
00:48:52,620 --> 00:48:53,820
an imperative class why would you
897
00:48:53,820 --> 00:48:54,890
ever need convenience inits? >> So
898
00:48:54,890 --> 00:48:57,690
the question is why would I ever use a convenience init?
899
00:48:57,690 --> 00:49:02,190
The answer is I have a reasonable default for
900
00:49:02,200 --> 00:49:06,560
some of the arguments to a designated initializer, and
901
00:49:06,570 --> 00:49:08,800
it's actually possible to default things in swift.
902
00:49:08,800 --> 00:49:12,040
You can basically say, equal something. But let's say you
903
00:49:12,040 --> 00:49:15,010
wanna default into some calculative value, maybe based
904
00:49:15,010 --> 00:49:17,080
on the other argument. A community initialize,
905
00:49:17,080 --> 00:49:20,150
initializer you could create would have fewer arguments and
906
00:49:20,150 --> 00:49:22,510
then turn around and call the designated one.
907
00:49:22,520 --> 00:49:24,420
Okay, it's truly for convenience,
908
00:49:24,420 --> 00:49:26,950
that's all these are for, phew is right, okay.
909
00:49:26,950 --> 00:49:29,790
There's a lot of rules there. And it's a lot to think about.
910
00:49:29,790 --> 00:49:31,620
And when you start writing your own initializers you're
911
00:49:31,630 --> 00:49:33,060
gonna bump up against these all the time.
912
00:49:33,060 --> 00:49:36,190
You're gonna be like I can't, I can't access that one until
913
00:49:36,200 --> 00:49:38,900
I do, yeah, I gotta call supren, okay, believe me.
914
00:49:38,900 --> 00:49:41,030
So try to stay away from doing your own inits,
915
00:49:41,030 --> 00:49:45,040
is my advice to you. Okay, inheriting init.
916
00:49:45,040 --> 00:49:48,440
Now, if you don't implement any designated inits,
917
00:49:48,440 --> 00:49:51,280
you'll inherit all of your superclass's designated inits.
918
00:49:51,280 --> 00:49:53,980
But if you in, if you implement even one designated
919
00:49:53,980 --> 00:49:56,510
init, you don't get any of your superclass's designated
920
00:49:56,520 --> 00:50:01,950
initializers, okay. If you override all of your desig,
921
00:50:01,960 --> 00:50:04,360
class, superclass's designated inits, or
922
00:50:04,360 --> 00:50:07,130
don't implement any of them, then you will inherit all of
923
00:50:07,130 --> 00:50:12,100
the convenience initializers from your superclass, okay. In
924
00:50:12,100 --> 00:50:13,870
other words, the convenience superv, initializers in your
925
00:50:13,870 --> 00:50:17,570
superclass are all kinda dependent on the designated
926
00:50:17,570 --> 00:50:19,840
initializer in your superclass being sensible and
927
00:50:19,840 --> 00:50:23,380
making sense together. So you have to get them all
928
00:50:23,380 --> 00:50:27,410
by overriding them all, or by overriding none of them. For
929
00:50:27,410 --> 00:50:29,010
the convenience one to inherit,
930
00:50:29,020 --> 00:50:33,280
to make, for sure make sense, okay. If you omit no inits,
931
00:50:33,290 --> 00:50:36,220
you get all of your superclasses inits,
932
00:50:36,220 --> 00:50:40,030
convenience and designated, okay. Any in it that
933
00:50:40,030 --> 00:50:44,260
you inherit by these rules count on the previous slide.
934
00:50:44,260 --> 00:50:47,730
All right, the previous slide says things like you have to,
935
00:50:47,730 --> 00:50:50,870
convenience has to call a designated in your own class,
936
00:50:50,870 --> 00:50:53,270
blah, blah, blah. Well, if you inherit it,
937
00:50:53,270 --> 00:50:56,810
then it is in your own class. Okay, so
938
00:50:56,810 --> 00:51:03,010
chew on that okay. Required init, different topic.
939
00:51:03,020 --> 00:51:06,580
It is possible to mark an init with the keyword required.
940
00:51:06,590 --> 00:51:12,360
That means that a subclass must implement this init.
941
00:51:12,360 --> 00:51:14,460
Okay, it is not optional for it, it must.
942
00:51:14,460 --> 00:51:18,960
And it can inherit if it follows these rules above.
943
00:51:18,970 --> 00:51:23,570
But it must implement it, it's required, okay. UI view, for
944
00:51:23,570 --> 00:51:25,540
example, which we're gonna do on Wednesday.
945
00:51:25,540 --> 00:51:26,100
It has a required init.
946
00:51:26,110 --> 00:51:29,340
Okay, and we're gonna see that. All right,
947
00:51:29,340 --> 00:51:32,440
failable initializers, it is possible for init to fail.
948
00:51:32,450 --> 00:51:36,180
We saw this double if you pass a string to the initialize,
949
00:51:36,180 --> 00:51:38,720
if a double has an initializer it takes a string.
950
00:51:38,720 --> 00:51:41,590
If you pass a string. That's not a double,
951
00:51:41,590 --> 00:51:44,060
then it will fail, then return nil, okay.
952
00:51:44,060 --> 00:51:47,730
So failable initializers cause optional versions of the thing
953
00:51:47,730 --> 00:51:50,760
to be returned and you specify them with this little question
954
00:51:50,760 --> 00:51:54,330
mark. You put in a question mark in there, that turns
955
00:51:54,330 --> 00:51:57,670
this ititializer into the kind of initializer that will turn
956
00:51:57,670 --> 00:52:02,840
an optional of this class whatever it is. Okay? And
957
00:52:02,840 --> 00:52:06,280
to fail, you just return nil. Inside this initializer,
958
00:52:06,280 --> 00:52:09,480
if something goes wrong, return nil, and nil will come
959
00:52:09,480 --> 00:52:13,890
back to whoever's trying to get this thing. Make sense?
960
00:52:13,890 --> 00:52:17,690
Okay, failable initializers are pretty rare. Images, for
961
00:52:17,690 --> 00:52:18,090
example, UI image,
962
00:52:18,090 --> 00:52:20,860
image named, if it can't find an image with this name,
963
00:52:20,860 --> 00:52:23,960
it returns nil. Okay. So that's why this image right
964
00:52:23,960 --> 00:52:28,900
here Let image. This image is an Optional UIimage. And
965
00:52:28,900 --> 00:52:31,700
we probably use if-let here, if let image if we can like
966
00:52:31,710 --> 00:52:35,840
this image equal to UIimage name foo, then whatever.
967
00:52:36,940 --> 00:52:41,350
Okay? All right. Creating Objects All right,
968
00:52:41,350 --> 00:52:45,120
usually you create an object by calling its initializer
969
00:52:45,120 --> 00:52:49,250
with the appropriate arguments using the type name. So
970
00:52:49,260 --> 00:52:50,920
CalculatorBrain, we've seen that,
971
00:52:50,920 --> 00:52:54,590
ComplicatedObject with a bunch of arguments to create it.
972
00:52:54,590 --> 00:53:00,670
Or here's let z = the type array of String initializer.
973
00:53:00,670 --> 00:53:03,000
Okay, that creates an empty array of string.
974
00:53:03,000 --> 00:53:08,210
It's constant, so it's gonna be empty forever, okay? But
975
00:53:08,210 --> 00:53:11,240
sometimes other objects will create objects for you.
976
00:53:11,240 --> 00:53:14,180
They can call a method and it will create an object for you,
977
00:53:14,180 --> 00:53:14,810
give it to you, right? But
978
00:53:14,810 --> 00:53:18,980
when you're creating objects from scratch, this is the.
979
00:53:18,990 --> 00:53:20,590
Syntax you're using, you're used to that, right?
980
00:53:20,590 --> 00:53:25,990
No questions about that, it's a kind of obvious slide. Okay,
981
00:53:25,990 --> 00:53:28,990
now AnyObject, little more complicated here.
982
00:53:29,000 --> 00:53:32,630
AnyObject is a special type, it's actually a protocol, but
983
00:53:32,630 --> 00:53:33,900
we haven't talked about it yet, but anyway,
984
00:53:33,900 --> 00:53:36,100
it's a special type. You can leave it as a type.
985
00:53:36,100 --> 00:53:40,610
Protocols are types, I guess. It is used Commonly, or used,
986
00:53:40,610 --> 00:53:43,910
used to be, for compatibility with Objective-C APIs,
987
00:53:43,910 --> 00:53:47,450
because Objective-C, for those of you who are trying to learn
988
00:53:47,450 --> 00:53:50,520
some Objective-C along the way here, Objective-C has a very
989
00:53:50,520 --> 00:53:54,720
important type called ID. Okay, the type ID.
990
00:53:54,720 --> 00:54:00,560
Which means pointer to a object of unknown class.
991
00:54:00,560 --> 00:54:04,200
Okay. So that's a very open-ended type. [LAUGH] Okay,
992
00:54:04,200 --> 00:54:07,400
Swift doesn't do things that way. Swift is strongly typed.
993
00:54:07,400 --> 00:54:12,140
Okay, it infers types but it's strongly typed. However,
994
00:54:12,140 --> 00:54:15,310
Swift has to work with all those IOSAPIs, so
995
00:54:15,310 --> 00:54:17,080
it introduced this type called AnyObject.
996
00:54:17,080 --> 00:54:21,680
So AnyObject in Swift means A point or two an object of
997
00:54:21,680 --> 00:54:24,680
unknown class, okay? It only works for objects for
998
00:54:24,680 --> 00:54:29,350
classes not for structs. So it means the same thing as objective C.
999
00:54:29,360 --> 00:54:33,560
IN iOS 9 they fixed objective C. So it has almost none of
1000
00:54:33,560 --> 00:54:38,530
these, okay? They're still a few but very very few. So
移除了旧的实现。 但是在少数代码中还是存在的。
1001
00:54:38,530 --> 00:54:42,630
nowadays It is more used as an opaque type. Okay?
现在 AnyObject 主要用于需要隐藏具体细节的类型的场景。
1002
00:54:42,640 --> 00:54:43,400
When you want to have a point or
当你希望有一个指针指向
1003
00:54:43,400 --> 00:54:45,970
two in the object and you are not sure what it is or
一个对象,并且你不确定这个对象是什么你不希望让别人知道
1004
00:54:45,970 --> 00:54:48,370
you don't want anyone to know what it is okay?
这个对象具体是什么的时候,可以考虑用 AnyObject
1005
00:54:48,380 --> 00:54:52,510
So let's talk about how we can do this okay? So classes only
那么我们可以如何使用 AnyObject 这个类型呢?它只能用于 Class 类型的
1006
00:54:52,510 --> 00:54:55,680
and no structs. There is another type by the way. It is
对象,而不能使用在 Struct 的对象上。 这里顺带提一句, 还有另外一种类型。
1007
00:54:55,680 --> 00:55:00,080
what is called any, which is anything. Could be a struct,
叫做 Any, 他能指向任何类型的对象, 可以指向 Struct、
1008
00:55:00,090 --> 00:55:02,990
could be a class, anything. We almost, in fact, this class we
Class 以及其他所有类型的对象。 事实上,我们基本上
1009
00:55:02,990 --> 00:55:06,220
will never use Any, okay? You probably never wanna use it.
不会用到 Any 这个类型。 你也不会想要使用它。
1010
00:55:06,230 --> 00:55:08,930
It's in there, I think, for just language completeness.
它存在的意义就在于保证语法的完整性。
1011
00:55:08,930 --> 00:55:12,130
But I can't even think of a good explanation of why you
但是我完全无法想到你能使用到它的场景。
1012
00:55:12,130 --> 00:55:15,170
would wanna use it. So we're not even gonna talk about Any.
所以我们这就不讨论 Any 。
1013
00:55:15,170 --> 00:55:17,040
But we are gonna talk about AnyObject, okay?
我们还是继续讨论 AnyObject 吧。
1014
00:55:17,040 --> 00:55:19,270
So where will you see AnyObject? You'll sometimes
想想你们都在哪见过 AnyObject ?
1015
00:55:19,270 --> 00:55:23,640
see it in a method where one of the arguments Truly can be
你应该在在一些参数可能是多种类型的
1016
00:55:23,640 --> 00:55:26,340
more than one different kinds of class, mm kay? For
方法中看到它对吧?
1017
00:55:26,350 --> 00:55:29,310
example, you'll see on Wednesday, prepare for segue
例如将在下周三的课上才讲到的在MVC
1018
00:55:29,320 --> 00:55:33,120
which is a thing that prepares for transitions from one NVC
跳转前会调用 prepareForSegue 方法中含有 AnyObject 的参数。
1019
00:55:33,120 --> 00:55:36,590
to another, okay? We're gonna have multiple NVCs someday.
我们会在一个项目中含有非常多的 MVC 。
1020
00:55:36,590 --> 00:55:38,690
We're gonna wanna transition from one to the other, prepare
我们经常会从一个跳转到另一个过程,
1021
00:55:38,690 --> 00:55:41,660
for segue prepares for that transition. Well the sender,
而 prepareForSegue 则是为了这种跳转而存在的。 至于该方法的参数 sender,
1022
00:55:41,660 --> 00:55:45,100
in other words the object that initiated the transition could
换句话来说,这个对象用来初始化跳转,并且它包含
1023
00:55:45,100 --> 00:55:47,100
be lots of different kinds of objects.
许多不同的类型的对象。
1024
00:55:47,100 --> 00:55:48,430
It might be a button you collect on,
他可能是一个按钮
1025
00:55:48,440 --> 00:55:51,200
it might be a roll in a table, it might be some custom code
他可能是一个一个在列表中的滚动条, 它可能是一段在你的 Controller 中自定义的代码。
1026
00:55:51,200 --> 00:55:55,240
inside of your controller. Okay? So this sender has to be
所以这个 sender 参数必须使用 AnyObject 类型。
1027
00:55:55,240 --> 00:55:59,840
any object. Okay, cuz we don't know what it is. We
因为我们不知道具体情况下,它是什么?
1028
00:55:59,850 --> 00:56:03,410
don't know if it is a button or table row or what it is.
我们不知道它是否是一个按钮、列表或者是其他什么东西。
1029
00:56:03,420 --> 00:56:06,580
So AnyObject. Okay. So this is a case where he is an object,
因此最好使用 AnyObject。 这就是一个对象使用 AnyObject 的例子,
1030
00:56:06,590 --> 00:56:09,650
it could be many things we don't know what it is.
他可能是非常多类型的对象, 而我们不知道它具体是什么。
1031
00:56:09,660 --> 00:56:12,060
So we have to type this as AnyObject.
所以我们这里必须使用 AnyObject 类型。
1032
00:56:12,060 --> 00:56:15,930
Okay? Another example of that is touch digit. If we haven't,
另外一个例子就是之前用到的 touchDigit 方法。
1033
00:56:15,930 --> 00:56:16,630
when we control dragged,
如果在我们拖拽链接中,
1034
00:56:16,630 --> 00:56:19,630
remember when we changed it from any object to UI button,
忘记了从 AnyObject 改成 UIButton 的情况下。
1035
00:56:19,630 --> 00:56:22,200
Okay. If we hadn't done that, it would have created a touch
如果我们忘记了修改, 他将会创建一个 touchDigit 方法,
1036
00:56:22,200 --> 00:56:25,170
digit with sender AnyObject and then touch digit could've
该方法带有一个 AnyObject 类型的 sender 参数, 这样 touchDigit 方法
1037
00:56:25,170 --> 00:56:29,140
been sent by a UI button or maybe by UI slider. Okay. Or
可能由一个 UIButton 触发或者是 UISlider 触发。
1038
00:56:29,140 --> 00:56:32,710
something else. And we didn't do that because the inside
当时我们当时并没有用 AnyObject 类型的参数是因为
1039
00:56:32,710 --> 00:56:34,910
touch digit we wanted sender to be a UI button so
我们希望触发 touchDigit 放的的对象是 UIButton 的类型。
1040
00:56:34,910 --> 00:56:38,920
we just send it current title and other button things, but
这样我们就可以获得他的内容以及一些其他按钮的属性或方法,
1041
00:56:38,920 --> 00:56:43,820
it could have been AnyObject, all right? So, another use for
但是其实它还是可以改成 AnyObject。
1042
00:56:43,820 --> 00:56:46,690
AnyObject is when you wanna return essentially a cookie,
当时想要用一个密封的 Cookie 的时候, 你也可以用 AnyObject 类型。
1043
00:56:46,690 --> 00:56:49,290
okay? A cookie is something
Cookie 是一个
1044
00:56:49,300 --> 00:56:52,230
you give back where you're giving it to someone,
你给一个指定的人
1045
00:56:52,230 --> 00:56:53,360
they don't know what's inside of it,
但是他们不需要知道它里面的内容,
1046
00:56:53,370 --> 00:56:56,100
you're not gonna tell them, the only thing they can do
你也不希望告诉他们, 他们所要做的就是将它
1047
00:56:56,100 --> 00:57:00,340
is give it back to you, okay? So the cookie just saves some
再传递给你。 所有 Cookie 只是保存了一些
1048
00:57:00,340 --> 00:57:03,510
state, remembers something and you can give it back. So
状态, 或者是一些你能知道内部实现的东西。
1049
00:57:03,510 --> 00:57:06,540
browsers have cookies, right? You go visit a browser site,
因此浏览器有对应的 Cookie, 当你访问一个网站,
1050
00:57:06,550 --> 00:57:10,480
the site stores some stuff about itself and about you in
该网站会存储很多关于它的信息以及保存一些关于你信息
1051
00:57:10,480 --> 00:57:13,180
their cookie. When you go away and come back to that site,
的 Cookie。 当你离开这个网站的时候,这些 Cookie 会传回该网站,
1052
00:57:13,190 --> 00:57:15,590
it looks in the cookie and it can interpret it, okay?
如此看来的话, 该网站对 Cookie 应该是可理解的。
1053
00:57:15,590 --> 00:57:16,890
But it gives the cookie to the browser.
但是当将这个 Cookie 给浏览器时,
1054
00:57:16,890 --> 00:57:19,120
The browser has no idea what's in there. Okay.
浏览器完全无法知道它具体是什么内容。
1055
00:57:19,130 --> 00:57:23,530
To pick with AnyObject as far as the browser is concerned.
因此用 AnyObject 是浏览器最好的选择。
1056
00:57:23,530 --> 00:57:27,870
All right. So how do we use a variable type AnyObject when
那么我们如何使用一个 AnyObject 类型的对象, 并且
1057
00:57:27,870 --> 00:57:30,940
we don't know what the heck it is? Okay. And the answer is we
我们不知道它具体的内容? 答案是
1058
00:57:30,940 --> 00:57:33,640
have to convert it to a type that we do know what it is.
我们必须将其转换成一个我们知道的类型。
1059
00:57:33,640 --> 00:57:36,970
Okay. Now this conversion might not be possible.
当然, 这种换换过程可能会失败,
1060
00:57:36,980 --> 00:57:40,310
All right, because that thing might not be of that type
因为在我们转换的过程中,
1061
00:57:40,310 --> 00:57:41,350
when we try to convert it. So
我们所转换的对象可能不是那种类型。
1062
00:57:41,350 --> 00:57:46,820
we use this as keyword in Swift to try to convert it to
因此我们在 Swift 中用一个关键字 as 尝试去
1063
00:57:46,820 --> 00:57:50,150
the other type. Okay. In other words, try to treat that
转换成其他类型。 也就是说, 你尝试将 AnyObject
1064
00:57:50,160 --> 00:57:54,990
AnyObject as something else, optionally. So this is an op,
转换成其他东西。 转换后
1065
00:57:54,990 --> 00:57:59,000
this returns an optional. Okay, we usually, usually use
返回的是 Optional 的类型。 我们经常用 if let 的方式
1066
00:57:59,000 --> 00:58:01,970
it with if let, this as thing because it ensure an optional,
因为用这个中法获得的一定是 Optaional 的类型。
1067
00:58:01,970 --> 00:58:04,670
right? So if I have this local variable here,
例如我这里有一个变量,
1068
00:58:04,670 --> 00:58:08,870
AO which is in AnyObject and I was tying it to something,
ao 是一个 AnyObject 类型, 并且我尝试将它转换成
1069
00:58:08,880 --> 00:58:12,280
some class I don't know what it is, okay? But I wanna try
其他类型, 并且我们不知道 ao 具体是什么类型。 但是这里我希望将其
1070
00:58:12,280 --> 00:58:17,420
and use AO as if it were of class SomeClass, okay,
换换成 SomeClass 这种类型。
1071
00:58:17,420 --> 00:58:20,180
AO might be of SomeClass, and I'm gonna see if it is, and
ao 可能是 SomeClass 这种类型, 那么如果它是的话,
1072
00:58:20,190 --> 00:58:23,890
if it is I'm gonna use it as some class, I say if I can let
我们将其转换成 SomeClass 并且使用它, 也就是说如果我能让 foo
1073
00:58:23,890 --> 00:58:28,990
foo equal AO as SomeClass, then in here there's gonna be
等于 ao 转换成 SomeClass 的对象不为空的话, 那么我这里将会等一个
1074
00:58:29,000 --> 00:58:32,530
a local variable foo, which is not gonna be AnyObject, it's
名为 foo 的本地变量, 并且 foo 将不再是 AnyObject 类型, 应该
1075
00:58:32,530 --> 00:58:38,240
gonna be of type SomeClass. Got it? Just to that symbol
是 SomeClass 类型。 明白了吗? 使用这种方式
1076
00:58:38,240 --> 00:58:42,840
essentially casting if you wanna use that terminology?
将其转换成想要的类型。
1077
00:58:42,840 --> 00:58:46,940
Casting any object to be some class conditionally, okay?
使用 let if 这种方式将 AnyObject 转换成想要转换的类型即可。
1078
00:58:46,950 --> 00:58:51,420
So that's how we use something of AnyObject either that or
这就是我们如何使用 AnyObject 对象的方式, 或者是在我们
1079
00:58:51,420 --> 00:58:54,650
we don't know what's in there we just pass it around. Okay,
不青春它具体的内容是,而只是传递它时使用。
1080
00:58:54,650 --> 00:58:57,960
just pass it around to people who know what to do with it.
将其传递给真正知道它是什么内容的人。
1081
00:58:57,960 --> 00:59:02,660
We don't know what to do with it if it's a cookie, okay? So,
我们不知道一个 Cookie 具体能做什么。
1082
00:59:02,660 --> 00:59:05,400
what would code look like let's say on touchDigit.
那么如果 touchDigit 方法使用 AnyObject 参数该如何写?
1083
00:59:05,400 --> 00:59:08,870
If we had touchDigit and we had any object instead.
如果我们有一个 touchDigit 带有 AnyObject 的参数的话。
1084
00:59:08,870 --> 00:59:13,340
We probably say if we can let sendingButton equal the sender
我们可能会使用如果 sendingButton 等于 sender 转换成
1085
00:59:13,340 --> 00:59:16,240
as a UIbutton, then we'll treat it as a button,
UIButton 并且 sendingButton 不为空的话, 我们将其转换成,
1086
00:59:16,240 --> 00:59:19,410
get its currentTitle and go. Other wise else if
然后获得他的内容以及调用它的方法等。 另一方面, 如果
1087
00:59:19,410 --> 00:59:23,510
we can let sending slider equal to sender as UI slider,
能将其转换成 UISlider 并且不为空的话,
1088
00:59:23,520 --> 00:59:26,720
then we'll let the digit equal the sending sliders value
那么数字就等于 Slider 的值。
1089
00:59:26,720 --> 00:59:28,820
which is a double, we'll convert it to an int and
但是由于其实 Double 类型,所以我们需要将其转换成 Int
1090
00:59:28,820 --> 00:59:31,790
we'll convert that to a string, okay? That's how we
然后再转换成 String 类型。 这就是如何获得具体
1091
00:59:31,790 --> 00:59:35,660
get the digit. Did you see how we're doing kinda if let else,
数值的方式。 大家看清楚 if let 的方式
1092
00:59:35,660 --> 00:59:39,060
if let else? With this optional thing, that's how we
该如何操作了吗? 这就是我们处理 AnyObject 的方式。
1093
00:59:39,070 --> 00:59:43,700
would use that AnyObject. All right, another use
还有一种使用 AnyOject 的场景就是
1094
00:59:43,700 --> 00:59:49,140
of AnyObject is Property List, okay? So Property Lists are,
用在 Property List 中。 因为 Property List 中可能包含
1095
00:59:49,140 --> 00:59:53,080
essentially any combination of array, dictionary, string,
数组、 字典、 字符串
1096
00:59:53,080 --> 00:59:57,080
double, int, NSData, and NSDate. Kay, if you build
Double、 Int、 NSData 甚至是 NSDate 类型的数据。
1097
00:59:57,080 --> 00:59:59,520
any data structure out of only those classes,
如果你使用这些类型来存储数据的话,
1098
00:59:59,520 --> 01:00:02,220
you got a property list. So it's just a word. It's just
那么你就能获得一个 Property List。 这就是我们
1099
01:00:02,220 --> 01:00:06,160
a term we use to mean that. Kay, that's what it means.
想要的类型。 这就是它的含义。
1100
01:00:06,160 --> 01:00:10,130
And you might feel like, wait a second. Array,
等等,或许你会想:
1101
01:00:10,130 --> 01:00:13,160
AnyObject has to be a class, it cannot be a structs.
AnyObject 是一个Class类型, 而不是 Struct 类型。
1102
01:00:13,170 --> 01:00:16,000
But string, array, dictionary, double, those are structs.
但是 String、 Array、 Dictionary、 Double 这些都是 Struct 类型。
1103
01:00:16,000 --> 01:00:18,740
So how could this ever be in a property list. How could it
那么他们如何包含在一个 Property List 中呢?
1104
01:00:18,740 --> 01:00:23,210
ever be any object. And the answer is the bridging, kay?
他们是如何转换成 AnyObject 类型呢? 答案是桥接。
1105
01:00:23,210 --> 01:00:26,110
We got this automatic bridging to Objective-C, kay?
它们能自动转换成Objective C。
1106
01:00:26,110 --> 01:00:30,310
It automatically treats them like NSDictionary, NSArray,
它们会自动转换成 NSDictionary、 NSArray、
1107
01:00:30,320 --> 01:00:34,890
NSNumber, which are all classes. And allows them to be
NSNumber 或者是任何它们的封装类型。 这样就允许他们转换成 AnyObject了。
1108
01:00:34,890 --> 01:00:39,820
AnyObject, kay? Now these property lists
那么通过这种方式
1109
01:00:39,830 --> 01:00:43,560
are passed around blindly. The people who are looking in them
Property List 就在传递过程中可以不让人知道具体内容。 那么及时有人破解它的话,
1110
01:00:43,560 --> 01:00:46,730
are only knowing that their dictionaries and
他们也只能知道字典、数组
1111
01:00:46,730 --> 01:00:47,900
arrays of strings and dates and stuff.
字符串、日期以及一些其他的东西。
1112
01:00:47,900 --> 01:00:50,800
They don't know anything about what that data means, okay.
但是他们却不知道具体数据的含义。
1113
01:00:50,800 --> 01:00:52,600
They're just being passed around, so
那么它就可以被传递了。
1114
01:00:52,610 --> 01:00:55,010
let's look at an API and iOS that uses a property
那么让我们看看 iOS 中的一个 API, 一个便于
1115
01:00:55,010 --> 01:00:58,610
list to understand it better. It's called NSUserDefaults.
我们理解 Property List 的API。 那就是 NSUserDefaults。
1116
01:00:58,610 --> 01:01:02,110
What NSUserDefaults does, is it takes a property list and
那么 NSUserDefaults 是拿来干嘛的呢? 它是用来缓存 Property List
1117
01:01:02,120 --> 01:01:05,550
makes it persistent on disk. So when your app quits and
并且将其保存到磁盘上的。 那么当你 App 退出重启后,
1118
01:01:05,550 --> 01:01:08,190
runs again and you look it up again, it's there.
你能从新查询到保存的数据, 因为它保存在磁盘了。
1119
01:01:08,190 --> 01:01:10,660
So it's basically a database of property lists,
它有点类似一个 Property List 的基础数据库,
1120
01:01:10,660 --> 01:01:14,830
a database of structures with dictionaries, arrays, dates,
一个包含字典,数组,日期的数据库。
1121
01:01:14,830 --> 01:01:17,060
okay, that's what it is.
这就是N SUserDefaults。
1122
01:01:17,060 --> 01:01:19,430
It's a small database so don't use it for big things.
由于它是一个微型数据库,所以我们一般用它来保存大的数据。
1123
01:01:19,430 --> 01:01:21,700
You wouldn't wanna store like a dictionary of the entire
例如你绝对不会想用它来保存
1124
01:01:21,700 --> 01:01:23,330
English language in there. It's for
所以英语的字典。
1125
01:01:23,340 --> 01:01:26,870
small things like settings and things like that. The API on
他只是用来存一些小东西,例如设置、配置等一些小的东西。
1126
01:01:26,870 --> 01:01:30,810
it is very simply just say, setObject, property list here,
并且它的接口也非常简单, 只需要调用 setObject 用来存 Property List,
1127
01:01:30,810 --> 01:01:33,710
forKey, string. And then you can look it up and
用 String 来存 Key。 这样你就可以通过
1128
01:01:33,710 --> 01:01:36,280
get the property list back. So here you can see how you're
get 方法重新获得具体的 Property List。 你可以看得出,这里你
1129
01:01:36,280 --> 01:01:39,450
just passing it around. You're storing it as a cookie and
只是负责传递给NSUserDefaults。 你想保存 Cookie 一样保存它,
1130
01:01:39,450 --> 01:01:41,750
it's user defaults has no idea what's inside there.
并且 NSUserDefaults 对于里面具体是什么完全不了解。
1131
01:01:41,750 --> 01:01:44,290
It just knows it's only array is dictionary strings,
它只知道他可能是数组、 字典、 字符串
1132
01:01:44,290 --> 01:01:48,830
ints, etc, okay. It can also restore smaller things.
数值等一些数据。 并且能通过它重新获得这些小数据。
1133
01:01:48,830 --> 01:01:51,130
Like as I said a double, and it will make a little property
例如我告诉它保存一个 Double, 那么他就会创建一个小的 Property List
1134
01:01:51,130 --> 01:01:54,300
list out of a double because a double by itself Is a property
用来保存 Double, 因为 Double 会是一个 Property List 类型。
1135
01:01:54,300 --> 01:01:58,200
list, right? Because it's one of those classes, okay?
它是一个 Class 的类型。
1136
01:01:58,200 --> 01:02:01,710
Just like array is. How do you use user defaults?
就像一个数组一样。 那么你是如何使用 NSUserDefaults 的呢?
1137
01:02:01,710 --> 01:02:06,810
You create a shared one using this class or type method,
你只需调用它的类方法,
1138
01:02:06,810 --> 01:02:08,980
NSUserDefaults.standardUserDe- faults().
一个叫 NSUserDefaults.standardUserDefaults() 的方法。
1139
01:02:08,980 --> 01:02:12,320
This gives you the shared instance of standard, of user
这个方法将会返回一个共享的 NSUserDefaults 对象。
1140
01:02:12,320 --> 01:02:17,320
defaults. And then you just say To it. Get me a certain
接下来你只需调用它的方法获得数据,或者
1141
01:02:17,320 --> 01:02:21,630
property list, write this property list out. The changes
将一些数据写入即可。
1142
01:02:21,630 --> 01:02:23,730
you make will automatically be saved eventually, but
这些改变将会自动的保存下来,
1143
01:02:23,730 --> 01:02:26,200
if you wanna force them to be saved on disk, you can do
但是如果你想强制将其保存到磁盘的话, 你可以调用
1144
01:02:26,200 --> 01:02:29,530
synchronize, which returns a bool, which we almost always
synchronize 这个方法, 这个方法返回一个布尔类型,
1145
01:02:29,540 --> 01:02:31,400
ignore because not clear what to do if it fails.
这个返回值经常被我们忽略, 因为我们并不清楚它做什么,并且是否失败。
1146
01:02:31,400 --> 01:02:33,900
It would probably only fail if your disk were full.
如果返回时的false的话, 则表示你的磁盘以满。
1147
01:02:33,910 --> 01:02:36,810
Not sure what you're gonna do at that point, but any way.
并且你不确定如果失败了你应该做什么。
1148
01:02:36,810 --> 01:02:41,710
We usually ignore that return value, okay? Another example
我们经常会忽略这个返回值。
1149
01:02:41,710 --> 01:02:45,250
of property list might be in our calculator brain and
另一个使用 Property List 的例子将会在我们的 CalculatorBrain中使用,
1150
01:02:45,250 --> 01:02:46,680
I'm gonna show you a demo of this.
我将会在接下来的Demo进行展示。
1151
01:02:46,690 --> 01:02:50,790
Really quick here, which is, what if we wanted to get
接下来会讲非常快,关于假如我们希望在计算器中
1152
01:02:50,790 --> 01:02:53,590
the program that it's in the calculator? Right now,
获得它之前的运算结果。
1153
01:02:53,590 --> 01:02:55,560
our calculator could be programmed by saying
现在的计算能可以计算出类似:
1154
01:02:55,560 --> 01:02:58,730
five times four times three equals, let's put a program
5 乘以 4 乘以 3 等于什么这种运算, 我们只需将具体
1155
01:02:58,730 --> 01:03:00,900
in the calculator, right? Five times four times three.
操作输入计算器中即可。 输入 5 * 4 * 3.
1156
01:03:00,900 --> 01:03:03,930
It's kinda simple program. It'd be cool if we get
这个是非常简单的运算, 那么如果计算器能
1157
01:03:03,940 --> 01:03:05,400
that program and the person who got it,
将这种与运算表达式交给一个完全
1158
01:03:05,400 --> 01:03:08,570
it doesn't know anything about our internal data structure.
不了解运算表达式内部实现的人,那肯定非常溜的功能。
1159
01:03:08,570 --> 01:03:10,940
So it's AnyObject to them. And then later,
那么这就可以用上 AnyObject了。
1160
01:03:10,940 --> 01:03:12,280
they could come back to the CalculatorBrain and
只需将重新传递运算表达式给 CalculatorBrain,
1161
01:03:12,280 --> 01:03:16,810
say. Run this program that you gave me earlier, you see? So
并且告诉它, 运行这些你之前给我的运算表达式, 这样就可以得到答案了。
1162
01:03:16,820 --> 01:03:19,920
we use AnyObject as kind of this opaque program.
因此我们在这里用 AnyObject 来代表一个不透明的运算表达式。
1163
01:03:19,920 --> 01:03:22,220
Okay. Now this doesn't seem very variable right now.
不过现在看起来好像不可以带参数的。
1164
01:03:22,220 --> 01:03:24,520
But it's gonna be really valuable in assignment two,
但是我们希望将其做成可带参数的,
1165
01:03:24,520 --> 01:03:27,730
when you're gonna add variables to your calculator.
之后只需将参数的具体值告诉给你的计算器即可。
1166
01:03:27,730 --> 01:03:31,130
You're gonna be able to say three times X times five
也许你希望保存表达式 3 * X * 5这种表达式,
1167
01:03:31,130 --> 01:03:32,900
equals, where X is a variable, and
其中 X 是一个变量,
1168
01:03:32,900 --> 01:03:36,370
then you can run this then you get this program using this
那么你可以使用我接下来
1169
01:03:36,370 --> 01:03:40,170
code I'm going to show you here, the property list, and
将要完成的代码, 并且在代码中使用 Property List,这样你
1170
01:03:40,170 --> 01:03:43,170
then later you can, they can set the variable x and
可以做到了, 你可以设置变量 X 的值,并且重新运行
1171
01:03:43,180 --> 01:03:45,940
run it again. Maybe set the x to something else and
具体的表达式。 可以设置 X 为任意的一个值,然后
1172
01:03:45,950 --> 01:03:47,680
run the program again and again and again, you see?
不断的重复运行这个表达式。
1173
01:03:47,680 --> 01:03:50,650
So this would be a good use of AnyObject, because
那么希望这样的话, 最好使用 AnyObject 类型,
1174
01:03:50,650 --> 01:03:54,150
the calculator brain doesn't wanna give away its internal
因为 CalculatorBrain 并不想泄露
1175
01:03:54,150 --> 01:03:57,090
data struc for how, structure for how it represents
他的类型以及它是如何
1176
01:03:57,090 --> 01:04:00,660
a program. But it's happy to let someone have the program
重新运算该表达式的。 但是可以让别人拥有该运算表达式,
1177
01:04:00,660 --> 01:04:04,660
and give it to them later and they'll run again it later.
并且将其重新交给计算器, 计算器将会重新计算该运算表达式。
1178
01:04:04,660 --> 01:04:06,000
Make sense, see why we want it?
这样就可以理解为什么使用 AnyObject 了吧?
1179
01:04:06,000 --> 01:04:07,830
Now why would we make it a property list?
那么我们这里为什么要用使用 Property List 呢?
1180
01:04:07,830 --> 01:04:10,870
Because it'd be nice to store it in user defaults, right?
因为这样方面将其存入到 NSUserDefaults 里。
1181
01:04:10,870 --> 01:04:13,200
I've got a program? I'm gonna put it in the user defaults.
当我获得一个表达式之后, 我将其保存在 NSUserDefaults 中。
1182
01:04:13,210 --> 01:04:15,440
Next time my program runs, I can pull it out and
当下次我的程序运行时, 我能从 NSUserDefaults 重新获得,
1183
01:04:15,440 --> 01:04:17,340
ask a calculator brain to run it. You see,
然后让 CalculatorBrain 重新运算它。
1184
01:04:17,340 --> 01:04:19,280
it's just more flexible to be a property list.
由此可以看出 Property List 相对比较灵活。
1185
01:04:19,280 --> 01:04:21,850
So that's what we're gonna do. We're gonna create a var
我们接下来就按照上面的方法来实现。 首先,我们在 CalculatorBrain 中先创建一个
1186
01:04:21,850 --> 01:04:23,510
called program in our calculator brain.
叫做 program 的的属性。
1187
01:04:23,520 --> 01:04:25,880
It's gonna be gettable and settable. When you get it,
并且给他添加 get 和 set 的方法。 当你获得它的值得时候,
1188
01:04:25,890 --> 01:04:29,420
it gets the current program in, that the calculator brain
他将会给出之前当前表达式, 之前计算过的表达式。
1189
01:04:29,420 --> 01:04:33,220
has just run, and if you set it, it runs the program, okay?
当时你设置它的值得时候, 该表达式会重新运算一遍。
1190
01:04:33,230 --> 01:04:35,660
All right, so I'm gonna do that as the demo. I'm gonna
好的,接下来我讲在我们的Demo上添加该功能。
1191
01:04:35,660 --> 01:04:38,600
finish off the slides here first. One thing about this
不过我们先讲完所有的幻灯片。 首先是关于强制转换的,
1192
01:04:38,600 --> 01:04:41,800
casting, you can cast other things besides any object,
你可以强制转换任何类型,而不单单是 AnyObject。
1193
01:04:41,800 --> 01:04:45,270
okay. So you, for example, you can look at this slide later,
例如像幻灯片上所展示的,
1194
01:04:45,270 --> 01:04:48,010
but you could have a class that's a subclass of another
当你有一个子类的对象指向父类的指针的情况下,
1195
01:04:48,010 --> 01:04:51,740
class, okay? And you can try and cast using as to get
你可以将父类指针强制转换成子类的对象。
1196
01:04:51,740 --> 01:04:55,550
the subclass. You're not sure whether it's a subclass, but
虽然你没办法确定它是否是子类的对象,
1197
01:04:55,550 --> 01:04:58,550
you can cast and as will tell you whether it is, okay?
但是你可以通过强制转换的结果告诉你是否是其子类。
1198
01:04:58,550 --> 01:05:01,220
In this case, for example, if I have view controller
例如,我有一个UIViewController的变量,
1199
01:05:01,220 --> 01:05:03,720
the base class of our view controller, we can't say
但是我们没办法使用
1200
01:05:03,720 --> 01:05:06,890
vc.displayValue cuz this is a CalculatorViewController
vc.displayValue, 因为这个属性是属于 CalculatorViewController 的属性。
1201
01:05:06,890 --> 01:05:09,030
thing. But if I went and as'ed it, okay,
当时如果我进行强制转换的话,
1202
01:05:09,030 --> 01:05:12,800
if I took this VC and as'ed it to CalculatorViewController,
我将其强制转换成 CalculatorViewController 后,
1203
01:05:12,800 --> 01:05:16,470
now I could use displayValue. Okay, so as is for
我就可以使用 displayValue 这个属性了。 因此强制转换
1204
01:05:16,470 --> 01:05:18,400
more than just any object.
不单单是针对 AnyObject。
1205
01:05:18,400 --> 01:05:20,470
Okay, and yes, you can force with as!,
当然你也可以使用关键字 as! 来进行强制转换,
1206
01:05:20,470 --> 01:05:23,740
it'll crash if it can't do it. Assertions I'm not gonna talk
但是如果没办法进行强制转换的话, 你的程序会崩溃。 我不会在这里进行
1207
01:05:23,740 --> 01:05:26,240
about cuz we talked about it in the debugging section.
讲解断言, 因为我们将在调试的章节进行讲解。
1208
01:05:26,250 --> 01:05:29,850
You can look at the slide or the reading assignment to find
你可以查看幻灯片或者是阅读作业来查看
1209
01:05:29,850 --> 01:05:32,780
out more about assert. And that's it. So coming up on
更多的关于断言的信息。 记得周三来听
1210
01:05:32,790 --> 01:05:35,820
Wednesday I'm gonna start talking about custom drawing.
关于自定义绘制的课程。
1211
01:05:35,820 --> 01:05:38,160
On Friday we don't have a section for this week.
这周五, 我们没有课程。
1212
01:05:38,160 --> 01:05:40,960
And next Monday I'm gonna start talking about gestures,
下周我们我们开始讲解手势,
1213
01:05:40,960 --> 01:05:43,690
multi-touch gestures, okay, pinches and swipes and
多点触摸手势, 缩放以及滑动手势等,
1214
01:05:43,700 --> 01:05:45,860
stuff leading up to your assignment three,
并且该内容将会在你的第三次作业中用到,
1215
01:05:45,870 --> 01:05:48,130
which is you're gonna be doing a lot of drawing and
第三次作业包含了绘制、 多 MVC 以及其他的一些内容。
1216
01:05:48,130 --> 01:05:50,970
multiple MVCs and all that stuff. Your assignment this
这周的作业是
1217
01:05:50,970 --> 01:05:53,370
week is going to be the variables thing, okay?
关于变量的题目。
1218
01:05:53,370 --> 01:05:57,710
All right, so let's do that program var, okay?
接下来我们开始实现 program 属性吧。
1219
01:05:58,780 --> 01:06:02,110
Just gonna go back to our calculator here, all right,
重新回到计算器的程序,
1220
01:06:02,110 --> 01:06:07,080
and leave off exactly where we were. All right,
回到我们之前结束的地方。
1221
01:06:07,090 --> 01:06:08,690
so here's my controller, right here.
屏幕上显示的是我的 Controller。
1222
01:06:08,690 --> 01:06:10,890
I'm not gonna change my controller to do this.
目前我还不打算修改 Controller。
1223
01:06:10,890 --> 01:06:13,760
This is purely a brain thing, okay, here's my brain,
所需修改的都在 CalculatorBrain 中,
1224
01:06:13,760 --> 01:06:16,790
calculator brain. So I'm gonna add a new var.
这就是我的 CalculatorBrain 的代码, 我讲为其添加一个新的属性。
1225
01:06:16,800 --> 01:06:21,430
Let's put it I don't know, down here, okay?
我们加在那呢? 就这里吧。
1226
01:06:21,430 --> 01:06:25,040
It's gonna be called program. It's AnyObject.
取名为 program, 类型为 AnyObject。
1227
01:06:25,040 --> 01:06:27,870
Now I'm gonna make it be AnyObject, but I'm also gonna
这里虽然我给他的类型为 AnyObject, 但是我同事也可以给他的类型
1228
01:06:27,870 --> 01:06:30,440
make it be a property list, cuz it's more useful.
为 PropertyList, 因为这个名字更加有可读性。
1229
01:06:30,440 --> 01:06:32,380
People can put it in as user defaults or whatever.
其他人可以将其存在 NSUserDefaults 或者是其他的地方。
1230
01:06:32,380 --> 01:06:35,880
So I'm gonna use a cool Swift thing called typealias.
这里就要用到一个 Swift 的一个特性, 叫做 typealias 。
1231
01:06:35,880 --> 01:06:38,720
typealias lets you create a type, a name type,
typealias 让你可以创建一个新的类型, 一个别名的类型,
1232
01:06:38,720 --> 01:06:41,450
that's exactly the same as some other type. So
它实际上与你绑定的类型是等价的。
1233
01:06:41,450 --> 01:06:45,890
I'm gonna create a type called PropertyList which equals
这里我讲创建一个叫做 PropertyList 的类型,并且它与
1234
01:06:45,890 --> 01:06:50,330
AnyObject. So PropertyList is now a type in Swift.
AnyObject 等价。 那么这样之后, PropertyList 就是一个新的类型了。
1235
01:06:50,330 --> 01:06:52,100
It's exactly the same thing as AnyObject.
但是其实他是跟 AnyObject 等价的。
1236
01:06:52,100 --> 01:06:55,000
Now why would I create this? That's because
为什么我要创建这个类型呢?
1237
01:06:55,000 --> 01:06:57,530
I'm gonna change this to say PropertyList. So
因为我希望这里的类型名称是 PropertyList。
1238
01:06:57,540 --> 01:07:00,240
I'm gonna cha, make the type of my program be PropertyList.
然后将 program 的类型修改成新的类型 PropertyList。
1239
01:07:00,240 --> 01:07:03,110
This tells anyone using my program that yeah,
这就等于告诉任何用 program 属性的人,
1240
01:07:03,110 --> 01:07:06,540
it's AnyObject but it's also a PropertyList, okay?
虽然他是一个 AnyObject 类型,但是它同样是 PropertyList 类型。
1241
01:07:06,550 --> 01:07:09,910
This is documentation. I'm essentially documenting here
这就是文档化, 我本来就是进行文档化
1242
01:07:09,920 --> 01:07:11,720
that this AnyObject is PropertyList.
即 AnyObject 改名为 PropertyList。
1243
01:07:11,720 --> 01:07:14,990
I could have also just put some comments in my code that
并且我也会将在代码中添加对应的注释
1244
01:07:14,990 --> 01:07:18,590
says it is, but this kind of really hammers home the po,
这是一个非常好的做法将 program 的
1245
01:07:18,590 --> 01:07:21,690
the point here that this is a PropertyList,
类型修改成 PropertyList。
1246
01:07:21,690 --> 01:07:25,860
okay? Now I'm gonna have my program here be computed,
现在我将要把我的运算表达式拿来计算。
1247
01:07:25,870 --> 01:07:29,530
okay? So I'm gonna do the get set thing, right? And
因此我要添加 get 和 set 方法。
1248
01:07:29,540 --> 01:07:33,140
so now I need to have my program internally,
因此我需要有一个内部的变量用来存储运算表达式,
1249
01:07:33,140 --> 01:07:35,870
I mean, internally store my program, so I'm actually gonna
我的意思是在该类中存储运算表达式的变量,
1250
01:07:35,880 --> 01:07:39,240
go up to the top and create another private var
因此我们回到程序的上端,添加一个新的私有属性
1251
01:07:39,250 --> 01:07:41,780
which is my internalProgram. And
名叫 internalProgram 的属性。
1252
01:07:41,780 --> 01:07:44,350
so I have to think about how I'm gonna store my program,
因此我需要考虑我应该如何存储我的运算表达式,
1253
01:07:44,350 --> 01:07:48,250
you know, all the operands and operations, internally. And
而我们知道所有的运算表达式都是有操作数和操作符组成的。
1254
01:07:48,250 --> 01:07:50,950
I'm just gonna store it to show you the power of
因此我只需要存储这些信息,并且用来展示 AnyObject 的强大。
1255
01:07:50,960 --> 01:07:56,760
AnyObject. I'm gonna store it as an array of AnyObject,
因此我这里用一个 AnyObject 的数组来存储这些信息。
1256
01:07:56,760 --> 01:08:01,530
okay? And the objects in that array are gonna be a double if
而数组中的元素可能是 Double 类型,当然是一个操作数的时候,
1257
01:08:01,530 --> 01:08:05,500
it's an operand or a string if it's an operation. So
可能是一个 String 类型,当它是一个操作符的时候。
1258
01:08:05,500 --> 01:08:08,410
I'm gonna have an array that has a mix of doubles and
因此我这里需要一个混合了 Double 和 String 类型的数组,
1259
01:08:08,410 --> 01:08:11,280
strings, okay, and that's gonna store my program.
用来存储的运算表达式。
1260
01:08:11,280 --> 01:08:14,010
I'm just gonna put operands in as doubles and I'm gonna put
我只需将操作数以 Double 的形式进行存储,
1261
01:08:14,010 --> 01:08:16,780
operations as strings. So that's another power of
将操作符以 String 的形式进行存储。 这里就能展示 AnyObject 的优势之一了。
1262
01:08:16,780 --> 01:08:20,180
AnyObject here. So how do I implement my internalProgram?
那么我应该如何使用 internalProgram 呢?
1263
01:08:20,190 --> 01:08:24,690
Real simple, okay. When I set an operand, I'm just gonna
非常简单, 当我调用 setOperand 的时候,
1264
01:08:24,690 --> 01:08:29,830
tell my internalProgram append that operand, oops.
我只需将对应的操作数添加到 internalProgram 中即可。
1265
01:08:29,830 --> 01:08:32,430
Okay, now normally this is AnyObject.
当时这里使用的是 AnyObject 类型做为数组元素的类型,
1266
01:08:32,430 --> 01:08:36,900
Operand is a double, that's a struct. That normally wouldn't
但是操作数是一个 Double 的类型, 它是一个Struct。 原本应该是没办法
1267
01:08:36,900 --> 01:08:39,800
work, but the bridging, the Objective-C bridging,
存入数组中的, 但是苹果的桥接技术做到了, 就是与 Objective-C 的桥接技术
1268
01:08:39,810 --> 01:08:43,340
will make this work. Okay, I told you everywhere in the UI
使的操作数能存入数组中。 我之前在讲 UI 的时候也提到过,
1269
01:08:43,340 --> 01:08:45,310
where you have this compatibility,
Swift 与 Objective-C 是具有非常好的兼容性。
1270
01:08:45,310 --> 01:08:46,840
it's gonna automatically bridge.
因为苹果内部已经实现了自动的桥接技术。
1271
01:08:46,850 --> 01:08:48,980
I don't, look, I don't have to do anything or say anything.
而我不需要做任何事情。
1272
01:08:48,980 --> 01:08:52,150
It just automatically always bridges when necessary.
当它需要兼容的时候,就会自动进行桥接。
1273
01:08:52,150 --> 01:08:54,220
Okay, similar down here in my operation.
当然,下面的操作符也是同样的处理方式。
1274
01:08:54,220 --> 01:08:57,150
When someone performs an operation, I'm just gonna tell
当有人调用 performOperation 时, 我只需将它加到
1275
01:08:57,160 --> 01:09:00,420
my internalProgram append the symbol for this operation.
运算表达式的 internalProgram 变量即可。
1276
01:09:00,430 --> 01:09:02,990
Okay, that's a string. Again, a struct but
但是操作符是一个 String, 同样是一个 Struct 类型,
1277
01:09:03,000 --> 01:09:06,200
it's automatically bridged to NSString and
但是它会自动使用桥接技术, 转换成 NSString 类型,
1278
01:09:06,200 --> 01:09:09,330
thus can be an AnyObject. Okay, that's it, that's
所能能将其转换成 AnyObject 类型。
1279
01:09:09,340 --> 01:09:11,470
the entire implementation of my internalProgram.
这就是关于所有的 internalProgram 属性的使用。
1280
01:09:11,470 --> 01:09:14,770
I'm just remembering every operand and operation.
我只需记住没有一个操作数和操作符即可。
1281
01:09:14,770 --> 01:09:19,610
So now I'm just gonna return that internalProgram here,
接着在这里我只需返回 internalProgram 即可,
1282
01:09:19,610 --> 01:09:20,980
okay, to be the program.
用来返回 prgram。
1283
01:09:20,980 --> 01:09:24,150
Now you might be freaking out, whoa, you're returning
也许你会吓到, 为什么你这里
1284
01:09:24,150 --> 01:09:27,780
your internal data structure here to a public caller. But
要讲私有的数据返回给公用的接口?
1285
01:09:27,790 --> 01:09:32,560
what kind of type is an array? A value type. And
但是数组具体是什么类型? 是一个值传递的类型。
1286
01:09:32,560 --> 01:09:36,830
what happens when you return a value type? It gets copied.
那么一个值传递类型的数据作为返回值会出现什么情况? 会用它的副本进行返回。
1287
01:09:36,830 --> 01:09:40,360
Okay, so I'm not returning a pointer to my internal data
因此我不是返回一个指向内部数据的指针对象,
1288
01:09:40,370 --> 01:09:44,370
structure here, I'm returning a copy. Okay, that's, again,
而是返回它的一个副本而已。
1289
01:09:44,370 --> 01:09:47,100
a cool thing about having these things be value types.
由此可以看出一个值传递类型的优点。
1290
01:09:47,110 --> 01:09:50,470
All right, so now we have to do the set. So someone can now
那么解析来我们实现一下 set 方法。 目前别人能获得
1291
01:09:50,480 --> 01:09:53,180
get my program and they can give it back to me later and
我的运算表达式, 并且之后会将其重新交给我,
1292
01:09:53,180 --> 01:09:55,580
I have to run it. Okay, so how am I gonna do that?
而我必须重新运算一遍这个表达式。 那么我该如何实现呢?
1293
01:09:55,580 --> 01:09:59,380
Well, when someone sets my program, first I'm gonna clear
首先,当有人需要重新给我运算表达式的时候, 我先要清空
1294
01:09:59,390 --> 01:10:03,190
whatever's in my program. So you, you guys probably
当前的运算表达式。 而在之后的作业中,
1295
01:10:03,190 --> 01:10:07,490
implemented something like this for your homework, but
也可能会遇到类似的题目。
1296
01:10:07,490 --> 01:10:11,660
I'm gonna, see clear my accumulator. I'm gonna say
而这里我需要重置操作数, 将
1297
01:10:11,660 --> 01:10:15,070
that I have no pending binary operation. And I'm gonna
pending 属性有置为空并且
1298
01:10:15,070 --> 01:10:18,640
clear my internalProgram that I currently have out.
将当前存储的操作数和操作符都清空。
1299
01:10:18,640 --> 01:10:22,240
Actually I'll just removeAll items from my internalProgram.
事实上我只需将数组中的所有元素移除即可。
1300
01:10:22,240 --> 01:10:25,540
Okay, so that's clear cuz I'm running a new program, so
这就是我的清空方法, 然后我需要重新运算新的运算表达式,
1301
01:10:25,540 --> 01:10:26,880
I wanna clear all my stuff out.
但是在此之前我必须重置我的所有属性。
1302
01:10:26,880 --> 01:10:29,910
Right, make sense? Now I'm just gonna say if
而我现在只需判断
1303
01:10:29,920 --> 01:10:34,180
the program they get me, gave me is an array of op,
他们给我的新的运算表达式是否是一个操作数和操作符的数组,
1304
01:10:34,190 --> 01:10:37,050
operands and operations, so I'm gonna
而我这里只需要
1305
01:10:37,060 --> 01:10:41,560
say if I can let arrayOfOps equal this newValue that they
通过 if let arrayOfOps = newValue as? [AnyObject]
1306
01:10:41,560 --> 01:10:46,230
just gave me as an array of AnyObject, which it has to be.
的语句进行判断,并且该判断必须要成立。
1307
01:10:46,230 --> 01:10:48,930
If it's not that, I can just ignore this. Someone gave
如果它不成立的话,我就直接忽视它即可。
1308
01:10:48,930 --> 01:10:51,600
me a program which was not one that I gave out, so I'm
例如有人给了一个运算表达式给我, 但是却不是从我这里给出的,
1309
01:10:51,600 --> 01:10:54,540
ignoring it, cuz it's gotta be an array of AnyObject or
那么我讲忽视它, 因此它必须是一个 AnyObject 的数组,
1310
01:10:54,540 --> 01:10:57,610
I can't figure out what it is. So if it is, now I'm just
否则我无法判断里面具体是什么内容。 那么如果它是一个数组的话,
1311
01:10:57,610 --> 01:10:59,840
gonna go look through at all the ops in there,
我只需遍历所有在数组中
1312
01:10:59,850 --> 01:11:03,550
all the operations and operands. Okay, an arrayOfOps.
的操作数和操作符。
1313
01:11:03,550 --> 01:11:06,620
Okay, that's how we loop through an array, right, for
这就是我们常用的遍历数组的方式———— for in 的方式。
1314
01:11:06,620 --> 01:11:09,950
in. And for each one I'm gonna check and see what it is.
对于每一个元素我都进行检查看看它是什么。
1315
01:11:09,960 --> 01:11:12,460
Now what type, if I can Alt-click on this,
那么这里的 op 是什么类型呢? 如果我通过 Alt + 左键点击op的话,
1316
01:11:12,460 --> 01:11:16,090
what type is this gonna be? >> AnyObject?
会显示什么类型呢? [学生回答] AnyObject ?
1317
01:11:16,100 --> 01:11:16,930
>> AnyObject, exactly.
对的, 就是 AnyObject 类型。
1318
01:11:16,930 --> 01:11:20,530
See, AnyObject. So I can't do anything with AnyObject,
看见没, 就是 AnyObject 类型。 但是我们还没办法处理 AnyObject 类型,
1319
01:11:20,530 --> 01:11:23,100
so I have to try and see if I can make it something else.
因此我必须尝试看看能否做一些其他的事情。
1320
01:11:23,100 --> 01:11:27,400
So I'm gonna first try and make it be an operand by
首先我先进行判断它是否是一个 Double 类型的
1321
01:11:27,410 --> 01:11:32,010
saying op as a Double, okay? So if I get to here,
操作数? 如果运行到这一行的话,
1322
01:11:32,010 --> 01:11:32,140
then this op,
那么这个 op 在数组中的变量
1323
01:11:32,140 --> 01:11:36,380
the next thing I looked in the array was a Double. Excellent,
就是一个 Double 类型的数据。
1324
01:11:36,380 --> 01:11:39,680
then I can just say setOperand to be that operand, okay,
那么接下来我就可以调用 setOperand 来进行进行处理这个操作数。
1325
01:11:39,690 --> 01:11:43,090
cuz I'm running the program. So I'm just gonna replay,
因为我这里是重新运行运算表达式, 所以我这里只需对
1326
01:11:43,090 --> 01:11:46,290
basically, my operands and operations. Otherwise,
操作数和操作符进行一遍运算。
1327
01:11:46,290 --> 01:11:49,990
if I can let, let's say operation = op as a String,
另一方面, 我通过 let operation = op as? String 来进行判断是否是操作数,
1328
01:11:50,000 --> 01:11:54,770
okay, then I'm going to perform that operation.
如果是操作数的话, 这里就调用一下 performOperation 的方法。
1329
01:11:58,540 --> 01:12:03,070
Okay? Done, okay, very simple, I've done it.
实现好了,非常简单吧。
1330
01:12:03,080 --> 01:12:05,810
Now you're going to have to really make sure you
那么目前你必须保证你是理解
1331
01:12:05,810 --> 01:12:09,450
understand this, because you need to enhance this for your
这些代码的, 因为你需要对该项目进行完善, 在作业中
1332
01:12:09,450 --> 01:12:12,750
assignment number two. Because you're gonna have variables,
用到类似的方法。 例如你将用到变量,
1333
01:12:12,750 --> 01:12:15,490
you're gonna have to deal with here too. You're gonna have
而你也需要处理这些变量。
1334
01:12:15,490 --> 01:12:19,020
variables in your program, not just operands and operations.
你需要处理的你的运算表达式, 而不是仅仅的操作数与操作符。
1335
01:12:19,020 --> 01:12:20,520
Now, let's see this thing in action.
那么看我们来实际运行看看。
1336
01:12:20,530 --> 01:12:22,760
We've got this code and we wanna make sure it works, so
我们写好了这些代码,我们需要确认是否它实现成功了,
1337
01:12:22,760 --> 01:12:25,930
let's put something in our UI that actually checks
我们需要在界面上添加一些东西来检查这段
1338
01:12:25,930 --> 01:12:26,700
this program business.
程序的正确性。
1339
01:12:26,700 --> 01:12:27,800
So, I'm gonna go back to my storyboard.
所以我们先回到我们的 Storyboard。
1340
01:12:27,800 --> 01:12:31,600
I'm gonna steal a couple of buttons here. I'm gonna make
我这里只需要修改一些按钮。
1341
01:12:31,600 --> 01:12:34,540
this be the save button and this be the restore button.
将这个按钮变成保存的按钮, 而下面一个按钮变成重新运算的按钮。
1342
01:12:34,540 --> 01:12:37,670
So I'm gonna have the save button, save the program, and
而保存的按钮用来保存之前的运算表达式,
1343
01:12:37,680 --> 01:12:40,940
then I have the restore button, restore the program.
重新运算的按钮用来重新运算之前的的运算表达式。
1344
01:12:40,950 --> 01:12:43,980
Okay, so you can see how it would look like to use that.
这样你就可以想象我们应该如何使用它了。
1345
01:12:43,980 --> 01:12:48,120
So these are not operation buttons anymore, so
而这两个按钮不在是操作符的按钮,
1346
01:12:48,120 --> 01:12:51,990
I'm gonna disconnect using right-click,
所以我需要通过右键来断开之前链接的事件,
1347
01:12:51,990 --> 01:12:54,590
all these perform operations. Okay,
所有这两个按钮的事件回调。
1348
01:12:54,590 --> 01:12:59,760
then I'm gonna wire these up. Let's make some room. Wire up,
解析来我要重新绑定事件的回调。 让我们让 Xcode 多一些控件来进行绑定。
1349
01:12:59,770 --> 01:13:05,200
down here. All right, so I'm gonna wire up save first.
那么我们先对保存按钮进行绑定事件的回调方法。
1350
01:13:05,200 --> 01:13:09,440
Put it right here. I'll call it save. It's an action.
将其拖拉到这里, 并且给方法取名为 save, 设置它是 Action。
1351
01:13:09,440 --> 01:13:09,640
It doesn't need an arg,
该方法不需要参数,
1352
01:13:09,640 --> 01:13:11,980
I don't need the sender this time, this is the first time
这次回调方法不需要一个参数, 这是我们第一次遇到不需要
1353
01:13:11,980 --> 01:13:14,410
we've seen where we don't need the sender. Okay,
参数的回调方法。
1354
01:13:14,410 --> 01:13:16,810
at least the first time not in your homework.
至少不是在你的作业中的第一次遇到。
1355
01:13:16,820 --> 01:13:22,020
And here's restore. And restore is also an action, and
同样的方法拉一个 restore 的事件回调, 同样是一个 Action,
1356
01:13:22,020 --> 01:13:25,260
it doesn't need an argument either. Here we go, there's
同样不需要一个参数。 这样就可以了,
1357
01:13:25,260 --> 01:13:28,530
save and restore. Okay, how am I gonna implement this? Well,
这就是我们的两个按钮的事件回调的方法。 那么我具体该如何实现呢?
1358
01:13:28,530 --> 01:13:32,100
I need the saved program, so I'm gonna have a savedProgram.
我需要保存之前的运算表达式, 所以我需要一个 saveProgram 的属性。
1359
01:13:32,100 --> 01:13:34,560
Its type could be any object, but
它的类型可以是 AnyObject, 但是
1360
01:13:34,570 --> 01:13:40,000
I'm actually gonna have it be CalculatorBrain.PropertyList.
这里更建议使用 CalculatorBrain.PropertyList 的类型。
1361
01:13:40,010 --> 01:13:42,970
Just to be clear to myself, this is a property list, and
首先你的自己清楚, 这是一个 PropertyList,
1362
01:13:42,980 --> 01:13:45,810
if I wanted to save this into NFC or default,
并且如果我我想将其存入 NSUserDefaults 的话,
1363
01:13:45,810 --> 01:13:49,150
I could. Okay, but I'm not going to, but I could.
我也能做到。 但是目前我还没算这样做, 即使我是能做到的。
1364
01:13:49,150 --> 01:13:51,150
All right, so how do I save?
那么我该如何实现 save 方法呢?
1365
01:13:51,150 --> 01:13:52,780
And I'm gonna make this optional because,
在此之前我应该将其变成一个 Optional 的类型,
1366
01:13:52,790 --> 01:13:54,420
of course I might not have hit save yet.
因为我刚开始可能没有保存过预算表达式。
1367
01:13:54,420 --> 01:13:55,090
So it's gonna start out as nil,
那么它的初始值应该是 nil .
1368
01:13:55,090 --> 01:13:57,850
as soon as I hit save it's gonna have a value. All right,
直到我触发 save 方法之后, 它才会有值。
1369
01:13:57,860 --> 01:14:02,430
so how do I save? Well, I'm just gonna say that this
那么我们该如何实现 save 方法呢? 我应该
1370
01:14:02,430 --> 01:14:06,630
savedProgram = the calculators brain.program.
将 CalculatorBrain 中的 program 保存到 savedProgram 即可。
1371
01:14:06,630 --> 01:14:10,400
Okay, that's gonna store it in there, really good, you see?
只需在这里保存即可, 非常简单, 对吧?
1372
01:14:10,400 --> 01:14:15,910
How about restore? Okay, well, if I have a savedProgram
那么 restore 方法又该如何实现呢? 如果 savedProgram 不为
1373
01:14:15,910 --> 01:14:21,080
!= nil, then I'm just gonna set the brains.program
空的话, 我将其重新传递给 CalculatorBrain。
1374
01:14:21,080 --> 01:14:23,710
= savedProgram. Okay, I have to unwrap it,
并且我这里需要强制解析它,
1375
01:14:23,720 --> 01:14:27,350
cuz it's an optional. And then I'd better update my display
因为他是一个 Optional 的类型。 然后我需要更新我
1376
01:14:27,350 --> 01:14:30,390
value now because the brain is gonna have a different result.
显示的值, 因为 CalculatorBrain 可能重新运算后会有不同的结果。
1377
01:14:30,390 --> 01:14:34,420
It's got a new program, it's gonna have a different result,
因为它获得了一个新的运算表达式, 所以它可能会运算跟当前显示不同的结果。
1378
01:14:34,430 --> 01:14:36,230
got it? Okay now, this code,
看明白这段代码了吗?
1379
01:14:36,230 --> 01:14:38,330
you don't need this in your assignment. This is,
这些代码应该在作业中用不到。
1380
01:14:38,330 --> 01:14:41,160
I'm only putting this here just to demonstrate program.
这只是我放在这里的实例代码而已。
1381
01:14:41,170 --> 01:14:43,170
You will need the other code in CalculatorBrain,
你需要在 CalculatorBrain 中的其他代码,
1382
01:14:43,170 --> 01:14:46,340
but you will not need this code. Okay, let's go ahead and
而不需要这段代码。 那么让我重新
1383
01:14:46,340 --> 01:14:55,310
run this. All right,
运行这个程序。
1384
01:14:55,310 --> 01:14:57,780
here we go, make this a little bigger. All right,
将模拟器变大一点。
1385
01:14:57,780 --> 01:15:00,620
let's rotate so that our restore button looks better.
将模拟器进行旋转一下, 这样我们的 restore 按钮看起来更美观一点。
1386
01:15:00,620 --> 01:15:03,290
All right, here we go. So let's put a program in here,
我们先输入一个表达式,
1387
01:15:03,290 --> 01:15:10,060
4 x 5 + 1 =. Okay, that's 21. Okay, let's save that program,
输入 4 乘以 5 加上 1 等于。 答案是21. 然后点击保存按钮。
1388
01:15:10,060 --> 01:15:14,930
now let's do pi square root. Okay, that's good, + 8 =,
然后在点击π、 根号、 加号、 8 等一些其他按钮。
1389
01:15:14,930 --> 01:15:18,740
something like that. Now let's restore. What should happen to
然后点击 restore 按钮, 那么会发生什么呢?
1390
01:15:18,740 --> 01:15:21,540
our display when I restore? >> 21.
如果我点击 restore 后。 [学生回答] 21.
1391
01:15:21,540 --> 01:15:22,370
>> 21 cuz it's gonna rerun
答案是21, 因为我们重新运算了之前的
1392
01:15:22,370 --> 01:15:27,440
that program. And sure enough, there it is.
运算表达式。 这个是可以肯定的。
1393
01:15:27,450 --> 01:15:30,480
Okay, 14 divided by 7=, restore. Okay, we, we,
14 除以 7 等于, 点击restore。 看见没?
1394
01:15:30,480 --> 01:15:34,450
we've run that program, and that's the result in there, so
我们重新运算了之前的表达式, 所以它的结果就展示在这里了。
1395
01:15:34,450 --> 01:15:38,890
we can say x 2 =. Okay, we could save
而我们可以针对结果继续运算, 如乘以 2 等于之类的。
1396
01:15:38,890 --> 01:15:42,530
it again. Okay, 47 divide 8 =, and now when we restore,
当然我们重新保存新的表达式。 继续输入 47 除以 8 等于, 然后我们在点击 restore 按钮,
1397
01:15:42,530 --> 01:15:45,300
we'll get 42, which is the meaning of life, the universe,
我们还是能拿到 42,也就是说无论从哪个角度来
1398
01:15:45,300 --> 01:15:47,900
and everything, and so we're done, okay. So
说, 我们都完成了想要的功能。
1399
01:15:47,900 --> 01:15:52,500
get that program to code into your app, okay.
将这部分代码拷贝到你们的程序中,
1400
01:15:52,500 --> 01:15:55,570
You'll need to add that to your assignment one, and then
你将会在作业一种需要用到这些代码,
1401
01:15:55,570 --> 01:15:58,610
I will be publishing, I might actually publish assignment
当然我会将这些代码发布出来, 我也会将作业二
1402
01:15:58,610 --> 01:16:01,110
two before Wednesday, okay. It goes out on Wednesday,
在周三前发布出来。 作业将在周三发布,
1403
01:16:01,110 --> 01:16:03,280
due the next Wednesday. But for those of you who want to
直到下周三为止。 当然对于那些想先多学一点
1404
01:16:03,280 --> 01:16:05,580
start a little early, cuz you know everything you need to
的同学们, 因为已经学会了一些
1405
01:16:05,580 --> 01:16:08,650
know now to do assignment two as well, okay. So
你们也可以提前做作业二。
1406
01:16:08,650 --> 01:16:13,060
I'll probably publish that earlier, maybe tomorrow.
我会尽早将其发布出来,也可能是明天。
1407
01:16:13,060 --> 01:16:13,520
>> For more,
想获得更多信息,
1408
01:16:13,530 --> 01:16:13,560
please visit us at stanford.edu.
请访问我们的网站 stanford.edu。
================================================
FILE: subtitles/4. Views.srt
================================================
1
00:00:00,001 --> 00:00:03,769
[MUSIC]
2
00:00:03,771 --> 00:00:07,573
Stanford University. Okay,
3
00:00:07,575 --> 00:00:11,910
well welcome to Lecture four of Stanford's CS193P,
欢迎来到2016春季Stanford CS193P 第四讲
4
00:00:11,912 --> 00:00:16,682
spring of 2016. Today our topic is views. Okay,
我们今天的主题是Views
5
00:00:16,684 --> 00:00:19,485
views are the rectangular areas on screen that we draw
view就是我们在屏幕上绘画的矩形区域
6
00:00:19,487 --> 00:00:22,821
in. Its also the places where we handle multi-touch,
它也是我们处理多点触摸的地方
7
00:00:22,823 --> 00:00:26,392
we'll be talking about that next week. So today is,
我们将在下周讨论这个
8
00:00:26,394 --> 00:00:28,560
it's all about the drawing side and I have, of course,
所以今天我们只讨论绘图方面
9
00:00:28,562 --> 00:00:32,765
have a demo where we're gonna draw a custom view, okay? So
当然,我们有一个将绘出自定义view的Demo
10
00:00:32,767 --> 00:00:35,634
let's talk about views. The view is that rectangular area,
所以让我们来讨论一下views,View是一块矩形的区域
11
00:00:35,636 --> 00:00:39,104
as I said. It defines a coordinate space, okay?
正如我所说,它定义了一块坐标空间
12
00:00:39,106 --> 00:00:42,041
So, any time you're drawing or handling input, or whatever,
所以,任何时候你需要绘图、处理输入或者其他的事情时
13
00:00:42,043 --> 00:00:45,010
you need a coordinate space to be defined for you. And,
你需要一块定义好的坐标空间
14
00:00:45,012 --> 00:00:49,581
use that space to draw and to handle touch events. This kind
并且利用这块空间来绘图和处理触摸事件
15
00:00:49,583 --> 00:00:53,886
of view setup is hierarchical, as you might imagine, so,
这种view是分层的,如你所想
16
00:00:53,888 --> 00:00:58,824
you can imagine that you have some view, like maybe this is
你可以想象你拥有一个view,也许
17
00:00:58,826 --> 00:01:01,960
the view that contains your entire UI for an MVC.
包含了你的MVC中全部UI的view
18
00:01:01,962 --> 00:01:05,130
And then, you've got some sub views maybe they're buttons,
随后你得到了一些子view,比如按钮
19
00:01:05,132 --> 00:01:08,233
or whatever, and they might be inside a view,
或是其他什么东西。他们可能在一个view的内部
20
00:01:08,235 --> 00:01:10,602
like let's say a stack view or something like that.
比如我们说过的StackView或是其他类似的东西
21
00:01:10,604 --> 00:01:13,172
And, maybe those are in another view, okay? So,
这些可能都在另外的一个view之中,okay?
22
00:01:13,174 --> 00:01:16,075
you can see how these things are hierarchical, right?
你们能够看出这些东西的层级结构吗对吗?
23
00:01:16,077 --> 00:01:19,311
You're building your views in a hierarchy,
你们将一层一层的来构建你的视图
24
00:01:19,313 --> 00:01:22,848
a view hierarchy we call it, okay? Now,
我们称作视图的层级, okay?
25
00:01:22,850 --> 00:01:26,752
these views can overlap, obviously.
显然,view是可以部分重叠的
26
00:01:26,754 --> 00:01:30,089
And, they also, even if you have a view inside a view, so
view也可以在另外的view之中
27
00:01:30,091 --> 00:01:31,657
a view, let's say in this view right here,
28
00:01:31,659 --> 00:01:35,427
if you had another view inside it, it could actually extend
29
00:01:35,429 --> 00:01:39,665
outside the bounds of its parent, that's legal. And,
30
00:01:39,667 --> 00:01:42,134
you can set a switch in the view whether it's going to
31
00:01:42,136 --> 00:01:45,871
clip, and only show this part, or whether it'll allow this
32
00:01:45,873 --> 00:01:50,742
thing to draw outside of its parent, okay? So,
33
00:01:50,744 --> 00:01:52,478
that's how we build our user interface,
34
00:01:52,480 --> 00:01:54,113
is by kinda grouping these things together.
35
00:01:54,115 --> 00:01:57,116
You're totally familiar with that in the calculator.
36
00:01:57,118 --> 00:02:01,553
And, when you build something like this, every view ends up
37
00:02:01,555 --> 00:02:04,857
having a single super view, right? That's the view that
38
00:02:04,859 --> 00:02:07,893
it's inside. And, any given view could have
39
00:02:07,895 --> 00:02:11,230
any number of sub-views, okay? Like the stack views could
40
00:02:11,232 --> 00:02:14,600
have any number of things stacked inside of whatever.
41
00:02:14,602 --> 00:02:18,604
The sub-views, you can find them for a view by looking at
42
00:02:18,606 --> 00:02:21,240
this VAR, the CUI view VAR called sub-views.
43
00:02:21,242 --> 00:02:25,210
You see it's an array of UI view there, and,
44
00:02:25,212 --> 00:02:27,279
we'll talk a little later about the order of those
45
00:02:27,281 --> 00:02:31,450
sub-views matters. And that's it basically,
46
00:02:31,452 --> 00:02:35,287
okay? That's how we represent this new hierarchy.
47
00:02:35,289 --> 00:02:40,225
There is a UI window in iOS, but it almost doesn't matter,
48
00:02:40,227 --> 00:02:42,027
okay? You're never gonna interact with it.
49
00:02:42,029 --> 00:02:45,130
There's always only one, per app. Actually, you could have
50
00:02:45,132 --> 00:02:48,767
more than one if you had like an external screen, okay?
51
00:02:48,769 --> 00:02:53,705
Like you were doing, what do we call, the, the screen cast,
52
00:02:53,707 --> 00:02:57,709
it's not coming to me, where you can, have a second screen.
53
00:02:57,711 --> 00:03:00,345
I can't think of it but, like on Apple TV,
54
00:03:00,347 --> 00:03:01,180
you could have a second screen for
55
00:03:01,182 --> 00:03:05,984
your device. Whatever, so then you might have two UI windows,
56
00:03:05,986 --> 00:03:07,452
but it wouldn't matter cuz you're almost never gonna
57
00:03:07,454 --> 00:03:09,588
create one or send a message to one or whatever. So,
58
00:03:09,590 --> 00:03:12,925
it's all about views, it's all about this view hierarchy that
59
00:03:12,927 --> 00:03:15,694
we're gonna build okay, in iOS. It's unlike on the Mac,
60
00:03:15,696 --> 00:03:17,829
where of course the Mac you've got windows, right,
61
00:03:17,831 --> 00:03:20,899
separate windows. You don't really have that, in iOS,
62
00:03:20,901 --> 00:03:25,504
not enough room really to do that. This hierarchy
63
00:03:25,506 --> 00:03:28,574
is most often constructed in Xcode graphically.
64
00:03:28,576 --> 00:03:31,677
Like you did with the calculator, okay? But,
65
00:03:31,679 --> 00:03:33,745
you can build this hierarchy in code.
66
00:03:33,747 --> 00:03:36,181
The two methods are add subview, and
67
00:03:36,183 --> 00:03:38,450
remove from super view, that's putting them in and
68
00:03:38,452 --> 00:03:41,186
taking them out., Notice that add subview is sent
69
00:03:41,188 --> 00:03:44,022
to the future super view of the view saying, hey,
70
00:03:44,024 --> 00:03:47,059
or add this sub view to yourself. Remove from subview
71
00:03:47,061 --> 00:03:50,195
is actually sent to the view you want to remove, okay?
72
00:03:50,197 --> 00:03:53,565
So, remove from subview from super view rather is kind of
73
00:03:53,567 --> 00:03:56,668
like remove yourself from your super view, okay?
74
00:03:56,670 --> 00:04:00,839
Where does the view hierarchy start? It starts
75
00:04:00,841 --> 00:04:05,077
right at the top, in that MVC that we see in the storyboard,
76
00:04:05,079 --> 00:04:07,446
there's a view that fills that whole space.
77
00:04:07,448 --> 00:04:10,682
There is a pointer from your view controller to that top
78
00:04:10,684 --> 00:04:15,854
level view, okay? That pointer is called view. So,
79
00:04:15,856 --> 00:04:17,522
if you look at view controller, right,
80
00:04:17,524 --> 00:04:18,523
everyone knows what view controller is?
81
00:04:18,525 --> 00:04:20,826
That's where you put a lot of your code in the calculator,
82
00:04:20,828 --> 00:04:23,762
right? Where you wired up all your outlets and actions.
83
00:04:23,764 --> 00:04:27,032
That class, view Controller has a var called view.
84
00:04:27,034 --> 00:04:28,267
You haven't used it yet. But,
85
00:04:28,269 --> 00:04:32,437
it is a pointer to the top level UI View. Okay, and
86
00:04:32,439 --> 00:04:34,573
that's an important view to have a pointer to because
87
00:04:34,575 --> 00:04:36,642
you're gonna be adding subviews to it, etcetera,
88
00:04:36,644 --> 00:04:39,011
especially if you're doing it programmatically.
89
00:04:39,013 --> 00:04:39,578
You really need this var so
90
00:04:39,580 --> 00:04:43,882
you can call add subview on it and put some things in there.
91
00:04:43,884 --> 00:04:45,484
And, this view is automatically hooked up for
92
00:04:45,486 --> 00:04:48,487
you, in your storyboard. So, you don't have to do anything.
93
00:04:48,489 --> 00:04:51,823
It's just automatically hooked up, okay? And,
94
00:04:51,825 --> 00:04:55,060
this top level view is the view whose bounds will change,
95
00:04:55,062 --> 00:04:57,496
for example, when you rotate your device, right?
96
00:04:57,498 --> 00:05:01,767
The bounds go from being tall and thin, to being short and
97
00:05:01,769 --> 00:05:05,237
wide, okay? That's the bounds of this top level view.
98
00:05:05,239 --> 00:05:08,006
Now, when you change the bounds of this top over view,
99
00:05:08,008 --> 00:05:09,875
if you have constrains, like if you put it in
100
00:05:09,877 --> 00:05:12,944
the calculator where we tied the stack view to the edges?
101
00:05:12,946 --> 00:05:15,714
When those edges change, of course, the stack view gets
102
00:05:15,716 --> 00:05:18,550
pulled around and stretched. And, the stack view knows how
103
00:05:18,552 --> 00:05:20,786
to resize and distribute the things inside of it.
104
00:05:20,788 --> 00:05:23,088
That's part of what the stack view does. That's pretty much
105
00:05:23,090 --> 00:05:26,258
all it does actually. So that's why rotating,
106
00:05:26,260 --> 00:05:29,194
changing the bounds of this top view cause a ripple effect
107
00:05:29,196 --> 00:05:33,765
where everything re-positions inside, okay? Now,
108
00:05:33,767 --> 00:05:37,002
let's talk a little bit about initializing a UI View,
109
00:05:37,004 --> 00:05:41,306
okay? As always we want to try to avoid doing an initializer
110
00:05:41,308 --> 00:05:44,009
if we can just get away with just saying equals whatever,
111
00:05:44,011 --> 00:05:46,778
but if you have to have an initializer because you just
112
00:05:46,780 --> 00:05:50,649
feel like you just can't initialize a VAR somehow
113
00:05:50,651 --> 00:05:53,719
else. Then you have to be careful with UIView because it
114
00:05:53,721 --> 00:05:56,922
actually has two important initializers, okay?
115
00:05:56,924 --> 00:05:59,391
One of them is required, that's the second one,
116
00:05:59,393 --> 00:06:03,528
init with coder. That init is the init that's used to create
117
00:06:03,530 --> 00:06:07,699
the UIView when it's coming out of a storyboard, okay?
118
00:06:07,701 --> 00:06:09,701
So, if you built this view by dragging it out into
119
00:06:09,703 --> 00:06:13,372
your storyboard, when the storyboard gets reconstituted
120
00:06:13,374 --> 00:06:16,108
at run time, then this is the init that's gonna get
121
00:06:16,110 --> 00:06:18,844
controlled, init with coder. The init with frame,
122
00:06:18,846 --> 00:06:20,746
that's the init that you're going to call if you create
123
00:06:20,748 --> 00:06:24,616
a view in code, okay? And, that frame that you're doing,
124
00:06:24,618 --> 00:06:28,687
specifying is the frame of this view in its super view,
125
00:06:28,689 --> 00:06:30,455
in other words where it's going to be, where
126
00:06:30,457 --> 00:06:35,093
this view is going to be. And, it is legal to, to just do
127
00:06:35,095 --> 00:06:39,731
init with no arguments. That's gonna essentially be ZeroRect.
128
00:06:39,733 --> 00:06:40,799
You're just gonna put it, like,
129
00:06:40,801 --> 00:06:43,468
up here in the left-hand corner really, really small.
130
00:06:43,470 --> 00:06:47,139
You can set a frame later to move it. Anyway, since you
131
00:06:47,141 --> 00:06:49,374
have two of these things, and one of them is required,
132
00:06:49,376 --> 00:06:52,644
the second one, you're gonna end up implementing both,
133
00:06:52,646 --> 00:06:55,547
okay? Because you're gonna want your views to be able to
134
00:06:55,549 --> 00:06:58,517
work from storyboards or you want people to be able to,
135
00:06:58,519 --> 00:07:01,386
you know, instantiate them from code, okay? So,
136
00:07:01,388 --> 00:07:04,356
I recommend putting, all your initialization
137
00:07:04,358 --> 00:07:07,359
code in something like a set up function here. And
138
00:07:07,361 --> 00:07:09,728
then, override both of these two and
139
00:07:09,730 --> 00:07:10,695
call set up from both of them.
140
00:07:10,697 --> 00:07:12,931
That way they'll both be doing exactly the same set up,
141
00:07:12,933 --> 00:07:16,234
right? Makes sense, kinda obvious. But, I just wanna
142
00:07:16,236 --> 00:07:18,336
make sure you know you gotta do both of these.
143
00:07:18,338 --> 00:07:22,407
[COUGH] All right, another initialization mechanism that
144
00:07:22,409 --> 00:07:24,876
you can do for UI view, but only works for
145
00:07:24,878 --> 00:07:26,378
views coming out of storyboards,
146
00:07:26,380 --> 00:07:29,014
is to put your code in this method awakeFromNib().
147
00:07:29,016 --> 00:07:32,217
Okay, awakeFromNib() is actually called on any object
148
00:07:32,219 --> 00:07:35,420
that comes out of a storyboard, okay? But,
149
00:07:35,422 --> 00:07:40,692
not called at all in you call an object in code. And, so,
150
00:07:40,694 --> 00:07:43,562
you can, you know, put same kind of set up stuff that you
151
00:07:43,564 --> 00:07:46,064
would put in the other one as long as you're fine with your
152
00:07:46,066 --> 00:07:49,301
view only working when it's coming from a storyboard,
153
00:07:49,303 --> 00:07:52,804
okay? All right. So, now let's talk about the drawing.
154
00:07:52,806 --> 00:07:55,006
Okay, I've got this UI view and I wanna draw.
155
00:07:55,008 --> 00:07:57,843
Well, before I can show you how to draw, you know,
156
00:07:57,845 --> 00:08:01,546
draw something inside of these Your custom view, and I need
157
00:08:01,548 --> 00:08:05,083
to talk about some types, some data types, okay, the first
158
00:08:05,085 --> 00:08:09,120
one here is CG float. So, we don't use doubles or float,
159
00:08:09,122 --> 00:08:12,257
I haven't really talked about the float struct in Swift, but
160
00:08:12,259 --> 00:08:13,391
it's just like double. It's just,
161
00:08:13,393 --> 00:08:16,761
float is single precision, and double is double precisions,
162
00:08:16,763 --> 00:08:18,230
that's why it's called a double.
163
00:08:18,232 --> 00:08:20,532
But we don't use floats or doubles when we're drawing,
164
00:08:20,534 --> 00:08:23,835
we use this special struct called CGFloat,
165
00:08:23,837 --> 00:08:27,072
okay? And that means that sometimes, you're going to be
166
00:08:27,074 --> 00:08:29,908
doing calculations in doubles and then you're gonna want to
167
00:08:29,910 --> 00:08:32,777
draw, with the result you're gonna have to convert it
168
00:08:32,779 --> 00:08:36,948
to a CGFloat. By doing CGFloat called as initializer,
169
00:08:36,950 --> 00:08:39,618
there's an initializer for CGFloat that takes a double
170
00:08:39,620 --> 00:08:42,754
luckily and the other takes a float, okay?
171
00:08:42,756 --> 00:08:45,790
So always using CGFloats and you'll run into some trouble
172
00:08:45,792 --> 00:08:49,461
here and there, because you're trying to pass doubles into
173
00:08:49,463 --> 00:08:54,266
API that takes CGFloats, cuz it's drawing API, okay?
174
00:08:54,268 --> 00:08:55,901
There's a couple of other obvious structs.
175
00:08:55,903 --> 00:08:59,471
There's CGPoint that just has two bars in it, one is the x,
176
00:08:59,473 --> 00:09:03,241
and one is the y, tight? That represents a point and then
177
00:09:03,243 --> 00:09:06,611
a CGSize, same thing, a struct with two things, a width and
178
00:09:06,613 --> 00:09:11,182
a height. Okay, those are easy. Then there's CGRect.
179
00:09:11,184 --> 00:09:15,620
CGRect gets it's own slide because even though it's
180
00:09:15,622 --> 00:09:18,323
obvious, it has an origin which is a CGPoint and
181
00:09:18,325 --> 00:09:20,525
a size which is a size, right, it's a rectangle,
182
00:09:20,527 --> 00:09:24,262
has an origin and a size. It also has a lot of methods on
183
00:09:24,264 --> 00:09:27,566
it, okay? Cool methods you can use or vars and
184
00:09:27,568 --> 00:09:31,102
methods like finding the minimum x value
185
00:09:31,104 --> 00:09:35,974
of this rectangle or the mid y point of this thing or
186
00:09:35,976 --> 00:09:38,510
even intersect this with another rectangle and
187
00:09:38,512 --> 00:09:42,013
give me the intersecting part like this slashed area here,
188
00:09:42,015 --> 00:09:46,151
okay? Or this one intersects which says does this rect
189
00:09:46,153 --> 00:09:49,788
intersect this other rect. You can imagine there are a couple
190
00:09:49,790 --> 00:09:52,123
of dozen of these kinds of utility methods.
191
00:09:52,125 --> 00:09:53,491
So you want to definitely get to know them so
192
00:09:53,493 --> 00:09:56,561
you don't end up writing these methods yourself when you want
193
00:09:56,563 --> 00:10:00,365
to intersect two rects or whatever, okay? So those
194
00:10:00,367 --> 00:10:03,635
are the main types that we are going to use when we draw. So
195
00:10:03,637 --> 00:10:07,172
you're gonna be familiar with those types. All right, let's
196
00:10:07,174 --> 00:10:09,708
talk about the coordinate system we're gonna draw in in
197
00:10:09,710 --> 00:10:15,413
our view, okay? The origin is in the upper left, upper left,
198
00:10:15,415 --> 00:10:18,583
okay. It's not lower left like Cartesian coordinates or
199
00:10:18,585 --> 00:10:19,250
like the Mac okay,
200
00:10:19,252 --> 00:10:23,822
when you draw on the Mac, if you're building a Mac app,
201
00:10:23,824 --> 00:10:23,955
it's in the upper left.
202
00:10:23,957 --> 00:10:26,424
That means increasing y means you're moving down the screen,
203
00:10:26,426 --> 00:10:30,228
okay. So if you look at this point I created up here, 535,
204
00:10:30,230 --> 00:10:35,100
notice it's 500 over in x, but only 35 down in y.
205
00:10:35,102 --> 00:10:37,636
That's why it's way over on that side there,
206
00:10:37,638 --> 00:10:41,706
okay. The points that we are talking about here in drawing
207
00:10:41,708 --> 00:10:43,642
when I mention all of this, or
208
00:10:43,644 --> 00:10:47,312
the unit if you will are called points, okay. Now
209
00:10:47,314 --> 00:10:52,417
a point is not a pixel. Some iOS devices are extremely high
210
00:10:52,419 --> 00:10:56,354
resolution like the IPhone Plus, the IPhone 6 Plus, very,
211
00:10:56,356 --> 00:11:00,692
very high resolution. It has three pixels per point, okay.
212
00:11:00,694 --> 00:11:02,727
Some of the other devices have two pixels per point,
213
00:11:02,729 --> 00:11:05,864
some have one. Now yo're always drawing in points.
214
00:11:05,866 --> 00:11:08,266
Usually, you do't care too much about the pixels.
215
00:11:08,268 --> 00:11:10,402
If you have a lot of pixels, it just means the lines
216
00:11:10,404 --> 00:11:12,570
that you draw are gonna look really smooth or
217
00:11:12,572 --> 00:11:14,973
the images that you have are gonna be really detailed.
218
00:11:14,975 --> 00:11:17,175
You are gonna be able to use a lot of image data
219
00:11:17,177 --> 00:11:20,245
to draw them, okay? But if you do care, and
220
00:11:20,247 --> 00:11:23,682
actually in assignment three, you are gonna care, so
221
00:11:23,684 --> 00:11:27,385
pay attention. If you do care, you can find out by asking
222
00:11:27,387 --> 00:11:30,021
a UIView what is your content scale factor?
223
00:11:30,023 --> 00:11:35,927
That's basically how many pixels per point okay.
224
00:11:35,929 --> 00:11:38,830
The boundaries where you draw in, so this is super important
225
00:11:38,832 --> 00:11:40,732
and this is where a lot of confusion comes in,
226
00:11:40,734 --> 00:11:42,701
that's why I spend a lot of time in this even though it's
227
00:11:42,703 --> 00:11:46,604
very simple but people just get these things messed up.
228
00:11:46,606 --> 00:11:49,808
Let's talk about the rectangle in which you draw, okay,
229
00:11:49,810 --> 00:11:51,843
cuz you're gonna draw inside a rectangle. When you
230
00:11:51,845 --> 00:11:55,547
are drawing, you're using this var in your UI view.
231
00:11:55,549 --> 00:11:58,883
It's called bounds. It's a CGRect. That is the rectangle
232
00:11:58,885 --> 00:12:01,920
in which you're drawing in your own coordinate system, in
233
00:12:01,922 --> 00:12:04,956
the coordinate system you're drawing in, okay. So any time
234
00:12:04,958 --> 00:12:10,028
you're writing drawing code, you're using this, okay?
235
00:12:10,030 --> 00:12:14,332
There's another thing called frame, which is also CGRect,
236
00:12:14,334 --> 00:12:18,103
which people confuse between these two. But frame is
237
00:12:18,105 --> 00:12:21,072
completely different. It has a completely different purpose,
238
00:12:21,074 --> 00:12:23,975
and it's in a completely different coordinate system.
239
00:12:23,977 --> 00:12:27,212
Okay, so if try to use frame instead of bounds,
240
00:12:27,214 --> 00:12:28,947
it might work in certain circumstances but
241
00:12:28,949 --> 00:12:32,183
then be messed up in others. So what's frame about?
242
00:12:32,185 --> 00:12:36,054
Frame is about where your view is in it's super view?
243
00:12:36,056 --> 00:12:39,791
Frame is a rectangle, a CG rect that completely encloses
244
00:12:39,793 --> 00:12:44,929
you in your super views coordinate system. Okay, so
245
00:12:44,931 --> 00:12:47,632
when I wanna put some view somewhere,
246
00:12:47,634 --> 00:12:50,769
I have to specify where in it's SuperView it goes.
247
00:12:50,771 --> 00:12:53,238
I do that by specifying it's frame. And of course,
248
00:12:53,240 --> 00:12:55,607
since I'm talking about putting it into SuperView,
249
00:12:55,609 --> 00:12:57,375
that's in the SuperView's coordinate system,
250
00:12:57,377 --> 00:13:00,678
not in the drawing, coordinate system on the inside there,
251
00:13:00,680 --> 00:13:05,049
okay? Similarly, centre, a lot of people think, great,
252
00:13:05,051 --> 00:13:08,486
that's the center of my view. But it's not, okay?
253
00:13:08,488 --> 00:13:11,489
That's the centre of your view in your SuperViews coordinate
254
00:13:11,491 --> 00:13:13,992
system. So that's positioning you, okay?
255
00:13:13,994 --> 00:13:15,960
Frame and center are positioning you.
256
00:13:15,962 --> 00:13:17,562
They have nothing to do with your drawing.
257
00:13:17,564 --> 00:13:20,865
When you're drawing, you're using your bounds, okay? So
258
00:13:20,867 --> 00:13:23,735
I do not want to see you using frame inside your drawing
259
00:13:23,737 --> 00:13:28,072
code, or centre. Got it? And I'll emphasize this again in
260
00:13:28,074 --> 00:13:33,378
the demo. All right, so one thing about frame and
261
00:13:33,380 --> 00:13:36,414
centre, you might think that the size and
262
00:13:36,416 --> 00:13:38,149
width are going to be the same for the frame and
263
00:13:38,151 --> 00:13:42,420
centre, but they're not because views can be rotated.
264
00:13:42,422 --> 00:13:44,856
If you had a rotated view like view B, okay,
265
00:13:44,858 --> 00:13:50,128
it's bounds is 200 wide by 250 high you see that, but
266
00:13:50,130 --> 00:13:54,432
look at his frame 320 by 320 why? Because this is
267
00:13:54,434 --> 00:13:58,002
the smallest rectangle in the super views coordinate system
268
00:13:58,004 --> 00:14:03,241
that will completely contain this rotated view, okay?
269
00:14:03,243 --> 00:14:06,544
So the width and height are not the same, okay?
270
00:14:06,546 --> 00:14:10,782
This is the frame 320 by 320, this is the bounds. When
271
00:14:10,784 --> 00:14:14,152
you're drawing in B, you don't even know you're rotated, so
272
00:14:14,154 --> 00:14:17,055
you want to make sure that you're using
273
00:14:17,591 --> 00:14:21,726
this coordinate system right here, bounds, okay?
274
00:14:21,728 --> 00:14:26,497
All right, creating views, okay, most of the time you're
275
00:14:26,499 --> 00:14:29,167
creating views in your story board by dragging them out.
276
00:14:29,169 --> 00:14:31,736
Now if you have a custom view that's drawing something
277
00:14:31,738 --> 00:14:35,406
custom like today we're gonna do a thing where we draw
278
00:14:35,408 --> 00:14:40,278
a face view, it's gonna be a view Okay.
279
00:14:40,280 --> 00:14:43,448
Looks like this, got a couple eyes. Smiley face, okay?
280
00:14:43,450 --> 00:14:46,985
I'm drawing, it's got a skull there. When I'm drawing this,
281
00:14:46,987 --> 00:14:51,155
there's no face view in the object palate, right?
282
00:14:51,157 --> 00:14:52,657
I can't drag that out into my story board.
283
00:14:52,659 --> 00:14:55,627
So, how do I get my face view to appear somewhere in
284
00:14:55,629 --> 00:14:56,794
my story board. Well, the answer is,
285
00:14:56,796 --> 00:14:59,697
I'm going to drag out a generic view, okay,
286
00:14:59,699 --> 00:15:03,401
a generic UI view, unsubclassed UI view, and then
287
00:15:03,403 --> 00:15:07,038
I'm going to change it, edit it in this identity inspector,
288
00:15:07,040 --> 00:15:09,641
which I'll show you in the demo to change the class of
289
00:15:09,643 --> 00:15:12,110
it, so it's a subclass of UI view. And it's in that
290
00:15:12,112 --> 00:15:14,545
subclass of UI view that I'm gonna put all my drawing code,
291
00:15:14,547 --> 00:15:19,751
all my face drawing code, okay? Now on rare occasion,
292
00:15:19,753 --> 00:15:22,854
you will create a view in code, okay. You do that just
293
00:15:22,856 --> 00:15:26,257
by doing, calling UIView's little constructor here.
294
00:15:26,259 --> 00:15:29,694
You're gonna use the one with frame, not the one with coder,
295
00:15:29,696 --> 00:15:32,897
okay. You can also do UIView with no arguments,
296
00:15:32,899 --> 00:15:35,033
that means the tiny little UIView.
297
00:15:35,035 --> 00:15:36,734
You'll have to set it's frame otherwise.
298
00:15:36,736 --> 00:15:39,671
So here's what it looks like to create a view in code.
299
00:15:39,673 --> 00:15:41,606
Here I just created a rectangle which is 20/20, 150.
300
00:15:41,608 --> 00:15:45,877
So that's like 20, 20, 150. So this is this rectangle
301
00:15:47,113 --> 00:15:50,848
right about there and the, so
302
00:15:50,850 --> 00:15:54,218
then I'm creating a view. This view happens to be a UI label.
303
00:15:54,220 --> 00:15:56,087
UI label is just a sub class UI view.
304
00:15:56,089 --> 00:15:58,756
So it' s UI button, so it's UI stack view. All of these
305
00:15:58,758 --> 00:16:01,492
things are just sub classes of UI view. So I'm creating a sub
306
00:16:01,494 --> 00:16:03,928
code view using that frame thing using that frame thing,
307
00:16:03,930 --> 00:16:06,431
okay? I'm even setting the elabels text, all right?
308
00:16:06,433 --> 00:16:10,401
Then I'm going to say view, let's say this code is
309
00:16:10,403 --> 00:16:15,473
in the view controller, say view.addsubvie.
310
00:16:15,475 --> 00:16:18,109
Then all the sudden it's gonna appear here,
311
00:16:18,111 --> 00:16:22,780
okay? Here's that 20/20. And it's 100 by 50,
312
00:16:22,782 --> 00:16:27,986
okay? That's it. Very simple to add views with code,
313
00:16:27,988 --> 00:16:32,690
okay? Add subview. All right, custom view. So why do I want
314
00:16:32,692 --> 00:16:34,759
to create my own custom view subclass?
315
00:16:34,761 --> 00:16:37,762
Obviously you want to have a smiley face, okay?
316
00:16:37,764 --> 00:16:40,698
So you need to create your own custom one to do that. And
317
00:16:40,700 --> 00:16:43,568
you might have some pinches or swipes or
318
00:16:43,570 --> 00:16:46,304
something, which we'll do next week. For your face,
319
00:16:46,306 --> 00:16:49,040
you need a custom view to do those things, okay? So let's
320
00:16:49,042 --> 00:16:52,944
talk about the drawing end of that today. How do I draw?
321
00:16:52,946 --> 00:16:56,514
It's really easy. You're just gonna subclass UI view and
322
00:16:56,516 --> 00:16:59,150
you're gonna override this one method,
323
00:16:59,152 --> 00:17:02,720
drawRect, okay? DrawRect takes one argument,
324
00:17:02,722 --> 00:17:06,858
which is an optimization only, which is what part of
325
00:17:06,860 --> 00:17:08,559
the view the system wants you to draw.
326
00:17:08,561 --> 00:17:10,261
It might want you to draw the whole thing.
327
00:17:10,263 --> 00:17:13,631
But maybe it just wants you to draw the eyes, okay?
328
00:17:13,633 --> 00:17:14,399
Now you can ignore that and
329
00:17:14,401 --> 00:17:16,868
draw the whole thing anyway, and it will work. Or
330
00:17:16,870 --> 00:17:18,970
if you can efficiently just draw the eyes,
331
00:17:18,972 --> 00:17:21,739
then you might decide to just draw the eyes, okay. But
332
00:17:21,741 --> 00:17:24,008
this argument right here is purely an optimization.
333
00:17:24,010 --> 00:17:29,580
It can be ignored if you want. Okay, never call this method.
334
00:17:29,582 --> 00:17:32,717
If you ever call this method in the code you submit for
335
00:17:32,719 --> 00:17:35,286
homework, I'll be personally going over and
336
00:17:35,288 --> 00:17:36,754
slapping you on the wrist, okay?
337
00:17:36,756 --> 00:17:40,024
Because this, this is never, ever, under any circumstances,
338
00:17:40,026 --> 00:17:41,859
no exceptions are you calling this thing.
339
00:17:41,861 --> 00:17:46,030
The system calls drawRect. You never call it. So,
340
00:17:46,032 --> 00:17:49,767
how do you get your face to draw? If you want it drawn.
341
00:17:49,769 --> 00:17:52,837
Let's say the eye is closed. And you want to draw the face
342
00:17:52,839 --> 00:17:55,840
so the eyes are closed. Well you call this method here set
343
00:17:55,842 --> 00:17:59,010
needs display on your view. And that tells the system,
344
00:17:59,012 --> 00:18:01,412
hey this view needs to be redrawn. And
345
00:18:01,414 --> 00:18:04,782
the view at some point in the future when it's appropriate,
346
00:18:04,784 --> 00:18:08,986
will call your drawRect, okay? Now why is it done this way?
347
00:18:08,988 --> 00:18:10,688
Well if you think about it. Your view lives in
348
00:18:10,690 --> 00:18:12,690
an environment where there might be subviews and
349
00:18:12,692 --> 00:18:14,792
you're in SuperView and they're overlapping.
350
00:18:14,794 --> 00:18:16,461
There's all kinds of things happening. And
351
00:18:16,463 --> 00:18:19,831
you can't just run along and redraw one of those things
352
00:18:19,833 --> 00:18:21,332
okay they might be transparent,
353
00:18:21,334 --> 00:18:25,169
seeing through to each other, the system has to manage that
354
00:18:25,171 --> 00:18:27,772
okay and there's also performance reasons to do it.
355
00:18:27,774 --> 00:18:31,576
You might change, close the eyes, start frowning, etc,
356
00:18:31,578 --> 00:18:35,279
the nose starts running I don't know and all those
357
00:18:35,281 --> 00:18:35,880
things are happening all at once,
358
00:18:35,882 --> 00:18:38,683
we don't wanna redraw it every single time. The, one of them
359
00:18:38,685 --> 00:18:40,852
changes. You wanna wait until all of them have changed and
360
00:18:40,854 --> 00:18:44,922
then draw once your new face, okay? So the system is the one
361
00:18:44,924 --> 00:18:48,159
that decides. Now this is setNeedsDisplay() in Rect.
362
00:18:48,161 --> 00:18:50,495
Just lets you set this little rectangle right here.
363
00:18:50,497 --> 00:18:52,497
So this is an optimized version,
364
00:18:52,499 --> 00:18:54,532
an optimized version of setNeedsDisplay() in Rect.
365
00:18:54,534 --> 00:18:56,868
Maybe just call it with the i's. Okay,
366
00:18:56,870 --> 00:18:58,336
if you know that only the eyes have changed.
367
00:18:58,338 --> 00:19:01,939
But this is how you get a view to redraw, okay?
368
00:19:01,941 --> 00:19:04,976
All right, so how do I implement this drawRect thing,
369
00:19:04,978 --> 00:19:07,678
okay? I got this drawRect thing, how do I implement it?
370
00:19:07,680 --> 00:19:10,848
There's two ways, really. There is a, kind of a C-like,
371
00:19:10,850 --> 00:19:14,485
a non-object oriented. In Swift, it's just functions,
372
00:19:14,487 --> 00:19:17,855
global functions way of doing it called Core Graphics.
373
00:19:17,857 --> 00:19:21,692
Okay, Core Graphics and then there's object oriented way
374
00:19:21,694 --> 00:19:24,762
using a class called UIBezierPath, okay? They're
375
00:19:24,764 --> 00:19:28,466
both using Core Graphics underneath the covers so I'm
376
00:19:28,468 --> 00:19:31,936
gonna talk a little bit about the Core Graphics Concepts and
377
00:19:31,938 --> 00:19:35,239
they're going to apply to UIBezierPath as you can see.
378
00:19:35,241 --> 00:19:37,074
So what are the concepts of Core Graphics?
379
00:19:37,076 --> 00:19:39,644
Very simple. First, you're gonna get a context
380
00:19:39,646 --> 00:19:42,914
to draw in. So that context might be drawing on screen.
381
00:19:42,916 --> 00:19:44,849
That's the context you'd want if you're in drawRect.
382
00:19:44,851 --> 00:19:47,952
But you could have other contexts like drawing for
383
00:19:47,954 --> 00:19:51,522
a printer, drawing into an off screen buffer, okay?
384
00:19:51,524 --> 00:19:52,523
Those are all contexts of draw so
385
00:19:52,525 --> 00:19:56,394
that's the first thing you do. In if you're using this
386
00:19:56,396 --> 00:19:58,963
kind of like C-like function one you're gonna use this
387
00:19:58,965 --> 00:20:02,300
UI graphics to get current context inside of drawRect
388
00:20:02,302 --> 00:20:03,834
to get the context that's appropriate for
389
00:20:03,836 --> 00:20:08,573
drawRect, okay? So then number two is you create paths.
390
00:20:08,575 --> 00:20:12,076
So these paths are made of arcs and lines, rectangles,
391
00:20:12,078 --> 00:20:16,447
circles, whatever. You're pu- putting together these Paths,
392
00:20:16,449 --> 00:20:17,748
this path that you're building, okay?
393
00:20:17,750 --> 00:20:20,051
And that's the thing that's gonna be drawn, okay?
394
00:20:20,053 --> 00:20:23,888
So you build this path. Then you set attributes that you're
395
00:20:23,890 --> 00:20:28,159
gonna draw this path with like, the color of the lines.
396
00:20:28,161 --> 00:20:30,528
Any fonts if you're doing texts.
397
00:20:30,530 --> 00:20:32,797
Textures if you're filling things with textures.
398
00:20:32,799 --> 00:20:34,765
Linewidths, linecaps, those kinds of things.
399
00:20:34,767 --> 00:20:38,402
You set all those things up and then you stroke the path
400
00:20:38,404 --> 00:20:41,372
which means draw a line everywhere the path is or
401
00:20:41,374 --> 00:20:45,643
you fill the path which means fill in the space
402
00:20:45,645 --> 00:20:49,547
contained by the path with some color or texture, okay?
403
00:20:49,549 --> 00:20:52,450
That's it, this is the basic concept of how you draw.
404
00:20:52,452 --> 00:20:58,122
The UIBezierPath does the same okay it's
405
00:20:58,124 --> 00:21:04,395
just the UIBezierPath It has methods to draw.
406
00:21:04,397 --> 00:21:08,799
Line two, arch two, those kind of things instead of having
407
00:21:08,801 --> 00:21:11,636
these great functions. You know these global functions.
408
00:21:11,638 --> 00:21:15,439
You use UI color to set the stroke and fill colors.
409
00:21:15,441 --> 00:21:18,709
This is a class we'll talk about and then when it comes
410
00:21:18,711 --> 00:21:22,046
to stroking in fill, those are methods on UIBezierPath.
411
00:21:22,048 --> 00:21:23,748
So UIBezierPath just encapsulates
412
00:21:23,750 --> 00:21:25,783
all that core graphics staff in to a nice class.
413
00:21:25,785 --> 00:21:28,219
It's nice because you can find everything you want.
414
00:21:28,221 --> 00:21:30,821
Basically everything you want to do to draw except for
415
00:21:30,823 --> 00:21:34,225
colors, fonts, texts and images. Those four things
416
00:21:34,227 --> 00:21:38,329
are not in UIBezierPath but all other drawing is in there.
417
00:21:38,331 --> 00:21:41,832
Okay, so how do you do this? Lets, I'm only gonna talk
418
00:21:41,834 --> 00:21:44,368
about the UIBezierPathway cuz it's object oriented,
419
00:21:44,370 --> 00:21:46,971
we love object oriented programming here. So
420
00:21:46,973 --> 00:21:49,974
here's how you would, create a path and draw some.
421
00:21:49,976 --> 00:21:52,343
So we're gonna do a triangle. Okay, so first
422
00:21:52,345 --> 00:21:56,080
I create a UIBezierPath, okay theyre are one initializers
423
00:21:56,082 --> 00:21:58,049
with argueness we're gonna use the one we just created,
424
00:21:58,051 --> 00:22:00,885
a blank, blank path. Then, we're gonna move around.
425
00:22:00,887 --> 00:22:04,488
So, I'm gonna move to 8050, which is like right here.
426
00:22:04,490 --> 00:22:08,693
Let's say it's 160 wide and 250 high, or something.
427
00:22:08,695 --> 00:22:12,630
So, 8060s right about there. Then, I'm going to add a line
428
00:22:12,632 --> 00:22:15,566
to this point over here, 140, 150. Remember,
429
00:22:15,568 --> 00:22:19,403
origin is up here, positive numbers are going down, okay?
430
00:22:19,405 --> 00:22:22,506
Gonna add another line right here. Okay, to 10150, and
431
00:22:22,508 --> 00:22:26,877
then I'm going to close the path, there's a method called
432
00:22:26,879 --> 00:22:31,082
close path, and that just goes back to the starting point,
433
00:22:31,084 --> 00:22:31,749
so that closes it, and boom,
434
00:22:31,751 --> 00:22:36,087
I've created a triangle. Now I've misled you a little here,
435
00:22:36,089 --> 00:22:40,124
because no drawing on screen would actually happen if you
436
00:22:40,126 --> 00:22:41,359
put this in your draw rect. Okay,
437
00:22:41,361 --> 00:22:43,127
if you put these lines and this code in your draw rect,
438
00:22:43,129 --> 00:22:46,063
nothing would happen. Because all you've done here is
439
00:22:46,065 --> 00:22:49,367
create that path. You have not drawn it on screen.
440
00:22:49,369 --> 00:22:52,903
You haven't stroked it or filled it. So how do we do
441
00:22:52,905 --> 00:22:56,073
that? How do we get it onto screen? Well first of all,
442
00:22:56,075 --> 00:22:57,942
we need to set the colors that we're gonna use to
443
00:22:57,944 --> 00:23:03,047
stroke and fill. So I want my triangle to be red lines with
444
00:23:03,049 --> 00:23:06,283
green fill in the middle. So I'm setting green color
445
00:23:06,285 --> 00:23:08,919
as my fill color and red color as my stroke color.
446
00:23:08,921 --> 00:23:12,857
Notice that I send these as to a UIColor. I don't send them
447
00:23:12,859 --> 00:23:15,593
to the BezierPath. So it's kinda odd that you
448
00:23:15,595 --> 00:23:18,462
do your setting of the colors by sending it to UIColor.
449
00:23:18,464 --> 00:23:21,899
So we'll talk about UIColor green color, what that means
450
00:23:21,901 --> 00:23:24,902
in a couple slides. So I'm setting my stroke and fill and
451
00:23:24,904 --> 00:23:27,938
then here I'm saying the line width of my path to
452
00:23:27,940 --> 00:23:32,443
three points, three points wide, okay? Now I can just say
453
00:23:32,445 --> 00:23:37,615
path.fill and I get my filled path, see? Filled with green.
454
00:23:37,617 --> 00:23:41,385
Notice there's no line around the outside. But then if I say
455
00:23:41,387 --> 00:23:44,522
path stroke, now you get a line around the side. And
456
00:23:44,524 --> 00:23:49,360
you can see it's red. And it's line width is three, got it?
457
00:23:49,362 --> 00:23:51,929
So you're going to have code like this in your draw erect.
458
00:23:51,931 --> 00:23:56,300
If you wanted your view to be a view of a triangle.
459
00:23:56,302 --> 00:24:01,405
Okay, any questions about that? All right [COUGH].
460
00:24:01,407 --> 00:24:03,841
Now, you can use UI Bezier Path to draw more
461
00:24:03,843 --> 00:24:07,311
complicated things like rounded rects and circles and
462
00:24:07,313 --> 00:24:10,214
things like that, especially by using a lot of
463
00:24:10,216 --> 00:24:12,349
the initializers of UI Bezier Path, but
464
00:24:12,351 --> 00:24:15,853
there's also methods there. So you're going to have to study
465
00:24:15,855 --> 00:24:17,221
UI Bezier Path and see what it can do,
466
00:24:17,223 --> 00:24:20,691
okay, it can do quite a lot. Another interesting thing
467
00:24:20,693 --> 00:24:24,528
about UIBezierPath is you can set it as a clipping path. So,
468
00:24:24,530 --> 00:24:26,697
let's say you were drawing a playing card, right?
469
00:24:26,699 --> 00:24:29,533
Playing cards have rounded rects. They never have sharp
470
00:24:29,535 --> 00:24:31,769
corners, right? They have rounded rects.
471
00:24:31,771 --> 00:24:34,839
Well, you could Could create a UIBezierPath here using this,
472
00:24:34,841 --> 00:24:38,776
create a rounded rect, set it as the clipping path, and
473
00:24:38,778 --> 00:24:41,378
then just draw your card in the inside.
474
00:24:41,380 --> 00:24:43,681
And it would never draw in those corners,
475
00:24:43,683 --> 00:24:46,684
cuz it would be clipped to the rounded rect. Okay?
476
00:24:46,686 --> 00:24:50,421
So, addClip is kind of a cool Bezier path method.
477
00:24:50,423 --> 00:24:52,490
The Bezier path can also do hit detection. So
478
00:24:52,492 --> 00:24:56,227
you can see whether a point is like inside the triangle,
479
00:24:56,229 --> 00:24:58,863
right? If you have that Bezier path that's the triangle, you
480
00:24:58,865 --> 00:25:01,131
can do hit detection on it, whether it contains a point.
481
00:25:01,133 --> 00:25:03,667
So that's kinda cool, too, right, doing some game or
482
00:25:03,669 --> 00:25:06,504
something and people have to touch on a certain kind of
483
00:25:06,506 --> 00:25:08,806
funny shaped thing, not a rectangular thing.
484
00:25:08,808 --> 00:25:11,742
Then it's nice to be able to do hit detection with it.
485
00:25:11,744 --> 00:25:13,477
Okay? And there's lots and lots of other stuff,
486
00:25:13,479 --> 00:25:16,180
you need to check the documentation. All right,
487
00:25:16,182 --> 00:25:19,083
UIColor. We saw that UIColor, green color thing,
488
00:25:19,085 --> 00:25:23,120
on the previous slide. This, what kind of method is this?
489
00:25:23,122 --> 00:25:25,523
Do you guys remember from last lecture? The green color.
490
00:25:25,525 --> 00:25:28,025
What kind of method? Type method or
491
00:25:28,027 --> 00:25:31,128
instance method? It's a type method.
492
00:25:31,130 --> 00:25:33,497
Right because we're not sending it to a UI color,
493
00:25:33,499 --> 00:25:35,366
we're sending it to the type, UIColor.
494
00:25:35,368 --> 00:25:38,903
UIColor's a class and so we're sending this method to it.
495
00:25:38,905 --> 00:25:41,739
So it would be defined as static or
496
00:25:41,741 --> 00:25:45,976
class, funk, green color inside UIColor class.
497
00:25:45,978 --> 00:25:51,015
Okay? By the way, you can also create colors, not
498
00:25:51,017 --> 00:25:54,018
just picking these well-known colors like green color. But
499
00:25:54,020 --> 00:25:58,155
there are initializers for RGB, red, green, blue. HSB,
500
00:25:58,157 --> 00:26:00,891
that's hue, saturation, and br-brightness. You can even
代表色相、饱和度和亮度。你甚至可以
501
00:26:00,893 --> 00:26:05,095
set, create a color that's a pattern. Okay? It's like some
设定并创建一个颜色当做模板。了解?这就像
502
00:26:05,097 --> 00:26:07,097
image that you have. You'd make a pattern out of it.
你有一些图片,你能通过它创建一个模板
503
00:26:07,099 --> 00:26:09,233
And when it draws, it will be drawing with that pattern.
并且当它开始画的时候,会以这个模板来画
504
00:26:09,235 --> 00:26:14,204
It's kind of cool. Here we see views have background colors.
这点挺酷的。这里我们看到 view 有背景颜色
505
00:26:14,206 --> 00:26:16,574
So you've already seen that. We set the background colors
你已经看到了。我们在 calculator 里
506
00:26:16,576 --> 00:26:19,677
of our buttons in the calculator. You noticed that
设置里按钮的背景颜色。我们在
507
00:26:19,679 --> 00:26:21,145
when we did that in the attributes inspector,
属性检查器里处理的时候你已经看到了
508
00:26:21,147 --> 00:26:23,581
we had to scroll down to the bottom because the attribute
我们需要滑到底下,因为
509
00:26:23,583 --> 00:26:26,050
inspector is object oriented and it was showing all
属性检查器是面向对象的并且它会显示所有
510
00:26:26,052 --> 00:26:28,886
the inherited thing. Well we had to go down to The UIView
继承的东西。我们需要到 UIView
511
00:26:28,888 --> 00:26:31,188
level to set the background color of our buttons, okay,
这一层去设置我们 button 的背景颜色。
512
00:26:31,190 --> 00:26:36,594
cuz button inherits that from UIView. Okay? Interesting
因为这是 button 从 UIView 那里继承来的。没问题?
513
00:26:36,596 --> 00:26:40,130
thing about colors, they can have transparency.
关于颜色有趣的是,它们有透明度。
514
00:26:40,132 --> 00:26:42,833
Okay, meaning they kind of show through. That's called
好的,这意味着他们能够显示背后的东西,这个属性
515
00:26:42,835 --> 00:26:45,436
alpha. How many people have heard the term alpha when it
叫做 alpha,有多少人在碰到绘图的时候
516
00:26:45,438 --> 00:26:48,639
comes to drawing? Okay, so less than half of you.
听到过 alpha 这个术语?好吧,不到一半
517
00:26:48,641 --> 00:26:51,208
So alpha just means how transparent it is.
所以 alpha 表示的就是它有多透明
518
00:26:51,210 --> 00:26:53,544
An alpha of zero means fully transparent.
alpha 值为0表示完全透明
519
00:26:53,546 --> 00:26:56,380
So if you had bright red but it was alpha zero
所以如果你有个亮红色,但是 alpha 为0的话
520
00:26:56,382 --> 00:26:58,582
you wouldn't see anything because its fully transparent.
你什么也看不到,以为它是完全透明的
521
00:26:58,584 --> 00:27:01,352
You're seeing completely through it with no blocking.
你会看到它后面所有的东西,没有任何阻碍
522
00:27:01,354 --> 00:27:04,355
Alpha of 1 means fully opaque, meaning you would not
Alpha 值为1表示完全不透明,意味着
523
00:27:04,357 --> 00:27:07,191
see a single pixel of anything behind something that was
当一个东西完全不透明的时候,你看不到它后面的
524
00:27:07,193 --> 00:27:10,728
fully opaque. And between 0 and 1 is various degrees
任何东西。值在0和1之间就是不同层次
525
00:27:10,730 --> 00:27:14,465
of being able to see through. Okay, that's called alpha. And
的透明程度。好的,这就叫做 alpha
526
00:27:14,467 --> 00:27:16,667
you can create colors, like here's a yellow.
你能够创建颜色,像这里有个黄色
527
00:27:16,669 --> 00:27:20,337
By sending this message colorWithAlphaComponent, okay,
通过使用这个方法 colorWithAlphaComponent,请问
528
00:27:20,339 --> 00:27:21,639
is this a type method or
这是个类方法还是
529
00:27:21,641 --> 00:27:26,243
an instance method? Raise your hand if you think it's a type
个实例方法?认为是类方法的请
530
00:27:26,245 --> 00:27:31,048
method. Raise your hand if you think it's an instance method.
举手。认为是实例方法的请举手
531
00:27:31,050 --> 00:27:32,583
Hardly anyone raised their hand for either of them.
看来两边都没什么人举手啊
532
00:27:32,585 --> 00:27:34,518
That's great. But if you were to raise your hand for
没问题。但是如果你举手并觉得是
533
00:27:34,520 --> 00:27:36,186
instance method you are correct. Okay.
实例方法的话,你对了。好的
534
00:27:36,188 --> 00:27:38,989
This is an instance method. That's because we use this
这时一个实例方法。那是因为我们用这个
535
00:27:38,991 --> 00:27:42,760
type method yellow color to get an instance of a color and
yellowColor() 方法去获取一个颜色的实例
536
00:27:42,762 --> 00:27:45,396
then we sent it this instance method
然后我们在后面写这个实例方法
537
00:27:45,398 --> 00:27:49,199
to get a half transparent version of yellow. So
去获取这个黄色的半透明的版本。所以说
538
00:27:49,201 --> 00:27:51,568
this is half transparent yellow right here, and
这里是一个半透明的黄色,并且
539
00:27:51,570 --> 00:27:54,872
if we drew with it it would draw yellow in our view, but
如果我们用它来画的话,它会在 view 中画出黄色,但是
540
00:27:54,874 --> 00:27:57,274
we would be able to see through, to things behind,
我们能够看到它后面,它后面的那些东西
541
00:27:57,276 --> 00:28:01,845
perhaps even to other views, like our super view, behind.
或者甚至看到其他的视图,比如后面的父视图
542
00:28:02,581 --> 00:28:05,282
If you want to draw with transparency in your view.
如果你想在 view 里画有透明度的图
543
00:28:05,284 --> 00:28:07,651
If you want to draw with colors that are transparent.
如果你想使用有透明度的颜色
544
00:28:07,653 --> 00:28:13,424
You have to set in your view this var opaque to be false.
你需要在你的 view 里面设置这个 opaque 的变量为 false
545
00:28:13,426 --> 00:28:17,061
As a performance optimization the system has to assume
为了最优化显示效果,系统必须假定
546
00:28:17,063 --> 00:28:18,429
the view is opaque okay?
view 是不透明的,明白?
547
00:28:18,431 --> 00:28:21,065
That it doesn't draw with transparency. That's because
所以它画的时候不会带透明度。那是因为
548
00:28:21,067 --> 00:28:23,667
you can imagine things that are transparent overlapping,
你们想一下当各种有透明度的东西重叠在一起的时候
549
00:28:23,669 --> 00:28:26,904
think about the work that the system has to do to figure out
想象一下系统需要去处理在后面显示的
550
00:28:26,906 --> 00:28:28,906
what's showing through from behind.
那些东西的时候需要的工作量
551
00:28:28,908 --> 00:28:31,008
Okay? That kind of compositing,
懂得?那是件有点庞杂的事
552
00:28:31,010 --> 00:28:33,877
it's called, takes processor power and memory.
这叫做提高处理效率,节约内存
553
00:28:33,879 --> 00:28:37,414
Okay? So, it's gonna assume that everything is opaque. So
懂了吗?所以说系统会假定所有东西都是不透明的
554
00:28:37,416 --> 00:28:40,050
it doesn't have to do that, but if you are drawing with
虽然没有必要,但是你在画有透明度的东西
555
00:28:40,052 --> 00:28:42,953
transparency, that's fine, just set this opaque to false,
的时候,好吧,把 opaque 设成 false 就是了
556
00:28:42,955 --> 00:28:46,356
to let the system know, no I'm not opaque. I'm gonna draw
让系统知道,现在不是不透明的,我要画
557
00:28:46,358 --> 00:28:49,293
with transparency. Now another thing that's cool,
有透明度的图了。然后另外一件挺酷的事是
558
00:28:49,295 --> 00:28:52,529
is you can make your entire view transparent,
你能让你的整个 view 带有透明度
559
00:28:52,531 --> 00:28:56,533
by setting the alpha property on the view itself, okay? So
通过在 view 自身上设定 alpha 属性,明白?所以说
560
00:28:56,535 --> 00:28:59,002
you could draw your whole view, your triangle,
你能画出你的整个视图,三角形
561
00:28:59,004 --> 00:29:01,038
let's say, set the transparency to 0.5, and
然后,来吧,把透明度设为0.5,然后
562
00:29:01,040 --> 00:29:02,906
you'd have a half transparent triangle,
你就会有一个半透明的三角形
563
00:29:02,908 --> 00:29:04,842
even though you drew all with opaque colors,
即使你全部画的不透明的颜色
564
00:29:04,844 --> 00:29:07,711
you'd still have a half transparent triangle because
你仍然会有一个半透明的三角形,因为
565
00:29:07,713 --> 00:29:11,381
you set your alpha to 0.5. Okay? Using alpha is
你把 alpha 设成了0.5。懂了?当我们
566
00:29:11,383 --> 00:29:15,686
really cool for doing effects like fading views out. Okay,
需要例如把视图渐隐的效果时,用 alpha 挺不错的。好的
567
00:29:15,688 --> 00:29:18,422
sometimes you want a view just to fade out over time,
有时候,你想让一个 view 在一段时间内渐隐
568
00:29:18,424 --> 00:29:19,790
you want to animate that,
你想要做它的一个动画
569
00:29:19,792 --> 00:29:21,158
alpha is one of the things you can animate,
alpha 是你能用来做动画的东西之一
570
00:29:21,160 --> 00:29:23,560
we'll talk about animate in a couple of weeks, so
我们将在几周之后谈到动画。所以
571
00:29:23,562 --> 00:29:27,131
alpha is cool for doing that. All right,
alpha 用来做这事儿挺不错的。好的
572
00:29:27,133 --> 00:29:29,633
now let's talk about this view transparency and
现在我们来讨论一下这个视图透明度以及
573
00:29:29,635 --> 00:29:31,835
how that works when you're drawing with transparency.
当你用透明度绘图的时候,它的工作原理
574
00:29:31,837 --> 00:29:34,938
If you have non-opaque views, okay, say they're drawn with
如果你没有不透明的视图,也就是说是带有
575
00:29:34,940 --> 00:29:38,375
transparency, then what happens is that sub views list
透明度的。然后就是,当子视图列表的第一个东西
576
00:29:38,377 --> 00:29:41,311
that shows all of the sub views is in order
显示在后面的时候,显示所有子视图的那个子视图列表
577
00:29:41,313 --> 00:29:45,149
where the first thing in the sub views list is in the back
依然是井井有条的。
578
00:29:45,284 --> 00:29:46,550
okay. The next thing is in front of that,
下一个东西在它前面,
579
00:29:46,552 --> 00:29:48,285
and the next one's in front of that, the next one's in front
下一个又在这一个前面,下一个又在前面
580
00:29:48,287 --> 00:29:52,189
of that and so it's gonna show through like that, get it? So
所以,这就是它透视的原理。懂了吗?
581
00:29:52,191 --> 00:29:53,690
the sub views list matters okay.
子视图列表是很重要的,明白?
582
00:29:53,692 --> 00:29:57,928
The sub views list is built by add subview, everything is
子视图列表是通过添加子视图建立的。当你调用
583
00:29:57,930 --> 00:30:00,330
always put in the front when you call add subview. But,
addSubview() 方法时,东西都是放在前面的。但是
584
00:30:00,332 --> 00:30:03,767
there's another method called insert subview at, that let you
有另一个方法叫insertSubview at,让你
585
00:30:03,769 --> 00:30:06,370
put it anywhere you want. You put a subview in the back.
能再任何位置添加子视图。你在后面添加一个子视图
586
00:30:06,372 --> 00:30:08,038
Like if you say, insert subview at zero,
假设如果说,在索引值为0处插入子视图
587
00:30:08,040 --> 00:30:11,675
it will put it up at the back. Okay. So, just note that,
它就会在后面添加这个子视图。好的,这里记住
588
00:30:11,677 --> 00:30:16,180
that subviews orders obviously matters. You can
子视图的秩序真的很重要。通过
589
00:30:16,182 --> 00:30:20,484
completely hide a view by setting it's hidden to true.
设置一个 view 的 hidden 为 true,你能完全隐藏它
590
00:30:20,486 --> 00:30:24,087
If you set it's hidden to true, it will not receive any
如果你把它的 hidden 值设为 true 了,它不会接受任何的
591
00:30:24,089 --> 00:30:27,257
events, no touch events. Nor will it draw what it draws.
事件,没有触摸事件。也不会画出它的图像
592
00:30:27,259 --> 00:30:29,560
It will still be in view hierarchy, okay, but
它仍然会在这一层视图,但是
593
00:30:29,562 --> 00:30:32,129
it's as if it's not there. Now why would you want this? Well
看起来就像它没在那儿一样。好的,你什么时候会想要这样?
594
00:30:32,131 --> 00:30:34,431
you might have a view that only appears when a certain
比如,或许你有一个视图,只在一个按钮
595
00:30:34,433 --> 00:30:37,568
button is in a certain state. So you put it there.
处在确定的状态时才显示。所以你把它放在在那儿
596
00:30:37,570 --> 00:30:39,903
You set it hidden. When the button goes to that state you
设成隐藏。当那个按钮到那个状态的时候你
597
00:30:39,905 --> 00:30:44,341
unhide it and it'll appear. Now to be frank you probably
取消隐藏,然后它显示出来。实际上呢,你可能
598
00:30:44,343 --> 00:30:46,944
animate it's appearance with alpha okay so
会使用动画,设置透明度,把它渐渐显示出来。所以
599
00:30:46,946 --> 00:30:48,111
it would zooooo appear like that.
看起来就像 zoooo 的一下出来了。
600
00:30:48,113 --> 00:30:51,648
Instead of jumping on screen that's kind of disconsidering.
而不是一下子跳到屏幕上,那看起来有点欠考虑。
601
00:30:51,650 --> 00:30:53,784
But you still would probably use hidden to have it hidden
但是你最开始的时候可能要用 hidden 去
602
00:30:53,786 --> 00:30:55,319
in the first place. Because you wouldn't want it for
把它隐藏掉。因为,举个例子,在它完全显示之前
603
00:30:55,321 --> 00:30:57,688
example getting a touch of answer on like that until
你不会想让它对任何点击
604
00:30:57,690 --> 00:31:02,359
it was shown. What about drawing text.
做出回应。那么画文字呢?
605
00:31:02,361 --> 00:31:07,164
You do not draw text using a UiBezier path. Text is
我们不用 UIBezierPath 却画文字。文字算是
606
00:31:07,166 --> 00:31:12,069
really just a bunch of Bezier paths in a font. A font really
在一种字体中的一大堆的贝塞尔线条。一种字体实际上
607
00:31:12,071 --> 00:31:14,905
defines a bunch of Bezier paths for each character,
为每一个字符、每一条曲线都定义了一堆的
608
00:31:14,907 --> 00:31:19,943
each gliff. It's called. So we have higher level ways of
贝塞尔线条。这是它的原理。所以说我们有更高级的方法去
609
00:31:19,945 --> 00:31:22,880
drawing text by basically saying here's a string,
画文字。基本来说就是,来,这里有个字符串
610
00:31:22,882 --> 00:31:25,883
draw it. Okay that's a high level way to get all those
画出来。这是一种更高级的方法去把这些
611
00:31:25,885 --> 00:31:28,452
bezier paths drawn, you don't want to be dealing with that
线条画出来,你不会想要在 UIBezierPath 这一层
612
00:31:28,454 --> 00:31:31,588
at the UI bezier path level for sure. But most the time,
去画文字的,相信我。但是大多数时候
613
00:31:31,590 --> 00:31:35,325
we don't draw text even in our drawRect. We draw it by using
我们甚至不用 drawRect 去画文字。我们通过使用
614
00:31:35,327 --> 00:31:37,728
UILabel which you're used to all ready, okay?
你已经完全熟悉的 UILabel 去画它。了解?
615
00:31:37,730 --> 00:31:39,997
We just have a rectangle? Boom, we put it on here.
我们有一个矩形。当 ~ 我们把它放在这儿
616
00:31:39,999 --> 00:31:42,432
If we wanted to say hello on this guy's forehead,
如果我们想在这个家伙额头上画个 "hello"
617
00:31:42,434 --> 00:31:45,969
we might put a UILabel right there say hello. Okay, but
我们会在那儿放个 UILabel 然后写上 hello。好的,但是
618
00:31:45,971 --> 00:31:48,305
what if we did want in our draw rect.
假如我们想让它在 drawRect 里面
619
00:31:48,307 --> 00:31:51,241
What if we wanted to put some text in here and
假如我们想在这里放点文本
620
00:31:51,243 --> 00:31:53,343
we didn't want to put it as a subview,
但是我们不想把它弄成 UILabel
621
00:31:53,345 --> 00:31:57,214
a UILabel subview on our face view. And the way you do that
这样一个子视图,然后放在我们的 faceView 上。做这个的方法
622
00:31:57,216 --> 00:32:00,584
is with a class called NS attributed string Okay,
是用一个类,叫做 NSAtrributedString,明白?
623
00:32:00,586 --> 00:32:03,587
NSAttributedString. And you create it by
NSAttributedString。并且你要通过这里
624
00:32:03,589 --> 00:32:07,658
this constructor here. Usually you pass a string in here.
这个构造器来创建它。通常来说,你在这里传一个字符串
625
00:32:07,660 --> 00:32:11,929
What an attributed string is, it's a string that for every
一个特征字符串是什么呢?它是一个字符串,它的每一个
626
00:32:11,931 --> 00:32:15,832
character has a dictionary of attributes that say how to
字符,在字典中都有一个对应的属性,来说明怎么去
627
00:32:15,834 --> 00:32:19,903
draw it. And those attributes are things like, the color of
画它。这些属性就像字符或者曲线的
628
00:32:19,905 --> 00:32:24,241
the character or of the glyph, the font to use obviously,
颜色、使用的字体
629
00:32:24,243 --> 00:32:25,409
maybe a background color,
或者背景颜色
630
00:32:25,411 --> 00:32:28,545
those kind of things, right? You have a dictionary that has
像这些东西。明白?你有一个为每一个字符
631
00:32:28,547 --> 00:32:30,714
all those kinds of settings for every single character.
设置了这些东西的字典
632
00:32:30,716 --> 00:32:33,417
Now often you have long runs of characters that have
通常你有一长串有着同样的
633
00:32:33,419 --> 00:32:35,752
exactly the same color and font or whatever, so
颜色和字体的字符。不论如何
634
00:32:35,754 --> 00:32:38,789
you would have one dictionary for all of them. But
你会有一个包含了这些所有东西的字典
635
00:32:38,791 --> 00:32:41,725
that is the basic way that AttributedString works. And
这就是基本的 AttributedString 的工作原理。而且
636
00:32:41,727 --> 00:32:44,761
once you have an attributed string, okay, maybe you've set
一旦你有一个 attributed string。或许你
637
00:32:44,763 --> 00:32:47,431
these dictionaries on the characters, then you draw it
这些字符上设置了一些字典,然后你使用 text
638
00:32:47,433 --> 00:32:50,334
in your drawRect by saying text, that's the attributed
在你的 drawRect 里面画它,这就是 attributed string
639
00:32:50,336 --> 00:32:53,737
string. You send the method here, drawAtPoint,
在这儿你调用 drawAtPoint
640
00:32:53,739 --> 00:32:57,674
and then you give it a point. And it's going to draw that
然后你给它一个点,它就会以那个点
641
00:32:57,676 --> 00:33:00,811
text with the upper left corner being at that point.
为左上角的坐标把文字画出来
642
00:33:00,813 --> 00:33:03,480
So, if I wanted to do that hello and I would have drew
所以,如果我想画那个 hello 而且我想
643
00:33:03,482 --> 00:33:07,351
it right here. The hello would appear like this. Okay,
就在这里画。那个 hello 就会像这样
644
00:33:07,353 --> 00:33:09,419
this is the upper left and it would appear,
这里是左上角,它就会
645
00:33:09,421 --> 00:33:13,256
the text would appear here. Okay, got that? And I could
这段文字就会在这显示。懂了?而且我能
646
00:33:13,258 --> 00:33:18,228
find out how big this is gonna be by using this size method,
知道这个的大小是多少,用这个 size 方法
647
00:33:18,230 --> 00:33:22,833
okay, the size property, actually of attributed string,
这个 size 属性,纠正下,这个特征字符串的
648
00:33:22,835 --> 00:33:26,837
okay? So that's how we draw a text. Now this is a little
好了吗?所以这就是我们画文字的方法。这是个有点
649
00:33:26,839 --> 00:33:29,673
bit of a nasty class to use, to be honest,
用起来不太好的类,说实话,这个
650
00:33:29,675 --> 00:33:33,176
NSAttributedString, for a couple of reasons. But
NSAttributedString,因为各种原因
651
00:33:33,178 --> 00:33:36,480
all the reasons come from the fact this is an Objective-C
所有的这些原因都是因为这是一个Objective-C
652
00:33:36,482 --> 00:33:40,517
class that really didn't get a very good Swift treatment yet,
的类,目前对 Swift 的适配度并不太好
653
00:33:40,519 --> 00:33:43,820
okay? So there's two major differences
明白吗?所以这里有两个原因,让这个
654
00:33:43,822 --> 00:33:47,157
that make using it a kind of complicated in Swift,
东西在 Swift 里有点复杂
655
00:33:47,159 --> 00:33:52,129
okay? One is that mutability is not done with var and
一个是它的可变性不是用 var 和 let
656
00:33:52,131 --> 00:33:55,165
let. Okay, normally if you wanted an array that you can
来控制的。通常如果你想要一个数组
657
00:33:55,167 --> 00:33:58,535
add things to do, you would just say var x array, and
能给它加东西的话,你直接会说 var x array。然后
658
00:33:58,537 --> 00:34:00,337
since it's a var you can add to it. Okay,
这是个 var,你能给它加东西。
659
00:34:00,339 --> 00:34:03,974
if you said let x of an array, then you couldn't add to it.
如果你说这个 x 是设为 let 的数组,你就不能加东西
660
00:34:03,976 --> 00:34:05,976
Well that's not true for NSAttributedString.
但是这在 NSAttributedString 里不适用
661
00:34:05,978 --> 00:34:08,078
You can't say var NSAttributedString,
你不能把 NSAttributedString 设成 var
662
00:34:08,080 --> 00:34:11,448
that's still immutable. The way you do mutability is with
它仍然是不可变的。你要使用可变性就要
663
00:34:11,450 --> 00:34:15,185
NSMutableAttributedString, it's a different class, okay?
用到 NSMutableAttributedString,这是另一个类,懂?
664
00:34:15,187 --> 00:34:17,687
So if you wanna built an attributed string and
所以如果你想建一个特征数组而且
665
00:34:17,689 --> 00:34:20,824
start setting the attributes on it, you need to use
要给它设置属性的话,你需要用到
666
00:34:20,826 --> 00:34:23,293
MutableAttributedString instead, okay?
MutableAttributedString,懂?
667
00:34:23,295 --> 00:34:25,362
You create it the same way with some string but
你用了同样的创造字符串的方法,但是
668
00:34:25,364 --> 00:34:27,898
now you can set the attributes of it or whatever. So
你能看到它的属性等等。所以
669
00:34:27,900 --> 00:34:30,634
that's one thing that's kinda weird, okay? Is this mutable,
这是里面有点怪的一件事。要用到可变性
670
00:34:30,636 --> 00:34:33,737
you have to use this mutable version of it.
你就需要设置它使用 mutable 的版本
671
00:34:33,739 --> 00:34:37,307
By the way, NSAttributedString is not a string, a Swift
顺便,NSAttributedString 不是 Swift 的
672
00:34:37,309 --> 00:34:40,811
string, nor is it an NSString even an object string. It's
字符串,也不是 NSString,甚至连个对象都不算
673
00:34:40,813 --> 00:34:44,581
its own thing. It does have a property string and another
它就是它自己。它有一个 string 的属性和一个
674
00:34:44,583 --> 00:34:47,317
property called mutableString that will give you back
mutableString 的属性,能在你想
675
00:34:47,319 --> 00:34:52,389
a string/NSString using that bridging thing but
桥接这个东西的时候返回一个 string/NSString,但是
676
00:34:52,391 --> 00:34:55,625
it itself is not a string. You can't pass it as an argument
它本身不是一个字符串。你不能把它当参数传递
677
00:34:55,627 --> 00:34:57,461
to something that's accepting a string, okay?
给某个接收一个字符串的东西,明白?
678
00:34:57,463 --> 00:35:00,397
NSAttributedString is a different thing. Now,
NSAttributedString 是一个不一样的东西
679
00:35:00,399 --> 00:35:02,933
the second thing that's difficult with Swift and
第二事,Swift 处理起来比较困难的
680
00:35:02,935 --> 00:35:07,337
attributed string is the range here, okay?
attributedString 是一个 range
681
00:35:07,339 --> 00:35:08,505
You're trying to set attributes.
当你想设定一些属性的时候
682
00:35:08,507 --> 00:35:10,774
This is how you set attributes in the thing.
这就是你能设定属性的方法
683
00:35:10,776 --> 00:35:11,007
Here's the dictionary.
这时这个字典的问题
684
00:35:11,009 --> 00:35:13,443
I'm gonna show you what's in that dictionary in a second.
我一会儿会说这个字典里的东西
685
00:35:13,445 --> 00:35:16,113
And you're trying to set it for a range of characters in
你要尝试把它理解为一个字符串里的
686
00:35:16,115 --> 00:35:19,316
the string. You want the font or a color to be something for
一段区间的字符,你想让一个颜色或者字体
687
00:35:19,318 --> 00:35:22,686
a range of characters. Well, this range is not a Range,
设成一段字符。实际上这个区段不是个 Range 类型
688
00:35:22,688 --> 00:35:27,157
it's an NSRange. Okay, NSRange is different than Range.
它是个 NSRange,NSRange 和 Range 不一样
689
00:35:27,159 --> 00:35:29,993
And NSRange, the index is in here.
NSRange 呢,这里的这个 index
690
00:35:29,995 --> 00:35:35,265
Okay, it's a struct also that has a start in and a range,
同样,它是个 struct
691
00:35:35,267 --> 00:35:38,902
basically. A distance, start in a distance. But
692
00:35:38,904 --> 00:35:43,240
these indexes are into the Objective-C NSString. Now,
693
00:35:43,242 --> 00:35:45,542
what's the difference between a Objective-C NSString and
694
00:35:45,544 --> 00:35:47,544
a Swift string? Well, as I told you before,
695
00:35:47,546 --> 00:35:50,914
a Swift string is fully Unicode compliant, okay? So,
696
00:35:50,916 --> 00:35:53,550
things like emojis and stuff like that might be multiple
697
00:35:53,552 --> 00:35:57,654
characters. So the length of an NSString might be different
698
00:35:57,656 --> 00:35:58,989
then the length of a Swift string,
699
00:35:58,991 --> 00:36:03,860
okay? Swift string has a lot of possible Unicodes in there,
700
00:36:03,862 --> 00:36:07,264
whereas an NSString might not necessary do the full Unicode
701
00:36:07,266 --> 00:36:09,399
treatment. So, bottom line, they're different.
702
00:36:09,401 --> 00:36:12,302
Now, if you have just normal text which I hesitate to even
703
00:36:12,304 --> 00:36:15,972
say that because of course you want to build apps that work
704
00:36:15,974 --> 00:36:16,106
But if you have text where there's no Unicodes in there
705
00:36:16,108 --> 00:36:19,609
worldwide.
706
00:36:19,611 --> 00:36:21,711
that are multiple characters long.
707
00:36:21,713 --> 00:36:23,480
You don't have emojis in there and stuff.
708
00:36:23,482 --> 00:36:25,982
Then they're gonna be quite similar, these ranges, but
709
00:36:25,984 --> 00:36:28,618
otherwise, you might have to be really careful to make sure
710
00:36:28,620 --> 00:36:31,855
that you get the range to be the NSString range.
711
00:36:31,857 --> 00:36:34,791
That might mean taking the Swift string that you have,
712
00:36:34,793 --> 00:36:35,725
converting it to an NSString,
713
00:36:35,727 --> 00:36:38,195
which is really easy cuz it's bridged automatically.
714
00:36:38,197 --> 00:36:41,264
And then, looking into the NSString to see how long it is
715
00:36:41,266 --> 00:36:44,267
and where things are. Okay, I apologize for this.
716
00:36:44,269 --> 00:36:45,769
Of course, I have never worked for Apple, so
717
00:36:45,771 --> 00:36:48,905
I have nothing to do with it. But, this is the way it is.
718
00:36:48,907 --> 00:36:52,576
Hopefully, one day soon, Apple will come up with some
719
00:36:52,578 --> 00:36:55,712
nice more Swifty way of doing this. What is in this,
720
00:36:55,714 --> 00:36:58,348
by the way I'm not gonna ask you to do this very much in
721
00:36:58,350 --> 00:37:01,251
your homework. Okay, so don't worry too much about that.
722
00:37:01,253 --> 00:37:04,421
What are the attributes you could put in this dictionary?
723
00:37:04,423 --> 00:37:05,789
Exactly what you would expect.
724
00:37:05,791 --> 00:37:09,192
Okay, the foreground color is the color of the text.
725
00:37:09,194 --> 00:37:10,994
The stroke width is how thick you're gonna
726
00:37:10,996 --> 00:37:14,197
stroke the text. The FontAttributeName is the font,
727
00:37:14,199 --> 00:37:17,634
the UIFont. There's a class called UIFont, okay?
728
00:37:17,636 --> 00:37:20,170
You put those in a dictionary and then you can set that for
729
00:37:20,172 --> 00:37:23,840
whichever characters you want in your attributed string.
730
00:37:23,842 --> 00:37:27,377
Now let's talk about fonts, by the way. Fonts are super
731
00:37:27,379 --> 00:37:31,047
important in iOS, okay? They are critical to the look and
732
00:37:31,049 --> 00:37:34,017
feel of it. You can see there's all kinds of different
733
00:37:34,019 --> 00:37:39,389
fonts here, right? You see some bold, some not, some kind
734
00:37:39,391 --> 00:37:43,026
of heading fonts, there's system fonts like up here.
735
00:37:43,028 --> 00:37:46,630
Look at the use of fonts here, how important that is, okay?
736
00:37:46,632 --> 00:37:47,797
So fonts are very important and
737
00:37:47,799 --> 00:37:48,765
if you're gonna build a good app,
738
00:37:48,767 --> 00:37:50,767
you really gotta pay attention to the fonts.
739
00:37:50,769 --> 00:37:54,237
So how do I get the fonts or pick the fonts that I want?
740
00:37:54,239 --> 00:37:56,339
Well there's really three ways to do pick it,
741
00:37:56,341 --> 00:38:00,510
okay? Number one way is using prefered fonts. So,
742
00:38:00,512 --> 00:38:03,013
preferred fonts are the fonts you're gonna use for
743
00:38:03,015 --> 00:38:06,249
the user's content, okay? The user, the stuff the user is
744
00:38:06,251 --> 00:38:08,652
actually, there, like if it's a calendar app,
745
00:38:08,654 --> 00:38:11,288
then it's all of the things about their appointments and
746
00:38:11,290 --> 00:38:13,423
things, okay? If it's the weather app,
747
00:38:13,425 --> 00:38:15,425
it's the temperature and things like that.
748
00:38:15,427 --> 00:38:19,262
Those are part of the user's content. You get these
749
00:38:19,264 --> 00:38:23,600
mostly in storyboard by going to attributes inspector and
750
00:38:23,602 --> 00:38:26,703
you pick the font. And we've only picked the system font
751
00:38:26,705 --> 00:38:29,272
because we've always just had buttons, which is a system
752
00:38:29,274 --> 00:38:32,575
font thing. But for content, you would pick in there
753
00:38:32,577 --> 00:38:35,312
prefered font. Okay, and I'm gonna talk about what those
754
00:38:35,314 --> 00:38:38,048
referred font styles are in a second. In code,
755
00:38:38,050 --> 00:38:41,785
your gonna use this UIFont static type function called
756
00:38:41,787 --> 00:38:44,654
preferredFontForTextStyle and say what kind of text
757
00:38:44,656 --> 00:38:47,891
style you want and it's gonna give you UIFont, okay?
758
00:38:47,893 --> 00:38:50,994
So what are these text styles? Okay, there's about eight or
759
00:38:50,996 --> 00:38:54,297
nine of them, but some examples are headlines, body
760
00:38:54,299 --> 00:38:57,600
font, footnote font, and you should use the one that's kind
761
00:38:57,602 --> 00:39:01,871
of appropriate to what your UI's doing at that time, okay?
762
00:39:01,873 --> 00:39:04,307
There's like caption font, a couple of other things,
763
00:39:04,309 --> 00:39:08,011
all right? And so, you know, sometimes you play with these.
764
00:39:08,013 --> 00:39:09,379
The difference between a caption and
765
00:39:09,381 --> 00:39:12,549
a footnote can sometimes be subtle so
766
00:39:12,551 --> 00:39:12,882
you might try both and
767
00:39:12,884 --> 00:39:15,518
see which one really feels the best. But, you're gonna use
768
00:39:15,520 --> 00:39:18,021
these various preferred fonts to build a cool looking
769
00:39:18,023 --> 00:39:21,124
UI, okay, that's beautiful and all that.
770
00:39:21,126 --> 00:39:24,227
The second kind of font thing is systems font, okay?
771
00:39:24,229 --> 00:39:26,996
systemFontOfSize, boldSystemFontOfSize.
772
00:39:26,998 --> 00:39:30,200
System font's what we'll been using in our calculator,
773
00:39:30,202 --> 00:39:33,937
those go on system elements like buttons, okay,
774
00:39:33,939 --> 00:39:35,972
that's where you would use a system font.
775
00:39:35,974 --> 00:39:37,407
In our display in the calculator,
776
00:39:37,409 --> 00:39:39,542
we probably shouldn't have used a system font there
777
00:39:39,544 --> 00:39:41,811
because really what's in the display is kinda the user's
778
00:39:41,813 --> 00:39:44,848
content. Probably we should've used a prefered font but
779
00:39:44,850 --> 00:39:48,051
I hadn't shown you the slide yet so can't introduce it all
780
00:39:48,053 --> 00:39:51,988
at once, okay? But that probably would be better.
781
00:39:51,990 --> 00:39:55,191
And the kind of font we want to use there might be instead
782
00:39:55,193 --> 00:39:58,361
of a, sometimes you want to use a specific font, okay,
783
00:39:58,363 --> 00:40:01,664
so the third way here Is to use a specific kind of font.
784
00:40:01,666 --> 00:40:04,901
Like maybe in the calculator display we wanna use something
785
00:40:04,903 --> 00:40:05,969
that looks like LED segments.
786
00:40:05,971 --> 00:40:08,171
You know what I'm talking about? The old style,
787
00:40:08,173 --> 00:40:11,941
funny calculator thing back when we had LED displays.
788
00:40:11,943 --> 00:40:15,578
[LAUGH] So we might wanna font this LED font which they make
789
00:40:15,580 --> 00:40:18,982
those. In that case, you wanna check out UI font and
790
00:40:18,984 --> 00:40:20,884
UI font descriptor, especially,
791
00:40:20,886 --> 00:40:23,620
because those are how you pick a specific font from
792
00:40:23,622 --> 00:40:27,090
a specific family, okay. Like the LED font family, okay.
793
00:40:27,092 --> 00:40:31,728
So those are the three ways. Preferred system or highly
794
00:40:31,730 --> 00:40:36,399
customized okay, how about drawing images? Just like
795
00:40:36,401 --> 00:40:39,102
there's UILabel for drawing text, there's something called
796
00:40:39,104 --> 00:40:41,571
UIImageView for drawing images. So, you could just put
797
00:40:41,573 --> 00:40:44,707
an image if I wanted to put image on this guy's forehead,
798
00:40:44,709 --> 00:40:48,711
I could just put UIImage here and have my image here. Okay,
799
00:40:48,713 --> 00:40:50,213
put it as a sub view of my face view.
800
00:40:50,215 --> 00:40:52,982
So, that's one way to do it. But, if I want to draw
801
00:40:52,984 --> 00:40:56,686
UI image in my draw rack, it's very similar to the text,
802
00:40:56,688 --> 00:40:59,122
I'm going to get a UI image, okay.
803
00:40:59,124 --> 00:41:01,958
There's a number of ways. I'll show you here to do that.
804
00:41:01,960 --> 00:41:04,360
But, the main way is you're gonna do it by name.
805
00:41:04,362 --> 00:41:07,063
You're gonna put the image in that images.xcassets.
806
00:41:07,065 --> 00:41:09,933
Do you remember that thing I kept moving out of the way
807
00:41:09,935 --> 00:41:12,735
into supporting files? Okay Images.xcassets,
808
00:41:12,737 --> 00:41:15,738
so you're gonna drag an image in there, give it a name, and
809
00:41:15,740 --> 00:41:18,975
then you're gonna say image named that name and you're
810
00:41:18,977 --> 00:41:22,912
gonna get a UIImage. This is optional because it's my fail.
811
00:41:22,914 --> 00:41:25,448
Okay, you might have forgotten to drag that image in there or
812
00:41:25,450 --> 00:41:28,751
something. Who knows? But once you have this UIImage in your
813
00:41:28,753 --> 00:41:31,521
hand. By the way there are other ways you can do it.
814
00:41:31,523 --> 00:41:32,956
You can create it from a file.
815
00:41:32,958 --> 00:41:35,158
Just some image data you got from the Internet for example.
816
00:41:35,160 --> 00:41:38,261
That's another way to create a UI image. You can even do it
817
00:41:38,263 --> 00:41:42,532
by drawing graphics onto an off screen buffer. You have
818
00:41:42,534 --> 00:41:47,237
that image in your hand, you draw it the same as the text.
819
00:41:47,239 --> 00:41:50,440
You say draw at point. That's the upper left.
820
00:41:50,442 --> 00:41:52,308
But you also can do what called draw erect.
821
00:41:52,310 --> 00:41:54,944
Which will draw the image but scale it to fit this
822
00:41:54,946 --> 00:41:58,548
rect, okay? Or you can draw it as a pattern which will
823
00:41:58,550 --> 00:42:01,684
tile it okay? It will take that rect and use it as a tile
824
00:42:01,686 --> 00:42:05,288
to fill in the rectangle. Okay, so this code would be
825
00:42:05,290 --> 00:42:10,994
the kind of code that would be in your draw rect. Okay
826
00:42:10,996 --> 00:42:14,697
the last thing here above you is redrawing on bounds change,
827
00:42:14,699 --> 00:42:19,702
okay. When your view's bounds changes, what happens
828
00:42:19,704 --> 00:42:25,241
to the face inside here? Okay so, if I had this face and
829
00:42:25,243 --> 00:42:29,979
it was in a portrait thing here. Let's say it looked like
830
00:42:29,981 --> 00:42:35,118
this, okay. Face. And I rotated my device in this
831
00:42:35,120 --> 00:42:40,757
bounds of my view changed to be like this. Landscape.
832
00:42:40,759 --> 00:42:43,293
You think well I'm a face would you have to redraw and
833
00:42:43,295 --> 00:42:46,329
it would probably look about the same. The answer is not.
834
00:42:46,331 --> 00:42:52,702
It would look like this, okay? It would get stretched out.
835
00:42:52,704 --> 00:42:56,472
And that's cuz the default when your bounds change is to
836
00:42:56,474 --> 00:43:01,010
scale all the bits, to not redraw, okay? And that's
837
00:43:01,012 --> 00:43:04,747
a performance thing as you can imagine. It's a lot more,
838
00:43:04,749 --> 00:43:07,417
a lot easier just to stretch those bits out than to ask
839
00:43:07,419 --> 00:43:11,220
draw rec to go again. So you can fix this though with this
840
00:43:11,222 --> 00:43:14,591
property in your eye view called content mode. Okay,
841
00:43:14,593 --> 00:43:20,063
UI content mode? And some of the value of it can be
842
00:43:20,065 --> 00:43:23,700
to move the contents to the left to right top, bottom top,
843
00:43:23,702 --> 00:43:26,736
right top left. Okay you could just move it, not stretch it
844
00:43:26,738 --> 00:43:29,706
out but just move it to that place in the new bounce.
845
00:43:29,708 --> 00:43:33,176
You can scale it. This is the default, ScaleToFill, okay?
846
00:43:33,178 --> 00:43:35,578
And that's a stretching out behavior.
847
00:43:35,580 --> 00:43:37,180
Or the holy grail, Redraw.
848
00:43:37,182 --> 00:43:40,149
If you set your content mode to Redraw, it'll call your
849
00:43:40,151 --> 00:43:44,887
drawRect again, okay? You often want that. Maybe most of
850
00:43:44,889 --> 00:43:49,425
the time you want that, okay? All right, everybody got that?
851
00:43:49,427 --> 00:43:52,595
We'll be doing this in the demo, too, so you'll see it.
852
00:43:52,597 --> 00:43:54,530
Okay, so I'm not going to come back to this slide.
853
00:43:54,532 --> 00:43:57,233
So, just tell you what's coming up after the demo.
854
00:43:57,235 --> 00:44:00,003
No section on Friday, as we said. Next week, gestures,
855
00:44:00,005 --> 00:44:03,439
multiple MVCs and View Controller Lifecycle, okay we
856
00:44:03,441 --> 00:44:05,908
will talk about next week. And right now I'm going to do
857
00:44:05,910 --> 00:44:08,745
at demo where I'm gonna do custom UI view to draw this
858
00:44:08,747 --> 00:44:14,083
face thing, okay. That's the timer saying we should
859
00:44:14,085 --> 00:44:20,556
start the demo. Let's do it. All right,
860
00:44:20,558 --> 00:44:23,393
so I'm not going to be doing calculator here, this
861
00:44:23,395 --> 00:44:26,629
is going to be a completely new application, alright,
862
00:44:26,631 --> 00:44:29,599
so I'm going down here to create new XCode project.
863
00:44:29,601 --> 00:44:32,769
Okay, here it is, it's still in iOS application though,
864
00:44:32,771 --> 00:44:34,237
we'll do single view application.
865
00:44:34,239 --> 00:44:38,007
I'm gonna call it, Faceit, okay? It's a face drawing
866
00:44:38,009 --> 00:44:41,277
app so I'm ca-calling it Faceit. Make sure it's Swift,
867
00:44:41,279 --> 00:44:43,312
not Objective-C and it'll be a universal
868
00:44:43,314 --> 00:44:45,515
app cuz we're eventually gonna do multiple MVCs with
869
00:44:45,517 --> 00:44:50,386
this thing too. No core date or testing, okay? We're going
870
00:44:50,388 --> 00:44:52,855
to put it in the same place we put the calculator here, not
871
00:44:52,857 --> 00:44:56,726
source code control yet. All right, so here's our new app,
872
00:44:56,728 --> 00:44:59,362
I'm going to do the same thing I always do with this,
873
00:44:59,364 --> 00:45:00,963
I called that images, that x, assets,
874
00:45:00,965 --> 00:45:02,732
but it's called assets.xz assets.
875
00:45:02,734 --> 00:45:04,934
This is where you would drag in your images if you wanted
876
00:45:04,936 --> 00:45:08,104
to do that UI image named foo thing. You'll put them in
877
00:45:08,106 --> 00:45:11,340
here, but, obviously, I don't need these things,
878
00:45:11,342 --> 00:45:11,407
as usual, so
879
00:45:11,409 --> 00:45:13,976
I'm just gonna group them into that supporting
880
00:45:13,978 --> 00:45:16,412
files so they're not distracting you every time you
881
00:45:16,414 --> 00:45:19,716
take a look over here on the left, cuz all we really care,
882
00:45:19,718 --> 00:45:22,251
care about, again, is our storyboard and
883
00:45:22,253 --> 00:45:25,054
our view controller. Now, again, we're talking about
884
00:45:25,056 --> 00:45:26,556
view controller lifecycle next week, so
885
00:45:26,558 --> 00:45:29,225
we're gonna delete these, all right? Now, the first
886
00:45:29,227 --> 00:45:31,761
thing I'm gonna do is actually rename this ViewController,
887
00:45:31,763 --> 00:45:34,030
because it gives me this [LAUGH] generic name,
888
00:45:34,032 --> 00:45:37,366
ViewController, which we talked about last week, not so
889
00:45:37,368 --> 00:45:40,603
good, okay? Well, I'm, I don't want that. I'm gonna,
890
00:45:40,605 --> 00:45:43,206
I want mine to be called FaceViewController,
891
00:45:43,208 --> 00:45:44,807
because it's gonna show a face.
892
00:45:44,809 --> 00:45:46,943
So I could just say FaceViewController, and
893
00:45:46,945 --> 00:45:50,113
I might think, okay, I'm good to go, FaceViewController. But
894
00:45:50,115 --> 00:45:52,982
of course, that's not good enough. First of all,
895
00:45:52,984 --> 00:45:55,885
I probably wanna rename the file, because in Swift,
896
00:45:55,887 --> 00:45:59,455
generally the name of the file should be the name of the most
897
00:45:59,457 --> 00:46:03,192
important class in the file, okay? Cuz that's what people
898
00:46:03,194 --> 00:46:04,827
are gonna expect when they see the name of the file.
899
00:46:04,829 --> 00:46:06,596
So that's one thing. But that's not enough,
900
00:46:06,598 --> 00:46:10,166
either. Cuz if I go back here to my storyboard,
901
00:46:10,168 --> 00:46:12,635
and I look at my view controller right here.
902
00:46:12,637 --> 00:46:15,805
I can actually look at this inspector up here. If you pick
903
00:46:15,807 --> 00:46:18,841
up your view controller and look at his one right here.
904
00:46:18,843 --> 00:46:20,409
It's called the identity inspector.
905
00:46:20,411 --> 00:46:23,813
I can actually see what the story board thinks the class
906
00:46:23,815 --> 00:46:27,216
of this view controller is. And you can see that it thinks
907
00:46:27,218 --> 00:46:31,554
it's view controller, okay. Generic view controller. And
908
00:46:31,556 --> 00:46:34,357
of course I renamed so that's not what I want. So I can
909
00:46:34,359 --> 00:46:36,959
change it though, just by clicking here and changing it
910
00:46:36,961 --> 00:46:41,397
to face view controller, okay. So in your assignment to,
911
00:46:41,399 --> 00:46:42,231
it's not a required task, but
912
00:46:42,233 --> 00:46:46,469
I strongly recommend that you rename your view controller.
913
00:46:46,471 --> 00:46:48,971
Assignment two is about the calculator. Rename it from
914
00:46:48,973 --> 00:46:52,441
calc, from View Controller to be Calculator View Controller.
915
00:46:52,443 --> 00:46:54,143
You will definitely want that for assignment three.
916
00:46:54,145 --> 00:46:57,113
But I recommend doing it in assignment two. Okay, just so
917
00:46:57,115 --> 00:46:58,748
you, this is all you have to do, what I just did.
918
00:46:58,750 --> 00:47:00,750
It's not that hard, but it's good practice.
919
00:47:00,752 --> 00:47:03,753
Okay, it'll help you understand that the storyboard
920
00:47:03,755 --> 00:47:07,423
has classes of things in its identity inspect, inspector
921
00:47:07,425 --> 00:47:11,694
that have to match up with what's in your code, okay?
922
00:47:11,696 --> 00:47:12,829
All right so
923
00:47:12,831 --> 00:47:17,500
we got that renamed where we want to draw our face okay so
924
00:47:17,502 --> 00:47:20,536
we need a face view a UI view a subclass of UI view it
925
00:47:20,538 --> 00:47:23,506
does that. So let's create a subclass of UI view. Anytime
926
00:47:23,508 --> 00:47:26,475
we're adding something to our project we go file new and
927
00:47:26,477 --> 00:47:30,046
I'm gonna create a new file right here and this file's
928
00:47:30,048 --> 00:47:33,583
gonna be iOS source. It's gonna be a Cocoa Touch Class,
929
00:47:33,585 --> 00:47:36,419
cuz it's gonna be a subclass of UIView. Any time we create
930
00:47:36,421 --> 00:47:39,589
a subclass of something in Cocoa Touch like UIView or
931
00:47:39,591 --> 00:47:42,158
UIViewController or whatever, this is what we want.
932
00:47:42,160 --> 00:47:45,061
So I'm gonna double-click that. Here it says subclass
933
00:47:45,063 --> 00:47:48,631
of, it's already on UIView. But you can create subclasses
934
00:47:48,633 --> 00:47:51,934
of NSObject, especially if you're doing Objective-C.
935
00:47:51,936 --> 00:47:54,337
Or UIView controller or any of many, many,
936
00:47:54,339 --> 00:47:58,107
many, many other classes. But we're gonna do UIView here.
937
00:47:58,109 --> 00:48:00,376
I'm gonna call my view Faceview.
938
00:48:00,378 --> 00:48:03,246
Okay, cuz that's what it is, a view that draws a face.
939
00:48:03,248 --> 00:48:05,648
Okay, I'm gonna put it in the same group right here,
940
00:48:05,650 --> 00:48:09,318
this group that has all this stuff in it. Okay, same thing.
941
00:48:09,320 --> 00:48:12,054
We'll put it in there. And here it is.
942
00:48:12,056 --> 00:48:13,990
Notice that it gave me a drawRect.
943
00:48:13,992 --> 00:48:17,226
Look at that, drawRect. But it's commented out.
944
00:48:17,228 --> 00:48:19,262
Now why did it give me a drawRect commented out?
945
00:48:19,264 --> 00:48:22,632
And this is because unless your drawRect actually does
946
00:48:22,634 --> 00:48:25,201
something, do not have a drawRect, because if
947
00:48:25,203 --> 00:48:28,371
the systems sees that you have a UIB that has a drawRect,
948
00:48:28,373 --> 00:48:31,908
it's gonna think that it needs to get you to draw itself all
949
00:48:31,910 --> 00:48:34,877
the time. But if you don't actually draw anything,
950
00:48:34,879 --> 00:48:35,645
maybe you just have subviews,
951
00:48:35,647 --> 00:48:38,214
okay, you don't draw anything, then you don't want the system
952
00:48:38,216 --> 00:48:41,784
wasting its time trying to get you to draw yourself, okay?
953
00:48:41,786 --> 00:48:46,255
But of course, we are going to draw, so I'm gonna uncomment
954
00:48:46,257 --> 00:48:50,326
out my drawRect here and start drawing my face.
955
00:48:50,328 --> 00:48:56,866
Now my face is gonna look like we drew before here, okay?
956
00:48:56,868 --> 00:49:00,803
It's gonna be a rectangle and here's my bounds, let's say,
957
00:49:00,805 --> 00:49:03,606
that I have to draw in. I'm gonna draw my face in
958
00:49:03,608 --> 00:49:07,176
the middle, okay? It's gonna be the smaller of the width or
959
00:49:07,178 --> 00:49:10,479
the height so the, its face fits completely, right? And
960
00:49:10,481 --> 00:49:13,082
then I'm just gonna have a couple of round eyes and
961
00:49:13,084 --> 00:49:15,117
a smiley face. And we're gonna make it so
962
00:49:15,119 --> 00:49:16,886
this smile can be a smile or a frown or
963
00:49:16,888 --> 00:49:18,521
it's movable basically, changeable.
964
00:49:18,523 --> 00:49:21,123
Okay, so that's, that's what we're gonna build right here,
965
00:49:21,125 --> 00:49:24,293
okay? So since I want this thing to be in the center, and
966
00:49:24,295 --> 00:49:27,630
I want it to be the smaller of the width or the height so
967
00:49:27,632 --> 00:49:30,967
it fits, I'm gonna create two vars to start off for
968
00:49:30,969 --> 00:49:36,105
the center and this radius of this thing. So let's do that.
969
00:49:36,708 --> 00:49:39,875
Okay, so let's, first let's do the radius here, so
970
00:49:39,877 --> 00:49:41,877
I'm gonna say var. I'm inside my drawRect here,
971
00:49:41,879 --> 00:49:45,314
just implement this. I'm gonna call this the skullRadius
972
00:49:45,316 --> 00:49:49,652
cuz this is the radius of my face's skull, okay? And
973
00:49:49,654 --> 00:49:55,825
I'm gonna make that equal to the minimum of the width or
974
00:49:55,827 --> 00:49:59,395
the height of my view. Now what is the width or
975
00:49:59,397 --> 00:50:02,565
the height of my view? Now there's different variables we
976
00:50:02,567 --> 00:50:05,735
could look at here. We could look at this rect, okay?
977
00:50:05,737 --> 00:50:10,339
I could say let width equal rect.size.width.
978
00:50:10,341 --> 00:50:14,510
Would that be right? No, that would not be right,
979
00:50:14,512 --> 00:50:16,879
because this rect is just an optimization
980
00:50:16,881 --> 00:50:19,615
that says what part of the view to draw, okay?
981
00:50:19,617 --> 00:50:23,019
Well, my face needs to kind of draw in the whole view or
982
00:50:23,021 --> 00:50:25,321
won't be the right size, okay, it would be all small.
983
00:50:25,323 --> 00:50:29,658
So this is not the right one. I could say frame.size.width.
984
00:50:29,660 --> 00:50:34,063
Would that be right? No or yes?
985
00:50:34,065 --> 00:50:38,167
No, I see a couple of shaking heads no. The answer is no.
986
00:50:38,169 --> 00:50:41,137
Okay, frame is the rectangle that contains me in my
987
00:50:41,139 --> 00:50:44,006
superviews coordinates. I'm drawing myself,
988
00:50:44,008 --> 00:50:45,741
I can't draw in my superviews coordinates.
989
00:50:45,743 --> 00:50:49,045
I need to draw in my coordinate system, okay? So
990
00:50:49,047 --> 00:50:52,982
what we want here is bounds, okay. Bounds is the rectangle
991
00:50:52,984 --> 00:50:56,185
that I'm drawing in in my coordinate system here,
992
00:50:56,187 --> 00:51:00,356
okay? Same thing height, bounds.size.height, okay?
993
00:51:00,358 --> 00:51:01,824
And also I want my skull's radius,
994
00:51:01,826 --> 00:51:06,429
I'm not doing the diameter. I want it to be divided by 2.
995
00:51:06,431 --> 00:51:08,798
Now it's kinda silly to do these local variables.
996
00:51:08,800 --> 00:51:11,400
Probably a lot better just to take this and copy and
997
00:51:11,402 --> 00:51:16,005
paste these right in here like this. Okay,
998
00:51:16,007 --> 00:51:19,141
let's make some more room here. All right, so
999
00:51:19,143 --> 00:51:22,211
I've got the radius of my skull. That's good. What's
1000
00:51:22,213 --> 00:51:25,281
the other thing I needed, is the center of my skull, so
1001
00:51:25,283 --> 00:51:29,518
I'm gonna call that skullCenter. Okay, and
这里我就命名为 skullCenter.
1002
00:51:29,520 --> 00:51:34,690
what is the center? Well, can I say center? Is that good?
那么这里的中心指的是什么? 我这里可以直接写 center 吗?
1003
00:51:34,692 --> 00:51:38,427
No, center is my center in my superviews coordinates.
当然不可以,这里的 center 指的是父视图坐标系的中心。
1004
00:51:38,429 --> 00:51:41,831
That's where I am, okay, not the center. Now here's two
而这个真是我所在的地方, 所以不能用 center 。
1005
00:51:41,833 --> 00:51:45,101
interesting ways to get the actual center of my bounds.
这里有两种方式获得当前坐标系的 center 。
1006
00:51:45,103 --> 00:51:49,205
Okay, one is, believe it or not, is I can say,
信不信由你, 其中一种是通过 convertPoint 方法获得。
1007
00:51:49,207 --> 00:51:53,909
convertPoint. This is a UIView method, convertPoint,
这是一个 UIView 的方法, convertPoint,
1008
00:51:53,911 --> 00:51:57,746
convert center, this point's center, from view,
将 center 从父视图的坐标系转换获得。
1009
00:51:57,748 --> 00:52:01,183
my superview, okay? [LAUGH] So I would be converting that
这样我就可以通过 center 来获得
1010
00:52:01,185 --> 00:52:03,953
point, the center, from my superviews coordinates to my
从父视图的坐标系转换到当前视图的坐标系的坐标。
1011
00:52:03,955 --> 00:52:06,622
coordinate system. This would be right. This would work,
这样做就能获得正确的结果, 这样做就能生效,
1012
00:52:06,624 --> 00:52:09,825
cuz now I'm in my coordinates system. Same point, but
因为现在是在当前视图的坐标系。 同一个点, 但是
1013
00:52:09,827 --> 00:52:10,826
it's in my coordinate system.
是在当前视图的坐标系中的表示。
1014
00:52:10,828 --> 00:52:12,294
So that's one way we could do it.
这就是其中一种方法。
1015
00:52:12,296 --> 00:52:14,029
We probably wouldn't do it that way. Instead,
我们通常不使用这种方法。 取而代之的是,
1016
00:52:14,031 --> 00:52:18,334
we'd probably use some of the vars that are on CGRect. For
我们经常用 CGRect 中的属性来获得。
1017
00:52:18,336 --> 00:52:21,570
example, I'd probably say something like
例如, 我们经常说脸的骨架的中心
1018
00:52:21,572 --> 00:52:26,442
the skull center is a CGPoint whose x, okay, x and
是一个 CGPoint, 一个通过 x 和 y组成的 CGPoint 。
1019
00:52:26,444 --> 00:52:30,913
y of CGPoint, right, whose x is probably my bounds.midX and
在这里我们的 x 应该是 bounds.midX ,
1020
00:52:30,915 --> 00:52:36,986
my y is my bounds.midY. Okay, so midX is just a property
y 应该是 bounds.midY 。 这里的 midX 只是一个
1021
00:52:36,988 --> 00:52:40,890
on CGRect which tells you the x that's midway across this,
CGRect 的属性, 这个属性可以告诉我们水平方向行的中点,
1022
00:52:40,892 --> 00:52:45,261
the rectangle. Same thing midY. Okay, so
当然这里指的是这个矩形中, 同理可以说明 midY 。
1023
00:52:45,263 --> 00:52:48,264
I've got my radius and my center. I put these as vars,
这样我就获得骨架的半径。 我这里通过变量进行存储,
1024
00:52:48,266 --> 00:52:50,533
but really they probably wanna be lets,
当时它们更应该用常亮进行存储。
1025
00:52:50,535 --> 00:52:51,567
okay, cuz I'm, I've calculated,
毕竟这个是通过我们计算获得的,
1026
00:52:51,569 --> 00:52:54,904
I'm not gonna change them after this. All right, so
并且在之后的代码中也不会修改。 这样我就有了
1027
00:52:54,906 --> 00:52:59,475
now that I have this, I need to create a Bezier path for
一些信息, 我需要创建一个 Bezier Path 来绘制出骨架,
1028
00:52:59,477 --> 00:53:02,578
my skull, for the circle here. So I'm just going to
这里通过一个圆来表示, 这里我通过
1029
00:53:02,580 --> 00:53:05,681
create a local variable here called skull. It's going to be
一个名为 skull 的局部变量进行存储。 它的类型
1030
00:53:05,683 --> 00:53:10,286
of type UIBezierPath, and so UIBezierPath. And
应该是 UIBezierPath 。
1031
00:53:10,288 --> 00:53:13,923
you can see UIBezierPath has a bunch of initializers here.
你可以看到 UIbezierPath 有非常多的初始化的函数。
1032
00:53:13,925 --> 00:53:16,859
I could use this one, ovalInRect, because a circle
当然我这里可以使用 ovalInRect 这个函数, 因为
1033
00:53:16,861 --> 00:53:20,329
is an oval. But I'm gonna use this one arcCenter because
圆也是一个椭圆。 但是我这里更愿意用另一个名为 arcCenter 的初始化函数,
1034
00:53:20,331 --> 00:53:25,201
I have the radius in center. I don't I don't wanna create
因为我这里有半径和中心点。而我这里也不需要再
1035
00:53:25,203 --> 00:53:26,335
an oval in my entire bound.
我的整个矩形中画一个椭圆。
1036
00:53:26,337 --> 00:53:29,505
I'd have to create just some kinda rectangle that gets my,
我这里希望根据矩形绘制出我的脸型的轮廓。
1037
00:53:29,507 --> 00:53:32,441
only my skull. So I'm gonna use this one, arcCenter, which
所以我这里用 arcCenter 这个初始化的函数,
1038
00:53:32,443 --> 00:53:35,477
takes a radius and a center, which I have right here.
而这个函数需要半径和中心点作为参数, 而这些参数我已经在上面获得了。
1039
00:53:35,479 --> 00:53:38,547
And then a start angle and an end angle as it goes around
另外还需要一个起始角度和结束角度来绘制这个弧度,
1040
00:53:38,549 --> 00:53:42,151
the arc, and either clockwise or counterclockwise. Okay, so
并且同时还需要配置是通过顺时针还是逆时针来绘制。
1041
00:53:42,153 --> 00:53:46,422
let's do that. So what is the center of my arc? skullCenter,
那么我们开始绘制吧。 那么这个弧线的中心点是什么? 当然是 skullCenter ,
1042
00:53:46,424 --> 00:53:49,625
okay, that's the center of this arc I'm gonna draw that's
这是我们即将绘制的轮廓弧线的中心点,
1043
00:53:49,627 --> 00:53:53,395
gonna be my skull. The radius is the skullRadius, right,
而这里的半径应该是 skullRadius,
1044
00:53:53,397 --> 00:53:55,664
that's the radius I'm going around.
在上面获得的半径。
1045
00:53:55,666 --> 00:53:59,802
Now startAngle to endAngle, that's in radians.
接下来是弧线的其实角度和结束角度。
1046
00:53:59,804 --> 00:54:00,903
Does everyone know what radians?
有人知道关于弧度的内容吗?
1047
00:54:00,905 --> 00:54:04,707
Raise your hands if you know what radians are. Okay,
如果知道弧度的请举手。
1048
00:54:04,709 --> 00:54:06,342
almost everybody, good. So radians,
很好,大部分的人都知道。 那么弧度是从 0 到 2π
1049
00:54:06,344 --> 00:54:09,845
0 to 2pi radians to go all the way around the circle.
将会获得一个完整的圆形。
1050
00:54:09,847 --> 00:54:14,350
So I'm gonna go from 0 to 2*_PI, okay.
那么这里我就知道为从 0 到 2π。
1051
00:54:14,352 --> 00:54:16,852
And then I can either go clockwise around or I can go
这里我可以选择顺时针也可以选择逆时针。
1052
00:54:16,854 --> 00:54:19,688
counterclockwise. In this case I'm going all the way around,
因为在这里我需要绘制出完整的圆形,
1053
00:54:19,690 --> 00:54:23,058
so it doesn't matter. So I'll just go counterclockwise,
所以选择哪一个都没问题。 这里我就先选择逆时针。
1054
00:54:23,060 --> 00:54:27,930
whatever, okay? So you can see how this has drawn an arc, so
这样你就可以看到如何绘制一个弧线了。
1055
00:54:27,932 --> 00:54:29,798
I've create a Bezier path which the path
1056
00:54:29,800 --> 00:54:33,402
is around a circle, okay? Now well, I have an error here?
1057
00:54:33,404 --> 00:54:35,137
What's, what do you think this error right here is?
1058
00:54:35,139 --> 00:54:38,774
Look at that. Cannot convert value of type 'Double'
1059
00:54:38,776 --> 00:54:40,776
to expected argument type 'CGFloat'.
1060
00:54:40,778 --> 00:54:43,812
It's pointing to this little thing right here, this _PI,
1061
00:54:43,814 --> 00:54:45,648
okay? Well, this is what I was telling you about before.
1062
00:54:45,650 --> 00:54:52,621
All these drawing things are in CGFloat. What type is this?
1063
00:54:52,623 --> 00:54:55,057
What type is that expression? >> Double?
1064
00:54:55,059 --> 00:54:56,058
>> It's a double, right. Okay,
1065
00:54:56,060 --> 00:54:58,861
so we can't use a double there. It has to be a CGFloat.
1066
00:54:58,863 --> 00:55:02,564
So we have to convert it to a CGFloat by doing this,
1067
00:55:02,566 --> 00:55:06,969
okay, CGFloat. Now how come this is not complaining?
1068
00:55:06,971 --> 00:55:10,706
Okay, I told you last time that if you see a literal 0.0,
1069
00:55:10,708 --> 00:55:15,177
it thinks that's a double. Didn't I tell you that? Well,
1070
00:55:15,179 --> 00:55:18,647
I lied. Because when it sees 0.0, it can convert it to
1071
00:55:18,649 --> 00:55:22,017
a number of different types, since it's a literal.
1072
00:55:22,019 --> 00:55:25,220
Okay, Swift can only do this automatic typing version for
1073
00:55:25,222 --> 00:55:27,990
literals, okay? So 0.0 is a literal, so
1074
00:55:27,992 --> 00:55:29,725
it can convert to a number of different types.
1075
00:55:29,727 --> 00:55:34,596
Double, Float, CGFloat. It knows how to do that, okay?
1076
00:55:34,598 --> 00:55:36,732
So why does it pick CGFloat here?
1077
00:55:36,734 --> 00:55:41,036
Because it knows this method takes a CGFloat. And when
1078
00:55:41,038 --> 00:55:44,073
it sees a literal and it sees CGFloat is the argument type,
1079
00:55:44,075 --> 00:55:46,408
it tries to convert it to a CGFloat if it can.
1080
00:55:46,410 --> 00:55:52,081
Which it can, okay? Everybody got that? All right,
1081
00:55:52,083 --> 00:55:54,717
so I have this skull. It's of type UIBezierPath.
1082
00:55:54,719 --> 00:55:56,919
I can set attributes on this skull now,
1083
00:55:56,921 --> 00:56:00,723
like maybe it's Line with maybe we'll make it 5.0 point
1084
00:56:00,725 --> 00:56:05,027
that's 5.0 points wide. Right? If I wanna set a color,
1085
00:56:05,029 --> 00:56:07,930
I don't say skull set color something like that.
1086
00:56:07,932 --> 00:56:12,334
I create the color I want. So let's make our skull be blue
1087
00:56:12,336 --> 00:56:15,070
okay? So blueColor, which is the type method on UIColor.
1088
00:56:15,072 --> 00:56:19,908
And I'm gonna say set. There's setFill, there's setStroke,
1089
00:56:19,910 --> 00:56:23,579
and there's also set, which sets both the fill and
1090
00:56:23,581 --> 00:56:26,448
the stroke. Okay? Now,
1091
00:56:26,450 --> 00:56:30,486
to get it to draw, I just need to say skull.stroke, okay,
1092
00:56:30,488 --> 00:56:35,023
and that's going to draw along this Arc, then I drew with
1093
00:56:35,025 --> 00:56:39,361
this line width and with the color that set. Okay, that's
1094
00:56:39,363 --> 00:56:44,032
all that's necessary to draw the skull of our face here.
1095
00:56:44,034 --> 00:56:46,335
So, now we have this nice UIView, this custom UIView.
1096
00:56:46,337 --> 00:56:49,938
It draws the skull of a face anyway. How do we get it into
1097
00:56:49,940 --> 00:56:53,642
our UI? So if we go back to out storyboard over here.
1098
00:56:53,644 --> 00:56:56,011
Right now it's blank. There's nothing in here.
1099
00:56:56,013 --> 00:56:58,380
There's only this background view.
1100
00:56:58,382 --> 00:57:01,250
And I promised you that there was a Var in
1101
00:57:01,252 --> 00:57:05,087
the Faceview controller that pointed to this view.
1102
00:57:05,089 --> 00:57:07,990
And I'm going to show it to you by right clicking on this.
1103
00:57:07,992 --> 00:57:10,993
You see it right here? When I mouse over it you see how it's
1104
00:57:10,995 --> 00:57:15,998
highlighting it? This is a outlet called View, just like
1105
00:57:16,000 --> 00:57:17,833
you have an outlet on your display on your calculator,
1106
00:57:17,835 --> 00:57:22,671
this one is an outlet or var, which is a type UI view and
1107
00:57:22,673 --> 00:57:24,740
it's connected to this thing right here so
1108
00:57:24,742 --> 00:57:25,674
it's automatically connected up for
1109
00:57:25,676 --> 00:57:28,844
you., you don't even need to control, drag it or anything,
1110
00:57:28,846 --> 00:57:31,513
okay? But we're not gonna be adding our viewing code, so
1111
00:57:31,515 --> 00:57:32,614
we don't really need that var,
1112
00:57:32,616 --> 00:57:33,816
I just wanted to show you it was there.
1113
00:57:33,818 --> 00:57:36,552
I'm going to add my view by dragging it out. And if I
1114
00:57:36,554 --> 00:57:39,388
go down here to my palette, obviously I'm not gonna find
1115
00:57:39,390 --> 00:57:42,291
face view in here. Okay. IOS didn't know
1116
00:57:42,293 --> 00:57:45,828
about face view when it built Xcode. But I can go down to
1117
00:57:45,830 --> 00:57:49,031
the very bottom here and grab one of these. Okay?
1118
00:57:49,033 --> 00:57:53,101
This is a generic UI view Okay not a sub class of UI view
1119
00:57:53,103 --> 00:57:57,039
just the UI view. So I'm gonna drag it out and put it here.
1120
00:57:57,041 --> 00:57:59,641
Now I'm gonna do a cool thing okay I'm trying to show you
1121
00:57:59,643 --> 00:58:02,744
bit by bit a little more of this constraints thing so
1122
00:58:02,746 --> 00:58:05,948
I'm gonna take this thing I'm gonna us the blue lines okay.
1123
00:58:05,950 --> 00:58:09,184
I'm gonna put it all the way up in the corner here and
1124
00:58:09,186 --> 00:58:12,020
then I'm gonna put the other edge all the way down in
1125
00:58:12,022 --> 00:58:14,723
the corner down here and I'm using blue lines you see blue
1126
00:58:14,725 --> 00:58:18,961
lines? Appearing. Those blue lines are making this thing
1127
00:58:18,963 --> 00:58:21,930
attach to well known point. In this case, the edges.
1128
00:58:21,932 --> 00:58:25,734
So I want my face view to fill the entire view, okay?
1129
00:58:25,736 --> 00:58:29,938
The entire view controller's view. All right, but just
1130
00:58:29,940 --> 00:58:32,841
doing the blue lines isn't enough as we learned, okay?
1131
00:58:32,843 --> 00:58:35,477
That's just kind of telling the system what you intend.
1132
00:58:35,479 --> 00:58:38,580
To make it do it you actually have to have constraints like
1133
00:58:38,582 --> 00:58:39,715
those things we control drag.
1134
00:58:39,717 --> 00:58:42,451
So I could try and control drag to an edge, control down
1135
00:58:42,453 --> 00:58:45,921
to this edge, all that, but there's an easier way. If you
1136
00:58:45,923 --> 00:58:49,791
did all blue lines and that's all you needed, you could
1137
00:58:49,793 --> 00:58:53,161
go down here to the same place where you update frames and
1138
00:58:53,163 --> 00:58:57,933
instead do Reset to Suggested Constraints. And that will
1139
00:58:57,935 --> 00:59:02,504
use the blue lines to put some suggestive constraints. Now,
1140
00:59:02,506 --> 00:59:05,641
let's see what it did. You can see all the constraints on
1141
00:59:05,643 --> 00:59:08,210
a view by going over to the inspector and
1142
00:59:08,212 --> 00:59:12,347
going to this tab right here, which is the size inspector.
1143
00:59:12,349 --> 00:59:14,850
And if you look in the size inspector, down toward
1144
00:59:14,852 --> 00:59:18,954
the bottom, you can see constraints. You see them? And
1145
00:59:18,956 --> 00:59:20,589
this is showing me all the constraints and
1146
00:59:20,591 --> 00:59:23,825
it put one trailing, that looks good. Leading yeah,
1147
00:59:23,827 --> 00:59:28,564
top nice, bottom perfect. So it put exactly the constraints
1148
00:59:28,566 --> 00:59:33,168
I want because it followed the blue lines okay so now I have
1149
00:59:33,170 --> 00:59:36,071
this view constrained to stick to the edges.
1150
00:59:36,073 --> 00:59:38,273
Perfect, because then when I go landscape or
1151
00:59:38,275 --> 00:59:42,344
portrait my bounds are constantly being resized.
1152
00:59:42,346 --> 00:59:45,314
To fit the new shape. Okay? Now
1153
00:59:45,316 --> 00:59:48,951
the other thing I need to do is this is a generic UI view.
1154
00:59:48,953 --> 00:59:49,384
If I ran right now,
1155
00:59:49,386 --> 00:59:52,087
it would come up blank because it has no draw rects.
1156
00:59:52,089 --> 00:59:55,691
Okay? I did that nice face view draw rect but this is not
1157
00:59:55,693 --> 00:59:58,293
a face view. So how do I set this to be a face view?
1158
00:59:58,295 --> 01:00:02,331
Exactly the same way that I set this view controller to be
1159
01:00:02,333 --> 01:00:06,201
a face view controller. I go to the identity inspector and
1160
01:00:06,203 --> 01:00:09,504
instead of, so here is when I did the controller, I click on
1161
01:00:09,506 --> 01:00:13,041
this view and instead of it being a generic UI view,
1162
01:00:13,043 --> 01:00:17,279
you see, I change it to be face view.
1163
01:00:17,281 --> 01:00:21,183
Got it? Okay. So let's run, okay it should work,
1164
01:00:21,185 --> 01:00:23,986
let's run on Iphone six, for example. And
1165
01:00:23,988 --> 01:00:29,491
hopefully, we'll get the skull of our face to draw.
1166
01:00:35,532 --> 01:00:36,632
Perfect it worked okay.
1167
01:00:36,634 --> 01:00:39,735
So here's our skull it picked the smaller of the width or
1168
01:00:39,737 --> 01:00:40,669
the height they went to smaller so
1169
01:00:40,671 --> 01:00:43,805
picked is drawing the whole side, if I rotate? Uh-oh.
1170
01:00:43,807 --> 01:00:47,976
Stretch-a-roo. Okay? So that's this problem with the content
1171
01:00:47,978 --> 01:00:52,447
mode of our face view is set to be scaled to fill. Okay, so
1172
01:00:52,449 --> 01:00:53,515
it's scaling our thing.
1173
01:00:53,517 --> 01:00:56,318
So we don't want that. So let's go back here and
1174
01:00:56,320 --> 01:00:59,588
go to storyboard and select out face view right here,
1175
01:00:59,590 --> 01:01:00,789
go to the attributes inspector.
1176
01:01:00,791 --> 01:01:04,026
The very first thing is the content mode. And you can see
1177
01:01:04,028 --> 01:01:07,162
it is scale to fill, and i want it to be redraw.
1178
01:01:07,164 --> 01:01:09,998
In other words when my bounds change, call my draw rect.
1179
01:01:10,000 --> 01:01:18,240
You see that? Okay here we go,
1180
01:01:18,242 --> 01:01:22,177
works in landscape works in portrait. Okay,
1181
01:01:22,179 --> 01:01:25,981
it's even switching from using the width to the height.
1182
01:01:25,983 --> 01:01:27,883
>> Because that gets smaller.
1183
01:01:30,654 --> 01:01:31,219
>> Okay, sound good.
1184
01:01:31,221 --> 01:01:33,455
Now one thing that's kind of a bummer.
1185
01:01:33,457 --> 01:01:37,726
Here in my interface builder, I don't see my face.
1186
01:01:37,728 --> 01:01:40,796
How come I don't see my face here? And it is possible to
1187
01:01:40,798 --> 01:01:45,267
make the face appear here, and we'll do that next week, okay?
1188
01:01:45,269 --> 01:01:47,402
Cuz it's nice, when you're building your storyboard,
1189
01:01:47,404 --> 01:01:51,173
to be able to see your custom views in there. All right, so
1190
01:01:51,175 --> 01:01:53,008
let's go back to our face view and add some more.
1191
01:01:53,010 --> 01:01:56,945
Let's add some eyes. We'll add some eyes to, our face,
1192
01:01:56,947 --> 01:01:59,848
here, and to do that, I'm gonna start having some little
1193
01:01:59,850 --> 01:02:01,650
helper functions here along the way.
1194
01:02:01,652 --> 01:02:06,788
I'm gonna make it so that my eyes and
1195
01:02:06,790 --> 01:02:10,258
the mouth are all relative to the size of my skull,
1196
01:02:10,260 --> 01:02:11,293
what ever my skull size is,
1197
01:02:11,295 --> 01:02:14,629
I'm gonna make everything else relative to it obviously. So
1198
01:02:14,631 --> 01:02:16,665
I'm gonna take this skull center and radius, and
1199
01:02:16,667 --> 01:02:21,169
take them out of drawrect and make them vars, okay.
1200
01:02:21,171 --> 01:02:24,740
These are gonna be vars. Now when I tried to do this, and
1201
01:02:24,742 --> 01:02:27,309
you'll try this in your homework I'm sure.
1202
01:02:27,311 --> 01:02:28,210
You're gonna get this error.
1203
01:02:28,212 --> 01:02:32,013
It stays instance member bounds, okay? Is,
1204
01:02:32,015 --> 01:02:34,750
cannot be used in type face view. And you are gonna get so
1205
01:02:34,752 --> 01:02:38,587
frustrated, cuz you're gonna say bounds is definitely
1206
01:02:38,589 --> 01:02:39,654
an instance member of face view,
1207
01:02:39,656 --> 01:02:43,425
how come I can't use it here? Okay? And the answer
1208
01:02:43,427 --> 01:02:47,529
of why you can't use it here is because you are in
1209
01:02:47,531 --> 01:02:52,134
the initialization phase. You are initializing this. And
1210
01:02:52,136 --> 01:02:56,471
during initialization, you cannot use your class.
1211
01:02:56,473 --> 01:02:59,307
It's not initialized yet. So you can't call vars and
1212
01:02:59,309 --> 01:03:04,513
methods like bounds. Okay? Everyone understand that?
1213
01:03:04,515 --> 01:03:08,049
So, you will see this message I'm sure. And you will post on
1214
01:03:08,051 --> 01:03:10,352
PL as a Piazza probably and say, what's going on? But
1215
01:03:10,354 --> 01:03:12,087
I'm telling you right now what's going on.
1216
01:03:12,089 --> 01:03:14,055
During an initialization you cannot,
1217
01:03:14,057 --> 01:03:15,157
until you're fully initialized,
1218
01:03:15,159 --> 01:03:17,659
you can't access your own properties. So,
1219
01:03:17,661 --> 01:03:18,760
what are we going to do here about this?
1220
01:03:18,762 --> 01:03:22,330
Well, I'm just going to change this to be calculated. Okay?
1221
01:03:22,332 --> 01:03:26,201
To be a computed property. Okay. I'm gonna just return
1222
01:03:26,203 --> 01:03:31,072
this value right here. Okay. Now, two things about computer
1223
01:03:31,074 --> 01:03:35,043
properties. Notice that I didn't say get},
1224
01:03:35,045 --> 01:03:39,681
If you have a computer property that only gets,
1225
01:03:39,683 --> 01:03:42,918
you do not need to put together in there. Okay.
1226
01:03:42,920 --> 01:03:46,388
And we never would. Look much nicer not to put it.
1227
01:03:46,390 --> 01:03:48,790
Same thing here, this is a CGPoint.
1228
01:03:48,792 --> 01:03:54,429
We're going to return this. Okay,
1229
01:03:54,431 --> 01:03:59,000
not gonna put the get in there. All right,
1230
01:03:59,002 --> 01:04:03,605
that make sense? Now one other thing I wanna do is,
1231
01:04:03,607 --> 01:04:08,777
my skull is all the way out to the edge. I'd like my skull to
1232
01:04:08,779 --> 01:04:12,747
be able to be kinda scalable, to be smaller than the edge.
1233
01:04:12,749 --> 01:04:16,184
So I'm gonna add a public var called scale, okay,
1234
01:04:16,186 --> 01:04:18,119
which is going to be a CGFloat.
1235
01:04:18,121 --> 01:04:22,123
And it's going to scale my skull. And I'm just gonna do
1236
01:04:22,125 --> 01:04:24,226
that by here when I'm calculating the radius,
1237
01:04:24,228 --> 01:04:27,529
I'm just gonna multiply it by my scale. And
1238
01:04:27,531 --> 01:04:28,997
I'm gonna set it to 90% so
1239
01:04:28,999 --> 01:04:32,100
that my skull's kinda 90% of all the way in.
1240
01:04:32,102 --> 01:04:34,002
Now I'm gonna use these two vars, okay,
1241
01:04:34,004 --> 01:04:37,172
these computed vars, in all my other calculation. Eyes and
1242
01:04:37,174 --> 01:04:39,941
mouth, all these things, I'm gonna use the same thing. So
1243
01:04:39,943 --> 01:04:43,411
we'll do eyes today, and then mouth, we'll either do mouth
1244
01:04:43,413 --> 01:04:46,081
at the start of next lecture, or maybe I'll just post,
1245
01:04:46,083 --> 01:04:49,651
I'm gonna post this code, by the way, on Piazza afterwards.
1246
01:04:49,653 --> 01:04:51,086
Maybe I'll just post the mouth code so
1247
01:04:51,088 --> 01:04:55,156
you can take a look at it. But let's do these eyes first.
1248
01:04:55,158 --> 01:04:58,193
How do we do the eyes? To do the eyes,
1249
01:04:58,195 --> 01:05:02,497
I need kind of the ratio between the skull radius and
1250
01:05:02,499 --> 01:05:05,400
the eye size. So to make things quick, I actually am
1251
01:05:05,402 --> 01:05:08,036
gonna type those in real fast, here they are. Okay, so
1252
01:05:08,038 --> 01:05:11,072
here's all the ratios from the skull's radius to the eye's
1253
01:05:11,074 --> 01:05:14,743
offset to the eye's radius to the mouth's width, height, and
1254
01:05:14,745 --> 01:05:18,914
offset, etc. Notice how we do constants in Swift.
1255
01:05:18,916 --> 01:05:24,552
We create structs and then we have type variables, static.
1256
01:05:24,554 --> 01:05:27,956
Type variables, okay, which are lets. They're typed like
1257
01:05:27,958 --> 01:05:33,328
this too. That has the value, okay? So this is how we do it.
1258
01:05:33,330 --> 01:05:34,663
Notice these are capitalized and
1259
01:05:34,665 --> 01:05:37,399
of course as the name of a type we always capitalize
1260
01:05:37,401 --> 01:05:39,334
the names of types. Don't forget that.
1261
01:05:39,336 --> 01:05:42,103
Okay, some of you don't like to do that. Please do it.
1262
01:05:42,105 --> 01:05:44,606
In Swift we capitalize all type names. And
1263
01:05:44,608 --> 01:05:48,443
we also capitalize these, okay, these static things that
1264
01:05:48,445 --> 01:05:50,612
are basically constants in this struct.
1265
01:05:50,614 --> 01:05:53,348
And I'll show you, we're gonna access these by saying Ratios
1266
01:05:53,350 --> 01:05:56,718
dot this, okay, cuz this is the name of the type.
1267
01:05:56,720 --> 01:05:59,821
This is the, the value in it. So I'll show you that when we
1268
01:05:59,823 --> 01:06:03,591
start using these. So how am I gonna do this?
1269
01:06:03,593 --> 01:06:06,494
How am I gonna make my eye here? I'm gonna create a new
1270
01:06:06,496 --> 01:06:09,965
method here. First of all, let me create a little type here,
1271
01:06:09,967 --> 01:06:13,068
which is an enum. I'm gonna call it eye. It's gonna have
1272
01:06:13,070 --> 01:06:18,139
left eye and right eye. That's just so I can talk about my
1273
01:06:18,141 --> 01:06:23,278
eyes in my API. And then I'm gonna have a func,
1274
01:06:23,280 --> 01:06:28,883
which I'm gonna call pathForCircleCenteredAtPoint,
1275
01:06:28,885 --> 01:06:33,621
okay? It's going to take a midpoint, which is a CGPoint.
1276
01:06:33,623 --> 01:06:38,293
And I'm gonna have withRadius, which is gonna be a CGFloat.
1277
01:06:38,295 --> 01:06:42,831
And it's gonna return a UIBezierPath. So
1278
01:06:42,833 --> 01:06:44,199
this is gonna be a utility function,
1279
01:06:44,201 --> 01:06:47,936
okay? It's private actually, make sure we get this private.
1280
01:06:47,938 --> 01:06:50,238
And this is private also.
1281
01:06:50,240 --> 01:06:53,641
Okay, these two are actually private. I'm gonna try and
1282
01:06:53,643 --> 01:06:57,946
get in the habit of actually putting my privates in here so
1283
01:06:57,948 --> 01:07:00,382
that we get the, our things proper.
1284
01:07:00,384 --> 01:07:05,120
This is properly public, okay? So this function is just going
1285
01:07:05,122 --> 01:07:08,690
to take a center point and a radius and give us a Bezier
1286
01:07:08,692 --> 01:07:10,859
path, exactly the same thing we're doing here. So
1287
01:07:10,861 --> 01:07:14,362
I'm actually even just gonna copy this, cut it actually,
1288
01:07:14,364 --> 01:07:19,467
and put it here. Okay, so we got that.
1289
01:07:19,469 --> 01:07:20,935
And the center is not the skullCenter,
1290
01:07:20,937 --> 01:07:24,506
it's the midPoint. And the radius is not the skullRadius,
1291
01:07:24,508 --> 01:07:28,076
it's this withRadius. Now let's just kind of point out
1292
01:07:28,078 --> 01:07:31,379
this weirdness of having something that reads nice when
1293
01:07:31,381 --> 01:07:35,116
you call it, because when I call this down here I'm gonna
1294
01:07:35,118 --> 01:07:37,619
say pathForCircle with CenteredAtPoint,
1295
01:07:37,621 --> 01:07:40,955
the skull's center, withRadius the skull's radius.
1296
01:07:40,957 --> 01:07:44,092
That reads really nicely, like English. Okay, got this nice
1297
01:07:44,094 --> 01:07:47,062
preposition in here. But when I'm inside my code,
1298
01:07:47,064 --> 01:07:50,265
it's kind of weird that this is called my withRadius.
1299
01:07:50,267 --> 01:07:52,200
Okay, that doesn't really make sense.
1300
01:07:52,202 --> 01:07:54,869
I really want this to be called radius inside.
1301
01:07:54,871 --> 01:07:57,038
So this is where I'm gonna have the internal name and
1302
01:07:57,040 --> 01:08:02,610
the external name that we talked about. See that? Okay,
1303
01:08:02,612 --> 01:08:06,247
so that's all that this is gonna do. I'm not gonna return
1304
01:08:06,249 --> 01:08:09,084
it directly though. I'm gonna let the path equal this and
1305
01:08:09,086 --> 01:08:11,886
then I'm going to use this to set my lineWidth as well.
1306
01:08:11,888 --> 01:08:15,023
In fact, let's just cut and paste this as well up here.
1307
01:08:15,025 --> 01:08:19,861
So I'm gonna have all my lineWidths be the same. Okay,
1308
01:08:19,863 --> 01:08:22,764
this is probably something I want to make a var out of,
1309
01:08:22,766 --> 01:08:23,431
just like I have the scale,
1310
01:08:23,433 --> 01:08:25,967
probably want the lineWidth also to be settable, but for
1311
01:08:25,969 --> 01:08:28,636
speed we'll keep going here. And then we're gonna return
1312
01:08:28,638 --> 01:08:33,808
that path. Okay, got that? Make sense? By the way,
1313
01:08:33,810 --> 01:08:36,778
when we have a really long line right here that wraps,
1314
01:08:36,780 --> 01:08:40,081
one way that we can kind of make it look nicer is just put
1315
01:08:40,083 --> 01:08:46,154
every argument on its own line. Like that,
1316
01:08:46,156 --> 01:08:49,090
okay? Just a little more readable that way when it's
1317
01:08:49,092 --> 01:08:51,192
really long. So I've got this right here, and
1318
01:08:51,194 --> 01:08:54,329
I've replaced this skull thing with this, okay? By the way,
1319
01:08:54,331 --> 01:08:57,765
I don't really use skull, except for to do this stroke.
1320
01:08:57,767 --> 01:09:04,038
So let's take this, cut it out of here, and put it here,
1321
01:09:05,075 --> 01:09:08,710
okay? Everyone see what I did there?
1322
01:09:08,712 --> 01:09:13,314
I'm just chaining calling this and then calling stroke on it.
1323
01:09:13,316 --> 01:09:15,483
And it's pretty obvious this is my skull,
1324
01:09:15,485 --> 01:09:17,252
because I got skullCenter, skullRadius.
1325
01:09:17,254 --> 01:09:20,155
Okay, so now I have this path for circle, I can use that to
1326
01:09:20,157 --> 01:09:23,591
draw my eyes as well. So I'm gonna add another private func
1327
01:09:23,593 --> 01:09:26,995
here called get eye, what did we call it, pathForEye,
1328
01:09:26,997 --> 01:09:28,730
pathForEye and it's gonna take an eye,
1329
01:09:28,732 --> 01:09:33,168
which is of type Eye and it's gonna return a UIBezierPath.
1330
01:09:33,470 --> 01:09:35,737
Okay, so this is gonna get an eye, either the left eye or
1331
01:09:35,739 --> 01:09:37,839
the right eye depending on what this argument here is
1332
01:09:37,841 --> 01:09:42,777
right here. And to do that I need the eyeRadius.
1333
01:09:42,779 --> 01:09:44,179
So we'll have the eyeRadius,
1334
01:09:44,181 --> 01:09:47,248
which is going to be equal to the skull's radius,
1335
01:09:47,250 --> 01:09:50,518
divided by one of these ratios up here, okay, in fact,
1336
01:09:50,520 --> 01:09:56,357
this ratio right here. So we'll say Ratio., Ratios.,
1337
01:09:56,359 --> 01:10:01,062
this one is SkullRadiusToEyeRadius, okay?
1338
01:10:01,064 --> 01:10:05,800
And then I'm gonna let the eyeCenter equal the I'm gonna
1339
01:10:05,802 --> 01:10:09,571
actually call a function here, getEyeCenter for
1340
01:10:09,573 --> 01:10:12,540
the given eye. Okay, and so let's do that.
1341
01:10:12,542 --> 01:10:17,679
Private func, getEyeCenter for given eye. And
1342
01:10:17,681 --> 01:10:22,116
that's gonna return a CGPoint which is gonna be the center.
1343
01:10:23,019 --> 01:10:26,721
All right, so how do I get the eyes centered here?
1344
01:10:26,723 --> 01:10:31,726
That's pretty straightforward. Here I'm just going to say,
1345
01:10:31,728 --> 01:10:34,596
what did I decide to do here on that one? Yeah, so
1346
01:10:34,598 --> 01:10:39,667
I'm gonna let the eyeOffset = skullRadius
1347
01:10:39,669 --> 01:10:43,738
/ Ratios.SkullRadiusToEyeOffset.
1348
01:10:43,740 --> 01:10:46,708
Okay, so I'm just, see how I'm just doing all the positioning
1349
01:10:46,710 --> 01:10:48,943
of everything based on the skullRadius and
1350
01:10:48,945 --> 01:10:50,778
the skullCenter? So
1351
01:10:50,780 --> 01:10:54,182
then I'm gonna let the eyeCenter = skullCenter.
1352
01:10:54,184 --> 01:10:56,651
So we'll start with the eye being right in the middle
1353
01:10:56,653 --> 01:10:59,053
of our face, and then I'm gonna move it up and over,
1354
01:10:59,055 --> 01:11:03,558
right? So first of all let's move it up by saying that
1355
01:11:03,560 --> 01:11:09,130
the eyeCenter.y -= because y minus is up, plus is down for
1356
01:11:09,132 --> 01:11:13,468
the y-axis, the y offset. So I'm gonna move it up by the y
1357
01:11:13,470 --> 01:11:18,673
offset. And then depending on which eye it is, okay, if it's
1358
01:11:18,675 --> 01:11:23,544
the left eye I'm gonna move the eyeOffset.x to the left,
1359
01:11:23,546 --> 01:11:27,382
okay, minus, oops, this is eyeCenter. And actually
1360
01:11:27,384 --> 01:11:30,451
this should be eyeCenter also. We're gonna move the eyeCenter
1361
01:11:30,453 --> 01:11:33,087
to the left to by the eyeOffset.
1362
01:11:33,089 --> 01:11:34,555
And if it's the right eye,
1363
01:11:34,557 --> 01:11:40,361
then I'm gonna move the eye to the right by the eyeOffset.
1364
01:11:41,598 --> 01:11:45,700
Okay, so that tells where our eye is. So I'm gonna return
1365
01:11:45,702 --> 01:11:50,571
the eyeCenter here. Okay, everyone got that, where the,
1366
01:11:50,573 --> 01:11:54,876
where the eye is? So we have a warning here, what is it?
1367
01:11:54,878 --> 01:11:59,614
Let var, okay, so this needs to be var Or no, this needs to
1368
01:11:59,616 --> 01:12:03,217
be a var, I guess. This needs to be a let, okay, and
1369
01:12:03,219 --> 01:12:06,988
the lets and vars are right, yes. Okay, so here we're gonna
1370
01:12:06,990 --> 01:12:09,090
get the center of the eye using that function, so
1371
01:12:09,092 --> 01:12:12,727
we have the radius and the center, for the eye. So,
1372
01:12:12,729 --> 01:12:18,399
now we can just return, return a path for
1373
01:12:18,401 --> 01:12:23,237
a circle centered at point, the eyeCenter withRadius
1374
01:12:23,239 --> 01:12:28,209
the eyeRadius. Okay, so we have this
1375
01:12:28,211 --> 01:12:31,212
path for eye, now we just need to stroke this path. So we're
1376
01:12:31,214 --> 01:12:37,218
gonna say pathForEye, for eye, let's do the left eye.
1377
01:12:37,220 --> 01:12:41,923
And stroke that. And then path for I. Lets' do the right I,
1378
01:12:41,925 --> 01:12:45,026
and stroke that, okay? Notice, by they way,
1379
01:12:45,028 --> 01:12:48,863
I can say .left here. I don't have to say i.left.because
1380
01:12:48,865 --> 01:12:53,601
it's going infer that path for I is going to be an I,
1381
01:12:53,603 --> 01:12:57,038
okay? Everybody got that? Success?
1382
01:12:57,040 --> 01:13:01,943
So hopefully now when we draw we'll get some eyes on our
1383
01:13:01,945 --> 01:13:06,614
face. Didn't quite work,
1384
01:13:06,616 --> 01:13:09,984
okay? So, let's close what happened there.
1385
01:13:09,986 --> 01:13:11,853
We got one eye, the left eye looks good, but
1386
01:13:11,855 --> 01:13:16,023
the right eye is kind of stuck in the center. So why is that?
1387
01:13:16,025 --> 01:13:19,293
Let's look here, well, looks to me like I moved the, for
1388
01:13:19,295 --> 01:13:23,631
the right eye, I moved the center y back to this,
1389
01:13:23,633 --> 01:13:25,233
where it was before, see I moved there, so
1390
01:13:25,235 --> 01:13:32,573
this really needs to be the x, see that? It's over on that.
1391
01:13:32,575 --> 01:13:36,778
Okay, bingo. Got a face,
1392
01:13:36,780 --> 01:13:39,747
can rotate him, it's looking good. So now we did the mouth,
1393
01:13:39,749 --> 01:13:42,984
okay, let's see if we have time for the mouth, yeah,
1394
01:13:42,986 --> 01:13:45,486
we might, we'll get started on the mouth anyway. So
1395
01:13:45,488 --> 01:13:48,322
here I'm doing the same thing, private funk, path for
1396
01:13:48,324 --> 01:13:51,292
mouth this time, okay, there's only one mouth, and
1397
01:13:51,294 --> 01:13:55,229
it's going to return UI Bezier Path okay,
1398
01:13:55,231 --> 01:13:57,799
and then we're gonna do the same thing down here.
1399
01:13:57,801 --> 01:14:00,802
We're gonna say pathForMouth().stroke.
1400
01:14:00,804 --> 01:14:01,969
Okay, so how are we gonna do the mouth?
1401
01:14:01,971 --> 01:14:05,273
I'm gonna have to use a bezier curve to do the mouth.
1402
01:14:05,275 --> 01:14:07,108
How many people know what a bezier curve is?
1403
01:14:07,110 --> 01:14:09,377
Raise your hand if you know. Almost nobody. Okay, So
1404
01:14:09,379 --> 01:14:12,580
Bayesian curve is just a line you draw between two points,
1405
01:14:12,582 --> 01:14:15,383
but you have two control points somewhere, where,
1406
01:14:15,385 --> 01:14:20,254
it tries to draw a tangent line to the control point and
1407
01:14:20,256 --> 01:14:20,988
start the curve on there.
1408
01:14:20,990 --> 01:14:23,558
And then draw the tangent line to the other control point and
1409
01:14:23,560 --> 01:14:26,828
tries to draw another one there, so you can make curves
1410
01:14:26,830 --> 01:14:28,329
Okay, using these little control points.
1411
01:14:28,331 --> 01:14:30,598
So I have to determine the start and the end, and
1412
01:14:30,600 --> 01:14:31,532
the two control points.
1413
01:14:31,534 --> 01:14:35,470
So first I'm gonna create a rectangle to contain my mouth
1414
01:14:35,472 --> 01:14:40,208
here. I need these mouth ratios, so the mouth width,
1415
01:14:40,210 --> 01:14:43,044
height, and offset, I'm gonna make as ratios to the skull
1416
01:14:43,046 --> 01:14:46,280
radius, okay? Save a little time by doing that. And
1417
01:14:46,282 --> 01:14:51,919
then I'm gonna make the The rectangle changes them out by
1418
01:14:51,921 --> 01:14:58,726
equal a and this I'm going to use the X,
1419
01:14:58,728 --> 01:15:03,431
Y width height intizler here. So
1420
01:15:03,433 --> 01:15:08,503
the X is just the center. .x- mouthWidth/2,
1421
01:15:08,505 --> 01:15:12,840
okay? So my mouth is going to be whatever the mouthWidth is
1422
01:15:12,842 --> 01:15:17,712
from the skullCenter. Move it over. And the y is similarly
1423
01:15:17,714 --> 01:15:20,848
going to be the skullCenter.y +,
1424
01:15:20,850 --> 01:15:25,119
actually The mouth off set, because we're going to put
1425
01:15:25,121 --> 01:15:27,054
the mouth down below the center of the face, so
1426
01:15:27,056 --> 01:15:30,491
we're going to do it down. The width is just the mouth width,
1427
01:15:30,493 --> 01:15:33,861
and the height is the mouth height, okay, and just to show
1428
01:15:33,863 --> 01:15:36,731
you what this is like, I'm going to create a rectangle,
1429
01:15:36,733 --> 01:15:40,768
UI Bezier Path has a constructor which creates
1430
01:15:40,770 --> 01:15:45,339
a rectangle using mouth rect. Okay, so let's run that,
1431
01:15:45,341 --> 01:15:50,478
let's return that actually. Okay, that's the path
1432
01:15:50,480 --> 01:15:52,580
we're going to use, we're just going to do a rectangle first,
1433
01:15:52,582 --> 01:15:58,019
I'm not going to do those control point things yet. So
1434
01:15:58,021 --> 01:15:59,353
our rect is going to be a rectangle, so
1435
01:15:59,355 --> 01:16:01,222
this is where we're going to do the mouth, if it's smiling,
1436
01:16:01,224 --> 01:16:03,891
it will be down here, if it's frowning, the mouth is going
1437
01:16:03,893 --> 01:16:06,494
to be up here. Okay, so we are going to put this here.
1438
01:16:06,496 --> 01:16:09,864
This is going to be the start point of my busy path.
1439
01:16:09,866 --> 01:16:10,798
This is going to be the end point.
1440
01:16:10,800 --> 01:16:13,668
I'm going to put one control point here and one control
1441
01:16:13,670 --> 01:16:16,337
point here for a full smile. And so it going to start
1442
01:16:16,339 --> 01:16:19,774
heading down towards this control point bottom out. And
1443
01:16:19,776 --> 01:16:22,510
then start heading towards the tangent line between here and
1444
01:16:22,512 --> 01:16:25,546
this one. So, that's what is going to make a smile. Okay?
1445
01:16:25,548 --> 01:16:29,183
Don't worry about it too much if you don't know Bézier path.
1446
01:16:29,185 --> 01:16:29,984
So here, I also,
1447
01:16:29,986 --> 01:16:33,254
I'm gonna type this in really fast just to save
1448
01:16:33,256 --> 01:16:37,291
some time here. Most important thing is this mouth curvature.
1449
01:16:37,293 --> 01:16:40,661
This is just a double, which is somewhere between -1,
1450
01:16:40,663 --> 01:16:44,265
which is a full front, and 1, which is a full smile.
1451
01:16:44,267 --> 01:16:48,235
Okay and so the smile offset, first I'm gonna make sure
1452
01:16:48,237 --> 01:16:51,739
it s between one and
1453
01:16:51,741 --> 01:16:55,309
going to be that one to minus one times the mouth's height.
1454
01:16:55,311 --> 01:16:57,945
That's why I'm going to put my control point all that way day
1455
01:16:57,947 --> 01:17:03,184
at the bottom if the smile is one All right, so here so
1456
01:17:03,186 --> 01:17:06,020
here I'm gonna start at the mouth's upper left. I'm gonna
1457
01:17:06,022 --> 01:17:09,256
end at mouth upper right, okay? My first control point
1458
01:17:09,258 --> 01:17:12,593
is a third of the way along the bottom. And the second
1459
01:17:12,595 --> 01:17:16,197
control is a third of the way on the other side, okay?
1460
01:17:16,199 --> 01:17:19,033
And on the bottom, wherever the smileOffset
1461
01:17:19,035 --> 01:17:21,469
puts it. Okay, so in full smile this is gonna be along
1462
01:17:21,471 --> 01:17:25,306
the bottom of the Rect. Could be up above the rack. Okay, so
1463
01:17:25,308 --> 01:17:28,776
I've got that so now I'm just gonna return UIBEzier path
1464
01:17:28,778 --> 01:17:31,078
here that incorporates all that.
1465
01:17:31,080 --> 01:17:36,317
Let's actually do this let's say let UIBEzier path. I'm
1466
01:17:36,319 --> 01:17:41,255
just gonna create a blank one then I'm gonna tell the path
1467
01:17:41,257 --> 01:17:44,258
that to move to the start. Move to our starting point
1468
01:17:44,260 --> 01:17:48,262
which is start, that's this thing right here. Okay then
1469
01:17:48,264 --> 01:17:51,632
I'm gonna create a bezierPath or bezierCurve which is add
1470
01:17:51,634 --> 01:17:55,403
curve to point that's the way you add a bezierCurve
1471
01:17:55,405 --> 01:17:58,839
to a path it goes to the end point which is this right
1472
01:17:58,841 --> 01:18:03,878
here, and here's my 2 control points cp1 and cp2.
1473
01:18:03,880 --> 01:18:08,082
Okay, that's these two guys. All right, so I've done that.
1474
01:18:08,084 --> 01:18:11,252
I'm gonna set the line width equal to 5.0 and
1475
01:18:11,254 --> 01:18:16,190
return the path. Okay? So let's see if that works,
1476
01:18:16,192 --> 01:18:19,527
we have our mouth here set to be 0, which is not a smile or
1477
01:18:19,529 --> 01:18:22,763
a frown, it should be just kind of straight line Cuz our
1478
01:18:22,765 --> 01:18:25,833
control points are gonna be right along the line sure
1479
01:18:25,835 --> 01:18:29,804
enough this is not a very happy camper let's make them
1480
01:18:29,806 --> 01:18:30,971
very sad.
1481
01:18:39,082 --> 01:18:42,149
Okay aw that looks very sad but I don't you saying I don't
1482
01:18:42,151 --> 01:18:43,784
wanna sending you guys out of here sad so
1483
01:18:43,786 --> 01:18:52,259
we're gonna go to full happy here. There it is. Very happy.
1484
01:18:52,261 --> 01:18:54,562
Okay. Now, one thing I'm gonna do, last thing I'm gonna do
1485
01:18:54,564 --> 01:18:57,598
before we leave is this mouth curvature is currently a local
1486
01:18:57,600 --> 01:19:00,267
variable inside this private method. I'm actually gonna
1487
01:19:00,269 --> 01:19:04,038
make this public. Okay, so I'm just gonna take it out of here
1488
01:19:04,040 --> 01:19:06,741
I put my public bars up here at the front,
1489
01:19:06,743 --> 01:19:09,677
loose scale. Put it there, I'm gonna make it a var so
1490
01:19:09,679 --> 01:19:13,347
it's public. So now other people using my face view can
1491
01:19:13,349 --> 01:19:17,184
set how happy it is, right? Cuz I've made this public so
1492
01:19:17,186 --> 01:19:18,252
they can set whether it's a smile or
1493
01:19:18,254 --> 01:19:21,322
a frown. And that's gonna be really valuable next week when
1494
01:19:21,324 --> 01:19:24,658
we stat having multiple NVCs cuz we're gonna wanna be able
1495
01:19:24,660 --> 01:19:27,161
to use other NVCs to show things,
1496
01:19:27,163 --> 01:19:29,930
okay? So just to show this still works,
1497
01:19:29,932 --> 01:19:35,970
the mouth curvature is still a full smile. There we go.
1498
01:19:36,405 --> 01:19:39,006
Okay we'll pick this up next week.
1499
01:19:40,443 --> 01:19:40,808
>> For more
1500
01:19:40,810 --> 01:19:40,841
please visit us at Stanford.edu.
================================================
FILE: subtitles/5. Interface Builder, FaceView Controller, Gestures, and Multiple MVCs.srt
================================================
1
00:00:00,001 --> 00:00:03,836
[MUSIC]
[MUSIC]
2
00:00:03,838 --> 00:00:06,305
Stanford University. >> All right,
斯坦福大学. >> 好的,
3
00:00:06,307 --> 00:00:12,177
well then welcome to lecture 5, of CS193P,
欢迎来到 CS193P 的第五课,
4
00:00:12,179 --> 00:00:16,015
this is the spring of 2016. And
现在是 2016 年春。
5
00:00:16,017 --> 00:00:21,420
today we are going to, first, work a little more and
今天我们首先要做的是在之前的工程中增加一点功能,然后
6
00:00:21,422 --> 00:00:24,957
face it. I'm gonna show you a little kind of some cool
看看效果。 我将会为你展示一些 Xcode 可视化界面中
7
00:00:24,959 --> 00:00:27,359
features in the interface builder part of Xcode,
很酷的特性,
8
00:00:27,361 --> 00:00:30,662
that's the part where we're building our storyboard.
这部分内容我们会在 Storyboard 上完成。
9
00:00:30,931 --> 00:00:34,033
Then we're gonna start talking about what FACEIT's
然后我们将开始讨论如何正确实现 FACEIT 中的
10
00:00:34,035 --> 00:00:36,802
view controllers model really should be.
视图、控制器和模型。
11
00:00:36,804 --> 00:00:39,638
Okay, cuz right now if you look at the code in our
好的, 现在如果你看了 FACEIT 中
12
00:00:39,640 --> 00:00:42,307
controller of our MVC for FACEIT. It's blank,
控制器的代码。 你会发现它是空白的,
13
00:00:42,309 --> 00:00:44,743
there's nothing in there. So we need to do some work there.
什么都没有。我们需要在上面完成一些工作。
14
00:00:44,745 --> 00:00:48,113
Now we're gonna come back to the slides and I'm gonna talk
现在先回到幻灯片上来
15
00:00:48,115 --> 00:00:51,283
about gestures, okay which is the input side of UIView.
我要讲解手势的相关知识点, 它是 UIView 的输入端。
16
00:00:51,285 --> 00:00:54,453
And I'll do a demo where we put some gestures in FACEIT.
我会演示一个在 FACEIT 上添加手势的 Demo。
17
00:00:54,455 --> 00:00:57,456
And then time permitting I'm gonna end with some more
然后如果时间允许的话,
18
00:00:57,458 --> 00:01:00,225
slides starting to talk about multiple MVCs which is
我会在幻灯片上讲解多重 MVC
19
00:01:00,227 --> 00:01:04,229
the gateway to building bigger applications. Okay, so that's
它是构建更大型的应用的入口。 好吧
20
00:01:04,231 --> 00:01:07,499
what's on tap for today. So let's dive right into the demo
以上就是今天的主要内容。 让我们直接进入 demo 环节
21
00:01:07,501 --> 00:01:10,702
here. I'm gonna do these two things I talked about,
我要开始做我刚刚谈到的那两件事,
22
00:01:10,704 --> 00:01:15,941
which is the Interface Builder. And a model for
一个是界面生成器。 另一个是
23
00:01:15,943 --> 00:01:16,508
our FaceViewController,
我们的 FaceViewController 的模型,
24
00:01:16,510 --> 00:01:18,243
which is gonna be a facial expression,
正常来说这个模型要描述一个面部表情。
25
00:01:18,245 --> 00:01:22,548
not surprisingly. All right, so here we are in FaceView,
好的,现在我们来到 FaceView,
26
00:01:22,550 --> 00:01:26,285
this is where we left off last time. I'm gonna quickly
这是我们上节课最后停留的部分。
27
00:01:26,287 --> 00:01:29,855
enhance our FaceView to be a little cooler right now if you
我会快速地强化 FaceView 的功能,让它变得更酷。
28
00:01:29,857 --> 00:01:33,559
remember from last time our FaceView has a mouth which we
如果你还记得上一次我们对 FaceView 上的嘴巴
29
00:01:33,561 --> 00:01:37,029
can configure a little bit. We can make it smile and
进行了一些配置。 我们可以让它微笑、
30
00:01:37,031 --> 00:01:40,466
frown and then it has two eyes, okay. And I'm gonna add
皱眉并且拥有两只眼睛,很不错。我现在要增加两样东西,
31
00:01:40,468 --> 00:01:43,368
two things, one I'm gonna make it so he can close those eyes.
首先我可以让它闭上眼睛。
32
00:01:43,370 --> 00:01:46,505
And another thing I'm gonna do is, I'm gonna add eyebrows.
我要做的另外一件事是:给它加上眉毛。
33
00:01:46,507 --> 00:01:49,208
Okay, now I'm gonna do this really fast because the code
我完成起来非常地快,
34
00:01:49,210 --> 00:01:51,577
that it takes me to do this really wouldn't learn that
因为我所需要的代码不需要对现有的的代码做较大改动,
35
00:01:51,579 --> 00:01:53,879
much from going through it, but it's pretty just more,
只需要新增一些代码,
36
00:01:53,881 --> 00:01:56,081
pretty much more of the same of the stuff women doing.
跟女人画眉毛相比要省事得多。
37
00:01:56,083 --> 00:02:01,120
So I'm gonna add a couple of more public vars because
因此我需要定义一组新的公有变量,
38
00:02:01,122 --> 00:02:03,122
these are gonna be configurable thing. One is
因为有一些东西需要可配置。
39
00:02:03,124 --> 00:02:06,758
whether the eyes are open, which is gonna be a bull. And
一个属性表示眼睛是否是睁开的, 这是一个布尔变量。
40
00:02:06,760 --> 00:02:10,462
I'm gonna say these are gonna start out open, okay?
我将把眼睛的初始状态设置为睁开,怎么样?
41
00:02:10,464 --> 00:02:13,398
So it's just gonna be the default. And then I'm also
true 是它的默认值。 然后我定义另一个新属性
42
00:02:13,400 --> 00:02:16,568
going to have something called eyebrowTilt, okay?
取名为 eyebrowTilt, 怎么样?
43
00:02:16,570 --> 00:02:20,272
Which is gonna be a double. And I'm gonna start it at 0.0.
这是一个 double 类型的变量. 初始值设置为 0.0。
44
00:02:20,274 --> 00:02:23,575
It's similar to the kind of the mouth curvature. So
它的定义类比于嘴巴弯曲的弧度。
45
00:02:23,577 --> 00:02:26,979
we're gonna have -1 be full furrow.
我们让默认曲率为 0。
46
00:02:26,981 --> 00:02:31,016
And one will be fully relaxed eyebrows, okay?
-1代表很郁闷,1代表完全放松的眉毛, 怎么样?
47
00:02:31,018 --> 00:02:35,187
So similar kind of thing as we had for the mouth curvature.
非常像我们对嘴巴的曲率做的设置。
48
00:02:35,189 --> 00:02:38,190
So let's do the eyes open first. Quite simple.
先来让眼睛睁开。很简单。
49
00:02:38,192 --> 00:02:42,094
In path for eye right now. You can see that we use a path for
在之前画眼睛的路径中, 你可以看到我们用一个圆的路径代表眼睛。
50
00:02:42,096 --> 00:02:44,429
circle, so that's why our eyes are circular. So
这解释了为什么眼睛是圆的。
51
00:02:44,431 --> 00:02:48,800
if my eyes are open, I'm just gonna continue to do that,
所以如果眼睛睁开的话, 我打算还这么做,
52
00:02:48,802 --> 00:02:50,035
okay? We'll do to pass the circle.
如何? 我们继续用圆来表示。
53
00:02:50,037 --> 00:02:54,206
But if the eyes are closed, then instead what I'm gonna do
但是如果眼睛是闭上的, 我会用另一个图形来表示
54
00:02:54,208 --> 00:02:58,877
is do this, which is basically use a line.
只使用一条线。
55
00:02:58,879 --> 00:03:00,546
So I'm just gonna draw a horizontal line,
如果眼睛是闭合的话我会画一条水平的线。
56
00:03:00,548 --> 00:03:03,815
if the eyes are closed. Okay? Very simple. Here, BezierPath,
非常简单。看这里, BezierPath,
57
00:03:03,817 --> 00:03:07,786
move to a point, draw a line. Okay? And then for the brows,
移动到一个点上,画一条线。然后画眉毛,
58
00:03:07,788 --> 00:03:10,422
you see, you know we have path for eye, we have path for
如你所见,我们有眼睛的路径,
59
00:03:10,424 --> 00:03:15,093
mouth, so I'm just gonna make a path for brows. Which I
有嘴巴的路径,所以我仅仅需要为眉毛画一个路径
60
00:03:15,095 --> 00:03:19,198
think I have here, yes. So here's our path for brow and
我想我可以用这里的代码!现在这就是我们的眉毛的路径
61
00:03:19,200 --> 00:03:22,167
it's pretty straightforward. It's just drawing a line but
它很简单. 画了一条直线
62
00:03:22,169 --> 00:03:28,207
this line is tilted. Depending on how much tilt we want.
这条线是倾斜的。 倾斜程度取决于我们设置的斜率
63
00:03:28,209 --> 00:03:32,077
And of course we need a little func a little static here to
当然了我们还需要向比例中增加一些静态变量
64
00:03:32,079 --> 00:03:36,815
go up in our ratios. Which is the relative position of
代表眉毛相对于旋转中心点的偏移距离
65
00:03:36,817 --> 00:03:40,285
the brow to the scroll radius, just like we have all these
就像我们设置的
66
00:03:40,287 --> 00:03:44,089
other things that are relative to the radius. Okay? And
其他与半径相关的信息。
67
00:03:44,091 --> 00:03:46,892
now that we have pathForBrow, just like we did with
现在我们有了方法 pathForBrow, 就像定义
68
00:03:46,894 --> 00:03:48,594
pathForEye and pathForMouth, we'll go ahead and
pathForEye 和 pathForMouth 那样, 让我们继续
69
00:03:48,596 --> 00:03:52,831
stroke those. So I'll say pathFor, Brow, the left one,
绘制它们。 我要绘制的路径是, 眉毛, 左边那条,
70
00:03:52,833 --> 00:03:58,270
stroke, and then we'll do a pathForBrow, the right one,
绘制, 然后绘制右边的眉毛。
71
00:03:58,272 --> 00:04:01,073
and stroke. Okay?
大功告成。
72
00:04:01,075 --> 00:04:03,408
So let's go ahead and run, see what we'll look,
让我们继续并且运行程序, 看看我们能看到什么,
73
00:04:03,410 --> 00:04:08,280
we should have eyebrows now. Okay,
现在我们应该有眉毛了。 好的,
74
00:04:08,282 --> 00:04:10,949
yeah, look we got eyebrows, they're kinda cute. And
耶, 看我们有眉毛了, 它们有一点可爱。
75
00:04:10,951 --> 00:04:12,384
we should also be able to close the eyes.
我们应该也可以让眼睛闭上了。
76
00:04:12,386 --> 00:04:16,421
So I'm gonna change this eyesOpen to false.
所以我要把 eyesOpen 设为 false。
77
00:04:18,559 --> 00:04:22,494
See if that works, it does, eyes closed.
看下有没有起作用,起作用了, 眼睛闭上了。
78
00:04:22,496 --> 00:04:26,298
We could set our eyebrowTilt maybe to I don't know -0.5.
我们可以把 eyebrowTilt 设置成..嗯..就-0.5吧。
79
00:04:26,300 --> 00:04:33,538
See what that does. Okay,
看下效果。好的,
80
00:04:33,540 --> 00:04:36,775
we have some tilt there. Okay, so you can see with eyebrows,
增加了倾斜的效果. 所以你看到了眉毛的效果,
81
00:04:36,777 --> 00:04:38,510
we can make more facial expressions.
我们可以做出更多的面部表情。
82
00:04:38,512 --> 00:04:39,011
That's why I put him in there.
这就是我把他放在这的原因。
83
00:04:39,013 --> 00:04:42,247
So we'd have a little more room to maneuver when it
当涉及到面部表情时
84
00:04:42,249 --> 00:04:43,048
comes to facial expressions.
我们有更多可操作的选项。
85
00:04:43,050 --> 00:04:46,318
I'm gonna do a couple other things here. One is, there's
我要在这设置更多的选项。 其中一个可以是
86
00:04:46,320 --> 00:04:51,290
some constants I have in here like my face is always blue.
我在这里定义的一些常量,比如颜色总是蓝色的。
87
00:04:51,292 --> 00:04:53,959
I don't really want that. So I'm gonna make that be a var,
我不希望这样。 所以我把它改成一个变量,
88
00:04:53,961 --> 00:04:57,029
so I'm gonna call it color. So I'm gonna go up here and
我准备给变量取名为 color。 我在这里定义一个变量 Color
89
00:04:57,031 --> 00:05:01,967
make a var. Color, which is gonna be a UIColor.
它的类型是 UIColor。
90
00:05:01,969 --> 00:05:06,371
And I'm gonna let it be blue by default. Whoops UIColor,
并且我设置它的默认值是蓝色。 唔 UIColor,
91
00:05:06,373 --> 00:05:10,909
blueColor by default. And I'm also gonna do a var for
默认是 blueColor。然后我也会把线的宽度定义为一个变量
92
00:05:10,911 --> 00:05:14,579
the linewidth, okay? Currently, our linewidth is
怎么样? 现在, 我们的线宽一直是 0.5
93
00:05:14,581 --> 00:05:18,617
always 5.0, but I'm gonna, and we'll make the default 5.0.
所以我把默认值设成 5.0.
94
00:05:18,619 --> 00:05:22,454
But I'm gonna change all those 5.0s with this var, so let's
但是我会把所有用到 5.0 这个常量的地方替换成这个变量,
95
00:05:22,456 --> 00:05:28,994
do that. There's 1, 2, 3, 4. Okay, that's our four things.
让我们完成这个工作。 一共有 1, 2, 3, 4. 好的, 我们替换了四处.
96
00:05:28,996 --> 00:05:33,298
Eyebrows, eyes, our mouth, and the skull. Okay? So now we've
眉毛, 眼睛, 我们的嘴, 和骨头. okay?
97
00:05:33,300 --> 00:05:37,402
kind of made our faceView quite externally usable, okay.
我们增强了 faceView 的外部调用性, 好的。
98
00:05:37,404 --> 00:05:40,772
All these public functions are something that a controller
所有这些公开的方法都是一个控制器
99
00:05:40,774 --> 00:05:44,443
is likely to use. Because we're part of the view, okay.
可能用到的。 因为我们都是视图的一部分, okay.
100
00:05:44,445 --> 00:05:48,280
We're a generic face-drawing view, and so we can be used by
我们是一个通用的脸部绘制视图, 所以我们可以被
101
00:05:48,282 --> 00:05:52,084
any controller that might want to draw a face. Okay? Which is
任何想要绘制面部的控制器所使用。 Okay?
102
00:05:52,086 --> 00:05:53,985
the kind of controller we happen to have now.
哪一个是我们已经拥有了的控制器呢?
103
00:05:53,987 --> 00:05:54,619
It's drawing a face. But
这里绘制了一张脸。 不过
104
00:05:54,621 --> 00:05:57,222
if we look at our controller right here, it's drawing
如果我们查看这里我们自己的控制器的话,
105
00:05:57,224 --> 00:06:01,326
a face but doesn't really control that face much, okay?
控制器绘制一张脸但是对这张脸做任何配置, okay?
106
00:06:01,328 --> 00:06:04,563
It has a view that it has in its storyboard, where we put
它拥有了 storyboard 提供的视图,
107
00:06:04,565 --> 00:06:08,667
our face right here, but it's not really doing much. And
我们要把脸绘制在上面, 但是没有做其他的工作。
108
00:06:08,669 --> 00:06:12,037
we'll fix that in a minute. Now, one thing that's kind of
我们一分钟就能完成。 现在, 有一件令人苦恼的事情,
109
00:06:12,039 --> 00:06:15,140
annoying is if we look in our storyboard right here.
当我们查看我们的 storyboard。
110
00:06:15,142 --> 00:06:16,341
Okay, here's our storyboard right there.
Okay, 这里是我们的 storyboard。
111
00:06:16,343 --> 00:06:20,312
We don't see that face and okay we only have one
我们看不到我们绘制的脸,并且我们只有一个 MVC
112
00:06:20,314 --> 00:06:23,682
MVC here we know it's a face and the fact that it
我们知道它是一个脸,可是
113
00:06:23,684 --> 00:06:24,850
doesn't show up there,eh. But
我们看不到。但是
114
00:06:24,852 --> 00:06:27,886
what if we had like 20 view controllers and this is just,
如果我们有 20 个控制器,
115
00:06:27,888 --> 00:06:29,755
this was the face but there's a whole bunch of other ones.
这里会有一张脸,那里又会有一整套别的东西。
116
00:06:29,757 --> 00:06:32,958
Would be really cool if that face would show up here. Okay.
如果脸能在这里展示出来,岂不是很酷? Okay。
117
00:06:32,960 --> 00:06:34,626
So we can see it in interface build. And in fact,
等下我们会在可视化构造器上看到它。 实际上,
118
00:06:34,628 --> 00:06:38,330
that's really easy to do. All you do is you go to your view,
很容易办到。你需要做的就是到你的视图上,
119
00:06:38,332 --> 00:06:41,366
okay? Your custom UIView, faceView. And you just put
okay? 你自定一个 UIView, 取名 faceView.并且它的定义上面加上
120
00:06:41,368 --> 00:06:46,638
@IBdesignable at the front. And if you say @IBdesignable,
@IBdesignable 关键字。 如果你声明了 @IBdesignable,
121
00:06:46,640 --> 00:06:51,676
then interface's builder is automatically going to assume,
可视化构造器会认为你想要
122
00:06:51,678 --> 00:06:52,711
you want me to draw this thing.
绘制这个东西。
123
00:06:52,713 --> 00:06:54,579
So you can see, it's even compiling it and drawing it.
所以你可以看到, 代码会被编译并且绘制。
124
00:06:54,581 --> 00:06:59,684
So now it appears, here in my storyboard, okay. So that's
在我的 storyboard 上,脸出现了。 okay。
125
00:06:59,686 --> 00:07:02,154
pretty cool. But one thing that's not so cool is, if I
这真的很酷。但是还有一件不太酷的事情,
126
00:07:02,156 --> 00:07:05,724
click on my faceView and bring up the inspector. I can only
如果我点击我的 faceView 并且打开检查器。
127
00:07:05,726 --> 00:07:08,593
inspect the attributes about my faceView that inherits from
我只能检查 faceView 从 View 继承来的属性
128
00:07:08,595 --> 00:07:11,663
view like its background color and things like that, right?
比如像它的背景色这样的属性, 没错吧?
129
00:07:11,665 --> 00:07:15,133
It'd be really cool if I could inspect things like eyebrow
如果我能检查像眉毛、最的曲率
130
00:07:15,135 --> 00:07:15,500
mouth curvature and the color and the scale. Wouldn't it be
以及颜色和比例的话那就很酷了。
131
00:07:15,502 --> 00:07:18,770
tilt and
这里是不是该倾斜
132
00:07:18,772 --> 00:07:20,572
cool if I could do all that in interface builder so
如果我能在可视化构造器中完成所有这些属性的设置那就太酷了。
133
00:07:20,574 --> 00:07:23,842
I could set up the face how I want it to initially look
也许我可以按照我的想法来初始化一张脸,
134
00:07:23,844 --> 00:07:27,245
maybe, interface builder and you can do that too.
可视化构造器可以实现。
135
00:07:27,247 --> 00:07:28,313
If you go back to faceView.
如果你回顾 faceView.
136
00:07:28,315 --> 00:07:33,151
All of these things that are right here, that are settable,
所有的这一切都在这里, 它们是可设置的,
137
00:07:33,153 --> 00:07:37,022
you can just put in front of them @IBInspectable.
你只需在它们前面加上 @IBInspectable。
138
00:07:37,024 --> 00:07:38,957
Okay, which means they will show up in the inspector.
Okay, 这代表该属性会被显示在观察器中。
139
00:07:38,959 --> 00:07:42,561
And so I am gonna make all of these things be inspectable,
因此我把所有的这些属性都设置为可观察的,
140
00:07:42,563 --> 00:07:47,432
put inspectable on all these guys. Okay. Could go into
给这些家伙们都加上 inspectable。 Okay. 现在可以回到
141
00:07:47,434 --> 00:07:50,068
interface builder. But if I go here and bring out my
可视化构造器中了。 你打开这里
142
00:07:50,070 --> 00:07:53,238
utilities now you can see I've still got all the view stuff.
你依旧能看到所有 View 相关的属性。
143
00:07:53,240 --> 00:07:55,774
But now I've got all these faceView things, so
但是现在我们拥有 faceView 中的东西了, 所以
144
00:07:55,776 --> 00:07:59,010
I can set my scale to 0.05. Okay, and that makes it small.
我可以把比例设置为 0.5. Okay, 这使得人脸变小了。
145
00:07:59,012 --> 00:08:02,814
Or I could set my color to orange. Okay,
或者我可以把颜色设置成橘黄色。 Okay,
146
00:08:02,816 --> 00:08:05,383
San Francisco Giants color, all right! I can make my line
洛杉矶巨人队的颜色。好吧! 我可以把线宽从5改为2
147
00:08:05,385 --> 00:08:09,254
width B2 instead of 5, where it's a little thinner, okay?
线就变细了, okay?
148
00:08:09,256 --> 00:08:13,925
I could set my mouth curvature in here, let's say -0.5.
在这里我可以设置嘴巴的曲率, 设置为 -0.5。
149
00:08:13,927 --> 00:08:18,196
All right, eyes open? On?
对把, 眼睛是否睁开? 睁开状态?
150
00:08:18,198 --> 00:08:22,234
There we go, okay, see that? 3.9 again.
让我们继续, okay,看到了吗? 比例变回0.9。
151
00:08:22,236 --> 00:08:25,103
All right, so that's kinda cool that we can do that.
好的, 我们做到了,这很酷。
152
00:08:25,105 --> 00:08:27,372
And, in fact, when we first run our application,
实际上, 当我们第一次运行程序,
153
00:08:27,374 --> 00:08:30,842
it will use these values. So let's go ahead and run and
它就会应用这些值。 让我们继续并且运行程序
154
00:08:30,844 --> 00:08:32,711
you're gonna see it's gonna keep this exact face.
你会看到屏幕上会显示相同的脸。
155
00:08:32,713 --> 00:08:35,981
Line, the color, the line thickness, all these things.
线条、颜色、线宽以及所有的这些属性。
156
00:08:35,983 --> 00:08:38,316
Just like if we had set any other attribute over here,
就像我们在检查器上设置的任何其他属性一样。
157
00:08:38,318 --> 00:08:40,819
background color or something like that. When we run,
比如背景颜色或者其他诸如此类的属性。 当然,当我们运行时
158
00:08:40,821 --> 00:08:43,889
of course, those would be the things it starts out with,
这些属性都会被初始化,
159
00:08:43,891 --> 00:08:46,758
okay? Now, one little thing to watch out for
okay? 现在, 有个小细节要注意
160
00:08:46,760 --> 00:08:50,428
when you're doing this, is [COUGH] these IBInspectable
当你要设置可变属性为 IBInspectable 时
161
00:08:50,430 --> 00:08:54,232
vars, you have to explicitly type them. You see, there,
你必须显式地声明它们的类型。 你看,像这里,
162
00:08:54,234 --> 00:08:56,434
most of these I wouldn't even need to type, right?
大部分我原本都不需要声明类型的, 对吧?
163
00:08:56,436 --> 00:09:00,071
Swift could infer that that's a double from there. But
Swift 可以通过赋值推断出 Double 类型。 但是
164
00:09:00,073 --> 00:09:00,906
if you're using the IBInspectable,
如果你正在使用 IBInspectable,
165
00:09:00,908 --> 00:09:03,975
you have to explicitly type, basically interface builder
你必须显式地写出类型, 因为可视化构造器不能推断它们的类型。
166
00:09:03,977 --> 00:09:07,379
can't infer it, Swift can, but interface builder can't.
Swift 可以,但是可视化构造器不能。
167
00:09:07,381 --> 00:09:09,748
So you need to make those explicit, okay.
所以你需要明确类型, okay。
168
00:09:09,750 --> 00:09:14,786
Just a minor deal there. Okay, now, one other thing I wanna
这只是一个小的需要注意的点。 Okay, 现在,另外一件我要说到的事情是
169
00:09:14,788 --> 00:09:19,224
talk about here with a view is if somebody sets the scale to
这里的视图,如果某个人设置了它的比例,
170
00:09:19,226 --> 00:09:22,093
something, okay, in the code. Like let's say our
okay, 在代码里面设置的。
171
00:09:22,095 --> 00:09:24,696
controller over here, let's say we have some code in here
控制器在这里, 我们在视图的代码中设置比例。
172
00:09:24,698 --> 00:09:29,100
to set the face view scale. It would actually do nothing.
实际上没有起到任何效果。
173
00:09:29,102 --> 00:09:32,404
Okay, nothing would change on screen, and why is that?
Okay,屏幕上不会有变化, 这是为什么呢?
174
00:09:32,406 --> 00:09:35,373
Well because when you set this scale to be 0.5,
因为当我们把比例设置为 0.5 时,
175
00:09:35,375 --> 00:09:39,511
all you did was set this var, you didn't do anything else,
你所做的只是修改了这个变量, 你没有做其他的事情,
176
00:09:39,513 --> 00:09:41,379
okay? So that var is set to 0.5 and
okay? 因此当变量变为 0.5
177
00:09:41,381 --> 00:09:44,616
if the face were to redraw, it would redraw smaller but
人脸的视图进行重绘, 它会被绘制的更小
178
00:09:44,618 --> 00:09:49,020
nothing is causing the face to redraw. So any time you have
但是重绘的事件没有被触发。 所以任何时候
179
00:09:49,022 --> 00:09:53,625
these public vars that you allow to be set, you need to
你允许这些公共的变量可以被修改的时候,
180
00:09:53,627 --> 00:09:57,562
make it so that when they change, the viewer redraws.
你需要在属性改变时重绘视图。
181
00:09:57,564 --> 00:10:00,298
Okay, now how are we gonna do that? Well we're gonna do that
Okay, 现在我们该怎么做? 我们用之前讲到的属性观察器
182
00:10:00,300 --> 00:10:03,935
using these property observers we talked about before.
来实现这个功能。
183
00:10:03,937 --> 00:10:07,172
Remember, the dil, didSet and the willSet? These are little
还记得 didSet 和 willSet 吗? 它们都是一小段代码
184
00:10:07,174 --> 00:10:10,475
pieces of code that get executed whenever this,
这种情况下每当变量被赋值或者访问的时候就会触发,
185
00:10:10,477 --> 00:10:15,246
in this case get set, okay? So if this got set to something
okay? 因此如果该变量被赋值
186
00:10:15,248 --> 00:10:19,117
then in didSet, I wanna re-draw, and how do I re-draw?
然后会进入 didSet 代码段, 我想要重绘, 那么我该如何重绘?
187
00:10:19,119 --> 00:10:24,756
Maybe I say self.drawRect do I do that? No,
也许我需要调用 self.drawRect 我要这么做吗? 不,
188
00:10:24,758 --> 00:10:27,926
I would fail this class if I did that cuz we never call
在类中这样做会失败, 因为我们永远都不能调用
189
00:10:27,928 --> 00:10:32,831
drawRect. We say instead setNeedsDisplay, okay,
drawRect。 我们说过使用 setNeedsDisplay 替代, okay,
190
00:10:32,833 --> 00:10:38,169
setNeedsDisplay is how we tell the system, please I need to
setNeedsDisplay 的作用是我们告诉系统, 我需要被重绘,谢谢。
191
00:10:38,171 --> 00:10:42,207
be redrawn, okay. And it will happen sometime in the future,
okay。 重绘会在未来的某个时刻执行,
192
00:10:42,209 --> 00:10:45,043
very close near future but sometime in the future.
非常接近的时刻,但是是在未来.
193
00:10:45,045 --> 00:10:48,380
Now, this is so common to do this. Usually we will take out
现在, 这种做法非常普遍。 通常我们会清除掉一些换行,
194
00:10:48,382 --> 00:10:51,349
a little bit of these character-turns here and
195
00:10:51,351 --> 00:10:55,987
put this all on the same line. Okay, like that. And
Okay, 就像这样。
196
00:10:55,989 --> 00:10:59,524
we're gonna put this after all of our little vars here.
我们要在所有的变量后面增加观察器。
197
00:10:59,526 --> 00:11:02,861
So I'm gonna put it in the place of the comment there.
它要占用注释的位置了。
198
00:11:02,863 --> 00:11:07,565
Put one there. The same thing here just so you can easily
在这里摆放一个。 这里也是相同的操作,你很容易发现
199
00:11:07,567 --> 00:11:11,302
see it cuz they're a little bit screen real estate
它会造成屏幕的空间有些挤。
200
00:11:11,304 --> 00:11:14,205
challenged here. All right, so now all of these vars,
好的, 所有的这些变量,
201
00:11:14,207 --> 00:11:17,475
if someone sets it, it's gonna cause this thing to redraw.
如果有人设置了它们, 就会引起重绘。
202
00:11:17,477 --> 00:11:18,777
What's really cool is if they set three or
如果变量中的三个或四个可以一次性地进行重绘那该多酷
203
00:11:18,779 --> 00:11:21,746
four of them right in a row, it's not gonna redraw three or
而不会引起三四次的重复绘制。
204
00:11:21,748 --> 00:11:23,782
four times. It's gonna keep saying,
它会不断地说,
205
00:11:23,784 --> 00:11:25,750
I need to be displayed, I need to be displayed,
我需要被显示,我需要被显示,
206
00:11:25,752 --> 00:11:25,817
I need to be displayed.
我需要被显示。
207
00:11:25,819 --> 00:11:28,553
But it's not gonna actually display until a little bit
但它不会真正地被显示,直到一段时间后
208
00:11:28,555 --> 00:11:30,822
later, and they'll all get displayed at once.
它们才会被一次性地重绘。
209
00:11:30,824 --> 00:11:33,458
So it's much more better performance to delay,
所以延迟可以提升性能,
210
00:11:33,460 --> 00:11:37,128
slightly, to do these things. That's not even the primary
对这种问题不要太过于重视,这不是主要原因。
211
00:11:37,130 --> 00:11:39,164
reason this is, does, it does it this way, but
实际上 setNeedsDisplay 方法本来就是这样设计的, 不过
212
00:11:39,166 --> 00:11:44,135
it's one of the reasons, okay? Everybody got that?
这只是其中的一个原因, okay? 每个人都理解了吗?
213
00:11:44,137 --> 00:11:46,438
Okay, so we're almost always gonna use this didSet.
Okay, 我们几乎经常会用到 didSet。
214
00:11:46,440 --> 00:11:48,373
Now you're gonna see we're using the didSet in
现在你会看到我们在控制器中
215
00:11:48,375 --> 00:11:50,975
the controller as well for a slightly different reason,
因为一些其他的原因使用 didSet
216
00:11:50,977 --> 00:11:53,111
okay? But the did, didSet is super convenient.
okay? 不过 didSet 真的是超级方便!
217
00:11:53,113 --> 00:11:57,415
All right, okay so let's talk about our controller here,
好吧, okay 让我们来聊聊控制器,
218
00:11:57,417 --> 00:12:02,020
it needs a model, okay. I have created a little class over
它需要一个模型, okay。 我已经创建好了一个简单的类
219
00:12:02,022 --> 00:12:06,591
here called FacialExpression, which I'm gonna drag in.
叫做 FacialExpression, 现在我把它拖进工程。
220
00:12:06,593 --> 00:12:09,461
I'm gonna copy it in here, okay? When you draw things in,
我把它复制进来, okay? 当你向工程总拖拽东西的时候,
221
00:12:09,463 --> 00:12:10,995
you almost always want this selected.
你几乎总是会把这一项选中。
222
00:12:10,997 --> 00:12:12,997
So pay attention to that, you want it to copy in, you don't
所以注意一下, 你希望它拷贝进来,
223
00:12:12,999 --> 00:12:16,134
really want it having a link to the other thing,
你不希望它链接到别的东西上面
224
00:12:16,136 --> 00:12:16,267
most of the time.
大部分情况下都是这样。
225
00:12:16,269 --> 00:12:19,537
So I'm gonna copy this little FacialExpression in.
所以我要把这个小小的 FacialExpression 拷贝进来.
226
00:12:19,539 --> 00:12:22,474
Let's take a look at FacialExpression. You'll see
让我们来看看 FacialExpression。
227
00:12:22,476 --> 00:12:25,910
we'll see FacialExpression right here, is our model.
你会在这里看到 FacialExpression,它是我们的模型。
228
00:12:25,912 --> 00:12:28,646
See, it's not a UI thing, it imports only foundation.
看到了吗,它不是 UI 方面的东西,它仅仅引入了 foundation。
229
00:12:28,648 --> 00:12:31,549
It's completely UI independent. Its idea of
它完全地与 UI 分离。 它对一张脸的描述
230
00:12:31,551 --> 00:12:34,586
a face is not really the same as a face view. Okay,
和 face view 不同。 Okay,
231
00:12:34,588 --> 00:12:37,388
a face view thinks of mouth curvature and eyebrow tilt and
face view 考虑的是嘴巴的曲率、眼睛的弧度以及
232
00:12:37,390 --> 00:12:40,125
things like that. This doesn't have any of that. It
这方面的东西。完全不包含这个模型里的东西。
233
00:12:40,127 --> 00:12:44,262
does have eyebrows but it just has Relaxed, Normal, Furrowed.
它包含了眉毛的信息,但是只有 Relaxed, Normal, Furrowed这样的描述。
234
00:12:44,264 --> 00:12:46,965
That's the only thing it knows about, those three states.
这三个状态是它唯一知道的。
235
00:12:46,967 --> 00:12:50,001
Same thing with Mouth. It's Frown, Smirk, Neutral, Grin,
嘴巴也是一样。 它的状态是 Frown, Smirk, Neutral, Grin 以及 Smile。
236
00:12:50,003 --> 00:12:52,704
Smile. That's it, it doesn't have mouth curvature.
它就是这样,不包含嘴的曲率。
237
00:12:52,706 --> 00:12:53,872
It doesn't even know what that is,
它甚至不知道曲率什么,
238
00:12:53,874 --> 00:12:57,509
okay? One thing kinda cool here, you see these are enums?
okay? 有一件很有趣的事情,你看到了吗这些都是枚举?
239
00:12:57,511 --> 00:13:00,745
Notice I have functions on my enums here, okay?
请注意我在枚举中定义了方法, okay?
240
00:13:00,747 --> 00:13:03,114
I'm not gonna talk about the implementation of those.
我不打算讨论这些方法的实现.
241
00:13:03,116 --> 00:13:04,249
If you read the reading assignment,
如果你读了作业题目,
242
00:13:04,251 --> 00:13:06,684
you should be able to figure these out. But
你应该可以计算出这些。
243
00:13:06,686 --> 00:13:09,187
I got some functions in here with my enums. But
我在枚举中定义了方法。 但是
244
00:13:09,189 --> 00:13:12,490
really facial expression is just the eyes, eyebrows, and
面部表情依然只有眼睛、眉毛和嘴巴。
245
00:13:12,492 --> 00:13:15,660
mouth, okay, which is specified by these enums. And
okay,使用了这些枚举来表达。
246
00:13:15,662 --> 00:13:18,463
some of the enums like eyes even has a state
枚举中的一部分,比如眼睛,包含了 FaceView 中没有出现的状态。
247
00:13:18,465 --> 00:13:21,900
FaceView can't even represent that face. It doesn't have
FaceView并没有包含一个眯着的眼睛。
248
00:13:21,902 --> 00:13:23,868
a Squinting, it only knows how to have eyes open and
它只知道如何控制眼睛的睁开与闭合。
249
00:13:23,870 --> 00:13:27,705
closed. So the point here is that this model has to be
这里的要点是模型需要被控制器解释给视图
250
00:13:27,707 --> 00:13:31,643
interpreted by the controller for that view, okay?
okay?
251
00:13:31,645 --> 00:13:35,213
The mo, the controller's gonna have to figure out, okay,
此外,控制器必须弄清楚, okay,
252
00:13:35,215 --> 00:13:37,282
what does relaxed eyebrow mean? Well, for
一个 relaxed 的眉毛是什么意思? 好的,
253
00:13:37,284 --> 00:13:39,818
the face view it means some sort of of eyebrow tilt, and
对脸的视图来说,它意味着眉毛的某种倾斜程度,
254
00:13:39,820 --> 00:13:42,253
so the controller has to figure out what that is, and
所以控制器需要把 relaxed 解释给视图,
255
00:13:42,255 --> 00:13:45,824
tell the view, you see? So this shows the primary
看到了吗? 所以这里就解释了控制器在 MVC 中的原始角色
256
00:13:45,826 --> 00:13:50,128
role of a controller in your MVC is to interpret
就是把模型
257
00:13:50,130 --> 00:13:51,563
the model for the view.
解释给视图。
258
00:13:51,565 --> 00:13:54,065
It also interprets input in the view for the model and
它也会把视图中的输入值解释给模型
259
00:13:54,067 --> 00:13:56,401
we'll see that when we talk about gestures, okay.
我们会在讲到手势的时候见到, okay.
260
00:13:56,403 --> 00:13:59,003
That's its kind of primary purpose in the controller.
这就是控制器的原始目的。
261
00:13:59,005 --> 00:14:01,072
So let's make our controller do that,
所以让我们的控制器来做这件事吧。
262
00:14:01,074 --> 00:14:04,943
let's make it turn a facial expression into something in
我们把 facial expression 转换成
263
00:14:04,945 --> 00:14:09,180
the face view, okay. So in our controller, the first thing
视图中的信息, okay。 所以在控制器中,我们要做的第一件事
264
00:14:09,182 --> 00:14:11,749
we're gonna do is we're gonna create a var here,
就是在这里创建一个变量,
265
00:14:11,751 --> 00:14:14,953
which is a pointer to our model, and it's just gonna be
它是一个指向模型的指针, 它是 FacialExpression 类型的
266
00:14:14,955 --> 00:14:17,121
a FacialExpression, okay. And we are even
okay。 我们甚至
267
00:14:17,123 --> 00:14:19,824
gonna set it to some facial expression to start it off
会把它初始化为某种面部表情。
268
00:14:19,826 --> 00:14:22,994
here. Remember we, with the FacialExpression, which is
记住,我们使用的 FacialExpression,
269
00:14:22,996 --> 00:14:28,032
a struct by the way, we get the free constructor. I,
是一个结构体,我们可以使用免费得到的构造器。 我,
270
00:14:28,034 --> 00:14:31,069
by the way, I can infer this. I don't need to have that,
顺便提一句,我可以推断它的类型。 我不需要写出来,
271
00:14:31,071 --> 00:14:34,272
right? So I get the free constructure which is what,
对吗? 我得到的这个免费的构造器中,
272
00:14:34,274 --> 00:14:40,678
eyes are we'll have them be open, and then eyeBrows.
我们让眼睛睁开, 然后是眉毛。
273
00:14:41,114 --> 00:14:44,749
We'll have him be normal. I think is what it is,
我们让眉毛是普通的。我认为它是这样的:普通
274
00:14:44,751 --> 00:14:48,820
normal. And then, we'll have mouth, which will have,
然后我们设置嘴巴,
275
00:14:48,822 --> 00:14:55,293
be a smile I'd say, okay? So this is gonna be our default
像我说过的一个微笑的嘴巴, okay? 这就是
276
00:14:55,395 --> 00:14:58,963
value of our facial expression.
我们的面部描述的默认值。
277
00:14:58,965 --> 00:15:00,732
So this is the model for our MVC.
这就是我们的 MVC 中的模型。
278
00:15:00,734 --> 00:15:05,536
Now what's interesting about this model is what if it
现在这个模型中有趣的部分是
279
00:15:05,538 --> 00:15:09,741
changes? Okay, if it changes, I need to update my view.
如何改变它? Okay, 如果它发生了改变, 我需要更新我的视图。
280
00:15:09,743 --> 00:15:15,046
All right, if, if it changes from smile to a frown
好的, 如果, 如果它从微笑变成了皱眉
281
00:15:15,048 --> 00:15:18,917
I need to change my curvature of my face view. So I'm gonna
我需要改变视图中的曲率。 所以我要
282
00:15:18,919 --> 00:15:23,321
use that same magic the didSet thing that we did before to
使用 didSet 的魔法就像我们之前做的那样
283
00:15:23,323 --> 00:15:27,692
update my UI whenever this facial expression changes.
当模型发生改变的时候更新 UI。
284
00:15:27,694 --> 00:15:31,229
And since this is a value type FacialExpression,
因为这个值的类型是 FacialExpression,
285
00:15:31,231 --> 00:15:34,465
if any of the vars in that value type change,
如果这个变量中的任何属性发生了变化,
286
00:15:34,467 --> 00:15:37,302
this didSet is gonna get called, okay?
didSet 就会被调用, okay?
287
00:15:37,304 --> 00:15:40,104
If this were a class, it wouldn't. So
如果模型是一个类则不会调用 didSet。
288
00:15:40,106 --> 00:15:43,675
luckily it's value type. So this is gonna get called. So
所以庆幸它是个值类型。这部分将会被调用。
289
00:15:43,677 --> 00:15:46,945
we need this update UI. This can be any function we call.
所以我们需要方法更新 UI。 我们可以在这里调用任何方法。
290
00:15:46,947 --> 00:15:48,680
I like calling it function, updateUI, but
我喜欢把这个方法取名为 updateUI。
291
00:15:48,682 --> 00:15:52,417
you can do whatever you want. And it's gonna be private,
你可以取自己喜欢的名字。 这个方法应该是 private 的,
292
00:15:52,419 --> 00:15:53,184
it's a func updateUI.
现在我们有了 updateUI 方法。
293
00:15:53,186 --> 00:15:56,387
And inside here we have to update our faceView.
在它的内部我们需要更新 faceView。
294
00:15:56,389 --> 00:15:59,123
Okay well, if we're gonna update our face view, we need
好的, 如果我们想要更新我们的 faceview,我们需要
295
00:15:59,125 --> 00:16:02,860
a pointer to that as well. Okay, how do we make a pointer
一个指向它的指针。 Okay, 我们该怎样创建一个视图的指针?
296
00:16:02,862 --> 00:16:06,331
to something in our view? >> Click drag.
>> 单击并且拖动。
297
00:16:06,333 --> 00:16:08,232
Yeah we click, we ctrl drag, right?
我们点击,然后拖动,对吧?
298
00:16:08,234 --> 00:16:09,867
We made an outlet. So let's just do that.
我们创建了一个 outlet。 动起手来吧.
299
00:16:09,869 --> 00:16:13,037
Let's bring our storyboard up here. We've got our face view.
打开 storyboard。我们已经得到了 face view。
300
00:16:13,039 --> 00:16:15,640
Here's our controller right here. I'm just gonna control
控制器在这里。 我将要
301
00:16:15,642 --> 00:16:18,509
drag from the face view into here and I'm gonna create
把 faceView 拖动到这里然后创建一个
302
00:16:18,511 --> 00:16:21,679
an outlet. I'm gonna to call it face view. It's my face
outlet。 取名为face view。它就是我的小人脸
303
00:16:21,681 --> 00:16:24,782
view, okay. We used display in our calculator. This is our
okay。 我们在计算器项目中使用过它进行显示。 这既是我们的小人脸
304
00:16:24,784 --> 00:16:30,588
face view. We click okay here and now we've got this outlet,
我们点击这里的 okay 选项,然后我们得到了一个 outlet,
305
00:16:30,590 --> 00:16:32,156
okay. So now we've got a pointer to this face.
okay. 现在我们得到了一个指向人脸的指针.
306
00:16:32,158 --> 00:16:35,226
So now we can talk to this face set it's curvatures and
接着我们会谈谈如何设置人脸的曲率以及
307
00:16:35,228 --> 00:16:37,628
all that business, right?
所有的这些逻辑,怎么样?
308
00:16:37,630 --> 00:16:42,333
So, now we've got a pointer to this in our update UI.
现在我们得到了这个指针以及 updateUI 方法。
309
00:16:42,335 --> 00:16:44,969
We can just talk to it and set it based on what
我们可以自由讨论视图并且基于面部表情来配置视图。
310
00:16:44,971 --> 00:16:46,838
the expression is. So how are we gonna do that?
所以我们该怎么做呢?
311
00:16:46,840 --> 00:16:50,008
All right, well first of all let's start with the eyes. So
好吧,首先从眼睛开始吧。
312
00:16:50,010 --> 00:16:55,313
if the facial expression's eyes are in the state
如果面部描述中的眼睛是睁开的,
313
00:16:55,315 --> 00:16:59,817
open, then we're gonna send a message to our faceView saying
然后我们就需要向 faceView 发送一条消息告知它
314
00:16:59,819 --> 00:17:03,221
eyes open equals true. So that's an easy one okay our
眼睛睁开的选项是 ture。这很容易办到,
315
00:17:03,223 --> 00:17:06,391
face views very similar to our model in that way, and
在眼睛的配置上模型和视图是相近的,
316
00:17:06,393 --> 00:17:10,461
same thing here closed. We'll do faceview.eyesopen
closed 选项的操作也很相近。 我们会把 faceview.eyesopen
317
00:17:10,463 --> 00:17:15,066
equals false but what about case squinting?
设置为 false 但是 squinting 的情况该怎么办?
318
00:17:15,502 --> 00:17:19,137
Okay, we don't really have faceView.squint, okay we can't
Okay, 实际上我们没有 faceView.squint 这样的属性, 好吧
319
00:17:19,139 --> 00:17:23,307
do that. So I'm just gonna say eyes open equals false.
我们办不到。 所以我只能写 eyes open 为 false.
320
00:17:23,309 --> 00:17:26,878
if you are squinting, eyes are closer to closed than they
如果你是 squinting(斜视)的表情, 眼睛闭上的状态更加贴近一些。
321
00:17:26,880 --> 00:17:28,913
are to open, okay. So it is the best I can do,
okay。 这是我们实现的最好方案,
322
00:17:28,915 --> 00:17:31,482
and this is the way some times a controller is. It's
这就是有时候控制器所发挥的作用。
323
00:17:31,484 --> 00:17:34,886
view might not be perfect at representing the model, it's
这个视图可能并不能很好地展示视图,
324
00:17:34,888 --> 00:17:37,488
doing the best it can. Now it might be, we might be sending
它只能尽力做到最好。现在控制器是这样做的,我们可能会向 face view 中的某一位
325
00:17:37,490 --> 00:17:40,091
in a request to the face view guys on our team saying hey,
发送一个请求,告诉它:嘿,伙计
326
00:17:40,093 --> 00:17:42,460
we wanna feature where our face view will do squinting
我们想让视图能够展示 squinting
327
00:17:42,462 --> 00:17:44,929
because we got squinting in our model. And they might be,
因为我们从模型中得到了一个 squinting 。某些视图可能就会照做,
328
00:17:44,931 --> 00:17:47,465
okay we'll take it under advisement and we'll see.
好吧我们会采纳的,让我们看一下怎么做。
329
00:17:47,467 --> 00:17:48,833
But for now we're kind of stuck.
但是现在我们陷入了困境。
330
00:17:48,835 --> 00:17:53,438
So we're gonna do squinting is the same as eyes closed.
所以我们在处理 squinting 的时候只能让眼睛和 closed 中的情况一样。
331
00:17:53,440 --> 00:17:54,939
How about our other two things? Okay,
那么其他两样东西会怎样呢? Okay,
332
00:17:54,941 --> 00:17:56,374
we've got these curvatures to deal with, right?
我们有这些曲率需要处理,对吧?
333
00:17:56,376 --> 00:18:01,512
So, we want to say something like faceView.mouthCurvature =
我们想要写 faceView.mouthCurvature =
334
00:18:01,514 --> 00:18:05,650
something based on the expression's mouth. But
模型中关于嘴部曲率描述的值。
335
00:18:05,652 --> 00:18:08,553
the expression's mouth is like grin and frown.
嘴巴的描述就像 grin 和 frown。
336
00:18:08,555 --> 00:18:09,187
What the heck are we gonna do?
我们要做些什么?
337
00:18:09,189 --> 00:18:11,289
How are we gonna convert that to a curvature?
我们该如何把它们转化成曲率?
338
00:18:11,291 --> 00:18:11,856
And what I'm gonna do is,
现在我就来解决这个问题,
339
00:18:11,858 --> 00:18:16,360
I'm actually going to create a little private dictionary,
我想要创建一个轻量的私有的字典,
340
00:18:16,362 --> 00:18:21,199
I'm gonna call it mouthCurvatures. Okay, plural
我会给它取名为 mouthCurvatures. Okay, 一对括号
341
00:18:21,201 --> 00:18:25,002
and I'm gonna make it equal to a dictionary. Remember,
并且我要让它等于一个字典。 记住,
342
00:18:25,004 --> 00:18:27,338
we can make a dictionary on the fly with square brackets.
我们可以使用一对方括号创建一个字典。
343
00:18:27,340 --> 00:18:30,541
And then I'm gonna have the keys of this dictionary be
接下来我准备给这个字典设置一些键值来匹配
344
00:18:30,543 --> 00:18:31,909
mouthCurvatures for my model and
模型中的嘴巴曲率。
345
00:18:31,911 --> 00:18:35,146
I'm gonna have the values be mouthCurvatures for my view.
字典的值对应了视图中的嘴巴曲率。
346
00:18:35,148 --> 00:18:40,118
Okay, doubles. All right, so for example let's do
好的, double 类型。举个例子,让我们增加一个新的成员
347
00:18:40,120 --> 00:18:44,722
FacialExpression.Mouth.Frown. Okay, that's
FacialExpression.Mouth.Frown。 Okay, 这是键值。
348
00:18:44,724 --> 00:18:47,892
the key. And the values gonna be -1.0 that's the curvature
对应的值为 -1.0,这是该种状态的嘴巴的曲率。
349
00:18:47,894 --> 00:18:50,194
that goes along with that. See what I'm saying? So
看到了我所说的了吗?
350
00:18:50,196 --> 00:18:52,497
I'm just creating this dictionary that's mapping
我创建这个字典的目的是
351
00:18:52,499 --> 00:18:59,103
between my model and my view. So how about Grin? Notice but
映射模型与视图中的值。 那么 Grin(咧嘴)该是什么样?
352
00:18:59,105 --> 00:19:02,473
by the way, when I do Grin, okay, it's already inferred to
顺便说一句, 当我咧嘴的时候, okay, 系统的类型推断已经推测出了字典的键是
353
00:19:02,475 --> 00:19:04,342
the fact that we're doing a FacialExpression.Mouth.
一个 FacialExpression.Mouth 类型的。
354
00:19:04,344 --> 00:19:07,578
So and I do, don't need to repeat this every single time.
因此我会这样做, 不需要每次都重复写前缀。
355
00:19:07,580 --> 00:19:10,448
I can just say the Grin. and the Grin, we'll say, is 0.5.
我可以只写 Grin。我们把 Grin 设定为 0.5。
356
00:19:10,450 --> 00:19:12,617
So it's not a full smile, just kind of a little bit
所以它不是开怀大笑,只是微微一笑的程度。
357
00:19:12,619 --> 00:19:16,854
of a smile. But a full smile would be 1.0 and
开怀大笑的曲率是 1.0
358
00:19:16,856 --> 00:19:22,660
a Smirk, now that sounds kinda like a little bit of a frown,
一个 Smirk(傻笑), 听起来有点像皱眉,
359
00:19:22,662 --> 00:19:23,227
but not too much of one.
但是不是同一个表情。
360
00:19:23,229 --> 00:19:27,165
So, we'll -0.5 right there and then there's this neutral
我们把这种表情的值设为 -0.5
361
00:19:27,167 --> 00:19:31,736
mouth position which we'll have be zero point zero. Okay,
最后还有一个 Neutral(中立)的表情,把它的值 设为 0.0。Okay,
362
00:19:31,738 --> 00:19:34,172
so, I created these mouth curvatures in a table. So now,
我把嘴巴的曲率装进了一个列表中。 现在,
363
00:19:34,174 --> 00:19:38,242
down here, I can just let my mouth curvature equal mouth
完成了, 我可以把模型的 mouth 属性所提供的信息转换成
364
00:19:38,244 --> 00:19:42,780
curvatures sub expression.mouth.
嘴巴视图的曲率。
365
00:19:42,782 --> 00:19:45,683
Okay, now the problem with this, and you can see there's
Okay, 现在遇到了问题,你可以看到这里有一个报错。
366
00:19:45,685 --> 00:19:49,420
an error here. And can anyone see what the problem here is?
所有人都能看到这里出问题了吧?
367
00:19:49,822 --> 00:19:53,824
This is a dictionary lookup. What is a dictionary lookup?
这是一个字典的查找。 字典的查找是什么?
368
00:19:53,826 --> 00:19:56,861
What type does it return? Optional,
它返回什么类型? 可选型。
369
00:19:56,863 --> 00:20:00,965
yeah, so this is a double. It's not an optional, okay?
耶, 这是个小麻烦。 它不是个可选型, okay?
370
00:20:00,967 --> 00:20:02,200
So how are we gonna deal with this?
所以我们该如何处理?
371
00:20:02,202 --> 00:20:07,972
What if facial expression gets enhanced some future date and
如果 Facial Expression 未来增加一些特性并且
372
00:20:07,974 --> 00:20:10,107
the expression's not in here?
这些新的表情不在这个字典里该怎么办?
373
00:20:10,109 --> 00:20:14,345
I'm going to have it default to zero. Okay?
我把它的默认值设为 0.0。 Okay?
374
00:20:14,347 --> 00:20:18,749
Remember this defaulting thing right here where you can take
记得在这里设置一个默认值
375
00:20:18,751 --> 00:20:23,020
something that's a double and if it turns into to nil if it
当你想要获取某个 double 类型的值,当它遇到nil时
376
00:20:23,022 --> 00:20:27,024
returns nil then you can have a default value there,
你会得到一个默认的 double 值,
377
00:20:27,026 --> 00:20:30,361
make sense? Var there. Okay, everyone's
明白了吗? 这里加上 var。 Okay
378
00:20:30,363 --> 00:20:34,131
cool with what I did there? All right, hopefully if you
我这里的处理很酷吧? 好的, 希望你在计算器项目中用到过它
379
00:20:34,133 --> 00:20:36,300
solve it from calculator it's very similar to that,
这里和那个项目中的情况很像。
380
00:20:36,302 --> 00:20:38,636
looking things up in the dictionary. So I'm gonna
在字典中查找东西。 接下来
381
00:20:38,638 --> 00:20:41,038
do with the same thing with the eyebrow tilt okay
我要给眉毛的曲率做相同处理。 okay
382
00:20:41,040 --> 00:20:44,208
the eyeBrowTilt equals eyeBrowTilts
eyeBrowTilt 等于 eyeBrowTilts(一个待生成的字典)
383
00:20:44,210 --> 00:20:48,446
which is gonna be a dictionary and I'm gonna put the,
并且我要用一个 expression.eyebrows 作为索引。
384
00:20:48,448 --> 00:20:53,651
a facial expression.eyebrows in there and
然后当我们查找不到的时候
385
00:20:53,653 --> 00:20:57,822
also default that one to zero if we can't find it. And
依旧设置为 0.0。
386
00:20:57,824 --> 00:20:59,991
then I'll just make a private var here,
接下来我在这里定义一个私有变量,
387
00:20:59,993 --> 00:21:02,393
we'll call it eyeBrowTilts And
取名为 eyeBrowTilts
388
00:21:02,395 --> 00:21:09,000
this one we'll do FacialExpression.Eyebrows.
用来处理 FacialExpression.Eyebrows。
389
00:21:09,002 --> 00:21:11,669
What do we got here, relaxed: so
这里需要填写的东西有 relaxed
390
00:21:11,671 --> 00:21:16,707
we'll have relaxed be a CGFloat, which is 0.5, so
我们要把 relaxed 解释成一个 CGFloat,值为 0.5,
391
00:21:16,709 --> 00:21:20,211
it's kind of relaxed eyebrows notice I.
这是一种放松的眉毛。注意
392
00:21:20,213 --> 00:21:23,681
Oops not CGFloat, that's 0.5 okay that's a double,
它不是 CGFloat,字面量是 0.5 okay 所以它是一个 double,
393
00:21:23,683 --> 00:21:25,583
okay the eyebrow tilts are doubles.
okay 现在眉毛的倾斜度是 double 类型了。
394
00:21:25,585 --> 00:21:29,220
So we don't I did not want CG float there.
这里我们不想要一个 CGFloat 类型。
395
00:21:29,222 --> 00:21:35,092
Okay how about .furrowed is -0.5 tilt and
Okay .furrowed 是 -0.5 的倾斜度
396
00:21:35,094 --> 00:21:41,132
.normal we'll make be 0. Okay?
.normal 我们设为 0。 Okay?
397
00:21:41,968 --> 00:21:46,337
Got that? All make sense? So we are doing the same look up
明白了吗? 所有人都明白了吗? 我们这里做了相同的查找。
398
00:21:46,339 --> 00:21:51,309
there. Okay so we've matched up our model with our view.
我们把模型与视图进行了配对。
399
00:21:51,311 --> 00:21:54,945
We've kind of interpreted our model for view. Now,
我们创建了一个为视图解释模型的控制器。 现在,
400
00:21:54,947 --> 00:21:59,150
lets go ahead and run this and see what this looks like.
让我们继续并且运行程序,看看是什么样子。
401
00:22:04,223 --> 00:22:08,025
Okay, now, hmm this doesn't quite look right. Because,
Okay, 现在,这个表情看起来不太对。 因为,
402
00:22:08,027 --> 00:22:11,095
if we look at our model it's supposed to be eyes open.
如果你查看我们的模型就会发现,从模型中看眼睛应该睁开。
403
00:22:11,097 --> 00:22:13,931
Okay, that's good. Eyebrows normal. No.
Okay, 这很棒。 眉毛是 normal。不。
404
00:22:13,933 --> 00:22:16,200
Those are not normal eyebrows, those are kinda furrowed.
屏幕上的眉毛不是 normal 的。没有有点皱。
405
00:22:16,202 --> 00:22:20,071
And mouth is supposed to be smile. Hmm, no, it's a frown.
并且嘴巴应该是 smile。 恩..., 不,这是一个 frown(愁眉苦脸) 的嘴型。
406
00:22:20,073 --> 00:22:24,175
In fact, all of this here instead of the UI looking like
实际上,所有的这些面部表情不像是模型所表达的,
407
00:22:24,177 --> 00:22:28,212
the model it still looks like we set it in the storyboard.
而是 storyboard 上的。
408
00:22:28,214 --> 00:22:32,717
Okay, so, why is it that our update UI is not getting
Okay,那么,为什么当我们初始化一个表情的时候
409
00:22:32,719 --> 00:22:37,188
called here when we initialize our facial expression?
我们更新 UI 的逻辑没有实现呢?
410
00:22:37,190 --> 00:22:39,957
That's because if you're setting a value during
这是因为当你通过构造器初始化一个值的时候
411
00:22:39,959 --> 00:22:44,929
initialization, didSet is not called. Okay?
didSet 不会执行。 Okay?
412
00:22:44,931 --> 00:22:48,966
It's only called if you set it later. Now why is that?
当你稍后对它赋值的时候 didSet 才会执行。这是为什么 ?
413
00:22:48,968 --> 00:22:52,169
Because you know that when things are being initialized
因为你知道的在 Swift 中,当对象被初始化的时候,
414
00:22:52,171 --> 00:22:54,305
in Swift, they have to be fully initialized before
在你对它做任何操作之前,
415
00:22:54,307 --> 00:22:57,241
you can do anything with them. So of course, they have to be
它必须被完全初始化。 所以理所当然的,当你想对 expression
416
00:22:57,243 --> 00:22:59,009
fully initialized before you can do any of this
做任何修改时,它必须被完全初始化。
417
00:22:59,011 --> 00:23:03,581
stuff either. Okay? So, this initialization did happen, but
Okay? 因此,这个对象已经初始化了, 但是
418
00:23:03,583 --> 00:23:05,983
this, you updateUI didn't get called because this was
你的 updateUI 没有被调用,因为
419
00:23:05,985 --> 00:23:07,918
happening during the initialization phase of our
expression 的初始化过程发生在
420
00:23:07,920 --> 00:23:10,821
faceView controller. Now, if we were to set the expression
faceView controller 的初始化过程中。 现在, 如果我们稍后去设置 expression
421
00:23:10,823 --> 00:23:14,325
later, this would get called, our UI would change. So,
updateUI 方法会被调用。 我们的 UI 会发生改变。所以,
422
00:23:14,327 --> 00:23:16,927
how do we deal with that? Okay, what we are gonna do
我们该如何处理? Okay。 有些事情无论如何我们都需要做
423
00:23:16,929 --> 00:23:20,131
there is something we should of done anyway. Which is,
那就是
424
00:23:20,133 --> 00:23:25,669
when our face view outlet is set by the system.
当我们的 faceView 的 outlet 被系统设置好的时候。
425
00:23:25,671 --> 00:23:30,040
We are gonna do didSet here and Update our UI. Okay?
我们在它的 didSet 中更新我们的 UI。 Okay?
426
00:23:30,042 --> 00:23:35,279
So, this didSet is called when iOS comes along shortly after
所以, 这里的 didSet 会在你的 MVC 创建不久之后
427
00:23:35,281 --> 00:23:39,250
your MVC is created, and it wires up this outlet. Okay?
被 iOS 系统所调用, 它会关联这个 outlet。 Okay?
428
00:23:39,252 --> 00:23:42,019
It hooks up the outlet so it's actually pointing to that face
系统会钩住(hook up)这个 outlet,让它指向内存中的 faceView。
429
00:23:42,021 --> 00:23:45,089
view. So, as soon as that happens, now we have
这样的话,只要这个过程发生,我们
430
00:23:45,091 --> 00:23:49,393
a hold of our face view. Now we can update it. Okay, so
就会持有我们的 faceView 了。现在我们可以更新 UI 了。 Okay,所以
431
00:23:49,395 --> 00:23:51,929
we are updating it both when our model changes and
当我们的模型发生变化以及我们的 faceView 第一次被系统钩住的时候,
432
00:23:51,931 --> 00:23:56,767
the first time our view is hooked up. Okay.
我们都会更新 UI。 Okay。
433
00:23:56,769 --> 00:24:00,171
And this is something also that we would usually
我们经常把 didSet 与对象
434
00:24:00,173 --> 00:24:02,339
put on the end like this.
写在同一行。
435
00:24:07,747 --> 00:24:10,347
Okay we might even do it here I might even leave this here
我们可以像上面这样写,这里我的代码保持这个写法
436
00:24:10,349 --> 00:24:13,083
just to emphasize that we're doing this but again this
只是为了强调我们可以这么写
437
00:24:13,085 --> 00:24:16,587
would be a common thing to throw on the end there okay.
不过写在末尾是更常见的写法。
438
00:24:16,589 --> 00:24:21,091
All right, so now let's run, now when Swift
好的,现在运行一下,当 Swift(错说了)
439
00:24:21,093 --> 00:24:24,161
iOS basically comes along and hooks up that face view.
iOS 系统基本启动并且钩住了 faceView 之后,
440
00:24:24,163 --> 00:24:28,165
Boom. It's going to be updated with our model, okay?
Boom~。 faceView 就更新到模型中描述的样子了, okay?
441
00:24:28,167 --> 00:24:30,634
And so now if we change our model here, let's make
并且现在如果我们修改模型,让我们把
442
00:24:30,636 --> 00:24:35,773
the eyebrows be relaxed. We'll make the mouth be a smirk.
眉毛设置为 relaxed(放松),把嘴巴设置为 smirk(傻笑)。
443
00:24:35,775 --> 00:24:39,610
Let's get those eyes closed. Okay. And we change all of
让眼睛闭上。 Okay。 我们改变了模型中的
444
00:24:39,612 --> 00:24:42,613
this in the model, then our view should be reflecting our
所有属性, 我们的视图会按照模型进行更新。
445
00:24:42,615 --> 00:24:49,553
model. Okay, and there it is. Kay? All good?
Okay, 就是这样 Okay? 都很棒吧?
446
00:24:49,555 --> 00:24:53,057
Everyone understand what we're doing there with our model?
所有人都理解我们对模型做了什么事情吧?
447
00:24:53,793 --> 00:24:57,061
Okay. So let's go back to the slides and
Okay。 让我们回到幻灯片。
448
00:24:57,063 --> 00:25:00,030
talk a little bit about gestures and then we'll come
让我们来讨论一下手势,并且我们将会回到项目中
449
00:25:00,032 --> 00:25:04,301
back and add some gestures to our FACEIT right here.
并且向其中增加一些手势。
450
00:25:11,711 --> 00:25:15,513
Okay. So, we saw last time how to draw in a view.
Okay。 所以,刚才我们看到了如何在视图中绘制。
451
00:25:15,515 --> 00:25:19,116
We drew the mouth, we drew the eyes, we did the eyebrows, but
我们画了嘴、眼睛和眉毛,所有的这一切。
452
00:25:19,118 --> 00:25:22,086
all of these things. We know how to do that pretty much,
我们知道如何做到这一点,
453
00:25:22,088 --> 00:25:22,653
right? We're using the use,
对吧? 我们在绘制的区域使用了
454
00:25:22,655 --> 00:25:25,689
UI bezier path in our draw rect, not so hard.
UIBezierpath,并不复杂。
455
00:25:25,691 --> 00:25:29,727
What about gestures? Okay? So this is input from the user on
那么手势呢? Okay? 这是用户在屏幕上的输入方式之一。
456
00:25:29,729 --> 00:25:35,733
the screen. Now, we can get the location and movement
现在, 我们可以得到每一次单个手指触碰到屏幕时的
457
00:25:35,735 --> 00:25:38,235
of every single finger that touches down on the screen,
位置和运动轨迹,
458
00:25:38,237 --> 00:25:42,573
that's possible. Okay? There's API for doing that, but we
这是可以实现。 Okay? 捕获手势有专门的 API,不过我们
459
00:25:42,575 --> 00:25:46,010
virtually never do that ever. Okay? Why don't we do that?
几乎从来没有用过这些 API。 Okay? 我们为什么不用?
460
00:25:46,012 --> 00:25:49,547
The reason for that is that the user thinks of interacting
原因是用户认为当他们在屏幕上使用手势进行交互时
461
00:25:49,549 --> 00:25:53,417
with your UI as a gesture, like, they're swiping, or
他们使用的手势是具体的,
462
00:25:53,419 --> 00:25:56,787
they're pinching. Okay, where they're panning around.
比如滑动或者捏合。
463
00:25:56,789 --> 00:25:59,023
Those are gestures that they're making.
这些是他们正在做的手势。
464
00:25:59,025 --> 00:26:02,259
So iOS has an abstraction layer that lets your
所以 iOS 系统中有一个抽象层
465
00:26:02,261 --> 00:26:06,630
app think of the input as gestures as well, okay? So
让你的应用可以把屏幕上的交互识别为对应的手势, okay?
466
00:26:06,632 --> 00:26:07,398
you're gonna get those gestures and
所以你就可以得到这些手势并且
467
00:26:07,400 --> 00:26:10,067
do something when they happen. There's predefined gestures.
当它们产生的时候做相应的处理。 这些都是预定义的手势。
468
00:26:10,069 --> 00:26:13,304
The great thing about that is, a swipe is exactly the same
很棒的一点是,在每一个单独的应用中
469
00:26:13,306 --> 00:26:16,273
thing in every single app. Okay? It's like, you know,
滑动手势都是类似的。 Okay? 你知道的,就好比,
470
00:26:16,275 --> 00:26:19,209
know, the speed of swiping and how far you have to swipe,
滑动的速度以及你需要滑动的距离,
471
00:26:19,211 --> 00:26:20,778
all that's exactly the same in every single app,
在每一个应用中这些都是相同的,
472
00:26:20,780 --> 00:26:23,247
cuz every single app is using these gestures. Okay,
这是因为每一个应用都用了相同的手势。 Okay,
473
00:26:23,249 --> 00:26:25,716
if you had to do your own swipe, well, you know,
如果你想要做自己的滑动手势,好吧,如你所知
474
00:26:25,718 --> 00:26:27,017
did, did the person swipe fast enough?
多快速的动作算是滑动?
475
00:26:27,019 --> 00:26:29,853
Or was that a pan? You'd have to do all that logic yourself.
或者那是一个点击手势吗? 你必须去独自处理这些逻辑。
476
00:26:29,855 --> 00:26:30,487
It would be a pain in the neck, and
这将是一个痛苦的过程,并且
477
00:26:30,489 --> 00:26:34,525
it would be inconsistent between applications. Okay. So
不同的程序间将会不一致。 Okay。所以
478
00:26:34,527 --> 00:26:38,696
gestures are recognized by instances of a class called
手势会被一个叫做 UIGestureRecognizer 的实例识别。
479
00:26:38,698 --> 00:26:42,066
UIGestureRecognizer. Okay, good name. But
Okay,好名字。不过
480
00:26:42,068 --> 00:26:44,468
UIGestureRecognizer itself is abstract.
UIGestureRecognizer 这个类本身是个抽象类。
481
00:26:44,470 --> 00:26:47,771
You never actually instatiate one. Instead there's a bunch
你永远都不会实例化它。 取而代之的是一组
482
00:26:47,773 --> 00:26:50,140
of sub-classes of UIGestureRecognizer. And
UIGestureRecognizer 的子类。
483
00:26:50,142 --> 00:26:53,777
those are what you instantiate to get the kind of gesture you
这些子类才是你需要根据具体的手势进行实例化的对象。
484
00:26:53,779 --> 00:26:55,746
want. So there's pan gesture recognizer and
所以会有 pan gesture recognizer、
485
00:26:55,748 --> 00:26:58,916
pinch gesture resto recognizer and tap and swipe gesture
pinch gesture recognizer 以及 tap 和 swipe gesture
486
00:26:58,918 --> 00:27:03,554
recognizer etc. Okay. Now when you wanna use a recognizer,
recognizer 等等。 Okay。现在当你想要使用一个识别器的时候,
487
00:27:03,556 --> 00:27:06,256
there's really two parts to it, okay.
它需要包含两部分, okay。
488
00:27:06,258 --> 00:27:11,028
One is you have to get some UIView to.
一个是在其上识别手势的 UIView。
489
00:27:11,030 --> 00:27:14,465
Take on this gesture recognizer and recognize that
把这个手势识别器加到视图上然后识别对应的手势。
490
00:27:14,467 --> 00:27:17,034
gesture. So there's the gesture recognition part
所以这一步需要你
491
00:27:17,036 --> 00:27:19,203
which is done by creating a gesture recognizer and
创建一个手势识别器然后
492
00:27:19,205 --> 00:27:23,107
then a view to use it. Only views can recognize gesdri,
指定一个视图去使用它。 只有视图才能识别手势,
493
00:27:23,109 --> 00:27:27,077
gestures not controllers. They can't recognize it only views.
控制器不能识别手势。
494
00:27:27,079 --> 00:27:29,947
Okay? So part one is to create the gesture recognizer you
Okay? 所以第一步就是创建你想要的手势识别器
495
00:27:29,949 --> 00:27:31,682
want, configure it how you want, and
按照你的想法配置它,并且
496
00:27:31,684 --> 00:27:35,619
then ask some view please start recognizing this. Okay?
指派一些视图开始识别。 Okay?
497
00:27:35,621 --> 00:27:39,957
Then part two is, what if the recognizer does recognize it?
然后第二步, 手势识别器识别了手势之后该做什么?
498
00:27:39,959 --> 00:27:43,193
Then it needs to have that handled some how. And
它需要一些处理。
499
00:27:43,195 --> 00:27:44,995
that's done with a gesture handler.
这一步通过一个手势处理器(gesture handler)来完成。
500
00:27:44,997 --> 00:27:46,664
So you've got gesture recognizers and
现在,你有一个手势识别器和
501
00:27:46,666 --> 00:27:50,067
gesture handlers, okay? And the handler is called
处理手势事件的方法,这个方法
502
00:27:50,069 --> 00:27:53,370
as the recognizer goes through a basically a state machine
在识别器不断从基础的状态机
503
00:27:53,372 --> 00:27:55,506
of recognizing this gesture. Okay, and
识别到这个手势时被调用
504
00:27:55,508 --> 00:27:58,142
we'll talk about what that state machine looks like. But
我们稍后会讨论这个状态机
505
00:27:58,144 --> 00:27:58,475
it's called repeatedly,
这个状态会不断重复
506
00:27:58,477 --> 00:28:01,145
the handler's gonna be called repeatedly, as the gesture
所以处理手势事件的方法也会不断被调用
507
00:28:01,147 --> 00:28:03,080
goes through the process of being recognized and
手势不断继续,这个过程不断被识别到
508
00:28:03,082 --> 00:28:06,383
moving and all the things it does. Okay now usually
调用也在继续
509
00:28:06,385 --> 00:28:10,454
the first of these things the creating of a recognizer and
通常我们先创建一个手势识别器
510
00:28:10,456 --> 00:28:10,821
adding it to some
然后将它添加
511
00:28:10,823 --> 00:28:14,191
view is usually done by your controller. Okay.
到 view 上,这通常由控制器完成
512
00:28:14,193 --> 00:28:16,560
It doesn't have to be done by your controller but
但并不是必须由控制器完成
513
00:28:16,562 --> 00:28:20,030
it's a good way for
但由控制器添加更好
514
00:28:20,032 --> 00:28:22,866
its views. Okay the views are the minion of the controller
views 是 controller 的仆从
515
00:28:22,868 --> 00:28:26,603
anyway. So, the controller wants to control its minions.
所以,控制器想控制它的仆从
516
00:28:26,605 --> 00:28:30,541
So, it might usually be the one that wants to
控制器通常
517
00:28:30,543 --> 00:28:32,976
add the gesture recognizer to a view but
给 view 添加手势识别器
518
00:28:32,978 --> 00:28:36,013
some views, the gesture recognition is so
但是,对于一些 view 来说,其手势识别器
519
00:28:36,015 --> 00:28:39,783
fundamental to who they are that they add it themselves.
太过于基础,所以他们自己添加
520
00:28:39,785 --> 00:28:40,084
They add it themselves.
他们为自己添加手势识别器
521
00:28:40,086 --> 00:28:42,653
For example, ScrollView. Okay what would a ScrollView
比如说,ScrollView 如果 ScrollView 没有
522
00:28:42,655 --> 00:28:45,456
be without a pan gesture. Just scrolling up and down it would
拖动手势,只是滚来滚去
523
00:28:45,458 --> 00:28:48,726
be useless it wouldn't even be a ScrollView anymore.
这样的 ScrollView 没有什么用,也不会再被叫做 ScrollView 了
524
00:28:48,728 --> 00:28:52,262
So ScrollView adds a pan gesture recognizer and
所以,ScrollView自己添加了拖动手势和
525
00:28:52,264 --> 00:28:54,131
a pinch gesture recognizer to itself.
捏合手势
526
00:28:54,133 --> 00:28:58,001
Probably in it's initializer. Okay but lot of the times your
可能是内部通过自己的初始化方法添加到
527
00:28:58,003 --> 00:28:59,903
controller that's adding the gesture,
但是,大多数时间都是 controller 添加手势的
528
00:28:59,905 --> 00:29:03,140
again the controller can't do the recognition to gesture
再重复一遍,controller 不能识别到手势
529
00:29:03,142 --> 00:29:05,576
only of you with the gesture recognizer can. But
只有你添加了手势识别器的控件可以
530
00:29:05,578 --> 00:29:08,879
the controller can add that gesture recognizer create and
但是,controller可以添加手势识别器
531
00:29:08,881 --> 00:29:11,582
add the gesture I can add it to one of its views in its
创建和添加手势给 controller 里的一个 view 中
532
00:29:11,584 --> 00:29:17,154
view. Okay, the second thing that handler that handles it.
第二件事是,处理方法来处理手势
533
00:29:17,156 --> 00:29:19,723
Well that could be handled by the controller or
可以使 controller 来处理
534
00:29:19,725 --> 00:29:20,691
it might be handled by the view.
也可以是添加了手势的 view 来处理
535
00:29:20,693 --> 00:29:23,393
It could even be handled by someone else. Would never be
甚至可以是其他人
536
00:29:23,395 --> 00:29:26,663
handled by your model because UI independent. But it could
但是,永远不要在你的 model 中处理,因为UI分离
537
00:29:26,665 --> 00:29:30,100
be anyone in the controller view camps can handle it.
在 controller 或者 view 阵营中的任何一者可以处理
538
00:29:30,102 --> 00:29:34,872
Generally, kind of, if the things that's the gesture is
通常,如果手势的处理事件是
539
00:29:34,874 --> 00:29:38,509
doing is only modifying how the view displays itself,
怎么修改 view 的展示
540
00:29:38,511 --> 00:29:42,012
like in our Faceview, it was just the scale of the view or
像我们的 FaceView 中 view 的大小
541
00:29:42,014 --> 00:29:44,414
maybe somehow it was change in the color of the view.
或是 view 颜色的变化
542
00:29:44,416 --> 00:29:47,518
Then the view is probably going to handle the gesture.
这是,通常是这个view来处理这个手势
543
00:29:47,520 --> 00:29:49,052
Okay, it's gonna both recognize it
这个view不仅识别手势
544
00:29:49,054 --> 00:29:52,990
with the recognizers and handle it. But if the gesture
而且处理对应的事件,但是如果这个手势
545
00:29:52,992 --> 00:29:56,260
is changing the model, then definitely the controller
改变了 model,这种情况绝对是
546
00:29:56,262 --> 00:29:58,662
would be the handler. Do you see why that is?
controller要成为处理事件的对象,你明白吗?
547
00:29:58,664 --> 00:30:00,597
Because the view can't see the model, okay,
因为,view 是看不到 model 的
548
00:30:00,599 --> 00:30:03,934
but the controller can. So the controller would set itself
但是 controller 可以, controller会成为
549
00:30:03,936 --> 00:30:07,004
as the recognizer of anything that's gonna affect the model.
任何会影响 model 的识别器
550
00:30:07,006 --> 00:30:11,308
We'll see that in the demo as well. Okay, so how do we
我们会在过会儿的 demo 中讲这一点
551
00:30:11,310 --> 00:30:15,445
add a gesture to UIView? We've decided we want a view,
我们应该如何给 UIView 添加手势呢,在这个例子中
552
00:30:15,447 --> 00:30:18,515
in this case lets say to recognize pan.
我们决定让 view 能识别到拖动手势
553
00:30:18,517 --> 00:30:21,752
Okay a pan is putting your finger down on the screen and
拖动手势就是在屏幕上拖动你的手指
554
00:30:21,754 --> 00:30:23,287
moving it around without lifting it up.
拖动时并不抬起手指
555
00:30:23,289 --> 00:30:26,023
And when you lift it up the pan is over. Kay,
当手指抬起来的时候,拖动手势也就结束了
556
00:30:26,025 --> 00:30:27,157
that's what a pan is basically.
这是拖动手势基本的含义
557
00:30:27,159 --> 00:30:30,160
You're panning your finger around. On the screen.
你可以在屏幕上来回拖动你的手指
558
00:30:30,162 --> 00:30:33,831
All right? So if we wanted to do that. Well, one, so we have
如果我们想添加这样一个手势
559
00:30:33,833 --> 00:30:37,067
to somewhere in our controller code add a pan gesture
我们需要在 controller 的代码中创建一个 pan 手势识别器
560
00:30:37,069 --> 00:30:40,437
recognizer to the view that we want the panning to happen in.
并将它添加到我们想让拖动手势发生的 view 上
561
00:30:40,439 --> 00:30:45,142
Okay? Well a great place to do that is in that didSet, okay?
最好是在 didSet 方法中做这些事情
562
00:30:45,144 --> 00:30:48,111
Of the outlet. Cuz as soon as that view that
因为在获得这个 view 的同时
563
00:30:48,113 --> 00:30:48,145
as soon as we get a pointer to it, let's immediately just add
我们立即为他添加了手势
564
00:30:48,147 --> 00:30:51,381
we want to pan,
我们想要拖动时
565
00:30:51,383 --> 00:30:53,350
the gesture recognizer right there. Okay.
对应的手势识别器已经准备好了
566
00:30:53,352 --> 00:30:55,886
And we know that this didSet right here.
我们知道这里的 didSet 方法
567
00:30:55,888 --> 00:30:58,622
Okay we already saw this in the demo I just did. This
在刚刚的 demo 中我写过这个方法
568
00:30:58,624 --> 00:31:01,758
didSet, when you're doing it to an outlet. This gets called
当你处理一个 outlet 时
569
00:31:01,760 --> 00:31:05,963
only once. It gets called when iOS first hooks up that view.
他在 iOS 绘制这个 view 时刚好调用,且只调用一次
570
00:31:05,965 --> 00:31:10,567
Kay, perfect time to go add a gesture recognizer. Okay, so
这是一个添加手势识别器的完美时机
571
00:31:10,569 --> 00:31:14,938
we're gonna do that. First, we are going to create the pan
我们开始这么做,首先,创建手势识别器
572
00:31:14,940 --> 00:31:18,275
gesture recognizer. Okay, here is a concrete subclass of
PanGestureRecognizer 是 UIGestureRecognizer 的
573
00:31:18,277 --> 00:31:21,478
UIGestureRecognizer called pan gesture recognizer.
一个继承实体类
574
00:31:21,480 --> 00:31:26,617
It takes two arguments here. The first is who is going
他有两个参数
575
00:31:26,619 --> 00:31:31,822
to handle this gesture when I recognize it. Okay.
第一个是当识别到手势后,处理手势的对象
576
00:31:31,824 --> 00:31:33,957
So this gesture pan gesture recognizer saying hey,
当PanGestureRecognizer
577
00:31:33,959 --> 00:31:36,760
if I recognize a pan, who's gonna handle it for me?
识别到拖动手势后,会问谁帮我来处理这个手势呢?
578
00:31:36,762 --> 00:31:40,764
And the answer is, self, so that's the controller. Okay.
答案是,controller 自己来处理手势
579
00:31:40,766 --> 00:31:42,499
Cuz there's an outlet in the controller so
因为这是在一个 controller 中的 outlet
580
00:31:42,501 --> 00:31:44,968
the target itself means the controller is going to
target 为 self,指这个 controller 自己将会
581
00:31:44,970 --> 00:31:50,007
handle this pan itself. Okay. The second argument here.
处理拖动手势,第二个参数是
582
00:31:50,009 --> 00:31:55,379
Is what method do you want me to invoke in self?
在识别到这个手势后
583
00:31:55,381 --> 00:31:58,749
When this gets recognized, okay?
你想让我调用 self 中的什么方法?
584
00:31:58,751 --> 00:32:00,984
Now there's some kind of funky syntax here.
第二个参数的语法可能有些难以理解
585
00:32:00,986 --> 00:32:05,989
This has to be an Objective-C runtime compatible selector,
这是一种兼容 OC 运行时的选择器
586
00:32:05,991 --> 00:32:10,093
okay? That just means a selector, a method, okay,
仅仅意味着是一个选择器、一个方法
587
00:32:10,095 --> 00:32:10,928
the name of a method,
方法的名字
588
00:32:10,930 --> 00:32:14,631
that is visible to the Objective-C runtime. Now, for
对于 OC 运行时是可见的
589
00:32:14,633 --> 00:32:17,701
this to be visible to run to the Objective-C runtime,
因为他对于 OC 运行时是可见的
590
00:32:17,703 --> 00:32:21,538
this method must be in a class that inherits from NS object.
所以,这个方法必须出现在某个继承自 NSObject 的类中
591
00:32:21,540 --> 00:32:24,641
Remember I was talking about that NS object thing and
记得我之前说过关于 NSObject
592
00:32:24,643 --> 00:32:25,776
sometimes you're gonna need it, well,
有些时候你需要它
593
00:32:25,778 --> 00:32:28,345
here you would need it. Now you don't usually care about
此时我们就需要,但我们通常不用关心这个问题
594
00:32:28,347 --> 00:32:31,281
that here, because this is almost always being handled by
因为这个处理方法通常在一个
595
00:32:31,283 --> 00:32:34,952
either a UI view controller which definitely inherits from
UIViewController 中, 而这个UIViewController 绝对继承了
596
00:32:34,954 --> 00:32:35,619
NS objects or a UI view,
NSObject 又或者在一个 UIView 中
597
00:32:35,621 --> 00:32:38,322
which definitely inherits from NS object, right? So
UIView 也绝对继承了 NSObject
598
00:32:38,324 --> 00:32:41,692
you usually don't care, okay? You're not usually going to be
所以你通常不用关心这个问题,你不会
599
00:32:41,694 --> 00:32:44,761
trying to send this to some object that's a Swift object
尝试将这个方法给一个没有
600
00:32:44,763 --> 00:32:48,699
that doesn't inherit from NS object. But this syntax here
继承 NSObject 的 Swift 类
601
00:32:48,701 --> 00:32:53,070
means create an Objective-C compatible selector, okay?
这个语法表示创建一个 OC 运行时的兼容选择器
602
00:32:53,072 --> 00:32:56,573
Selector just means a kind of an identifier for a method.
selector 表示方法的一种标识符
603
00:32:56,575 --> 00:33:00,911
And here is just the class, a dot, and then the name of
这是处理方法的类、点,然后是
604
00:33:00,913 --> 00:33:04,648
the method including the argument names. Don't forget
包含参数名称的方法名称。
605
00:33:04,650 --> 00:33:07,951
the argument names here. Now, this pan method that I'm gonna
别忘了参数名称,我会在下一页说明
606
00:33:07,953 --> 00:33:11,989
show you on the next screen, it has one argument.
这个 pan 方法,只有一个参数
607
00:33:11,991 --> 00:33:14,958
That argument is the pan gesture recognizer.
而这个参数就是这个拖动手势识别器
608
00:33:14,960 --> 00:33:16,994
So just like when we have target action, and
就像我们有一个 target - action 方法
609
00:33:16,996 --> 00:33:19,863
we have a button that has the sender is sending itself.
一个 button 将自己作为 sender 传递
610
00:33:19,865 --> 00:33:22,332
Same thing here, when we have a pan gesture recognizer and
同样的,当我们有一个拖动手势识别器
611
00:33:22,334 --> 00:33:25,502
it starts to recognize, it sends this pan method
当它识别到手势时,它会将自己作为参数
612
00:33:25,504 --> 00:33:29,706
here to the view controller with itself as the argument.
调用 ViewController 的 pan 方法
613
00:33:29,708 --> 00:33:32,409
Now you might say, why is this underbar right here?
你可能会问,为什么有这个下划线呀?
614
00:33:32,411 --> 00:33:35,345
Why don't I have the name of whatever this first keyword
为什么对于这个参数不能有一个名字
615
00:33:35,347 --> 00:33:39,349
is? And that's because I don't really care what that name is.
这是因为,我们不关心它的名字是什么
616
00:33:39,351 --> 00:33:39,883
I just wanna make it clear,
我只想让它保持优雅整洁
617
00:33:39,885 --> 00:33:44,221
I want the version of pan that has one argument. Now if I had
我希望这个 Pan 方法仅有一个参数
618
00:33:44,223 --> 00:33:49,426
multiple, pan methods that had different first, names there,
如果我需要多个参数,那么这里就需要参数名称
619
00:33:49,428 --> 00:33:52,829
then I would have to put the name in there. But I can put
我需要写名称,但是我可以
620
00:33:52,831 --> 00:33:56,133
underbar here because underbar in Swift means, I don't care,
在这里写下划线,因为在 Swift 中,下划线表示我不关心
621
00:33:56,135 --> 00:33:59,269
whatever, okay? I don't really care what this is. It's
我确实不关心这个参数名称的问题
622
00:33:59,271 --> 00:34:02,272
a substitute for I don't care. So I don't really care here.
下划线是我不关心这个问题的代名词
623
00:34:02,274 --> 00:34:05,675
So I just wanna introduce that you can do this underbar here.
我只是想说明你可以在这里用下划线替代参数名称
624
00:34:05,677 --> 00:34:09,179
But if I don't put this _:) here, then it's gonna try and
但是,如果我在这里没有写 (_:)
625
00:34:09,181 --> 00:34:10,981
call a pan method that has no arguments.
它会调用一个没有参数的 pan 方法
626
00:34:10,983 --> 00:34:14,017
So be careful, if you want the one that has the gesture
在这里需要注意,如果你想将一个手势识别器
627
00:34:14,019 --> 00:34:17,854
recognizer as an argument, you gotta put that in there, okay?
作为参数调用,你得在这里写 (_:)
628
00:34:17,856 --> 00:34:21,458
So that's specifying the selector. And then I just turn
这是一个具体的选择器,然后
629
00:34:21,460 --> 00:34:25,562
on this recognizer by taking the view, this is the UIView,
我通过添加手势识别器到这个 view 也就是这个 pannableView
630
00:34:25,564 --> 00:34:30,434
this pannableView, okay? And adding this thing I created as
开启我的手势识别器
631
00:34:30,436 --> 00:34:34,504
a gesture recognizer, okay? As soon as I add that,
一旦我添加了手势识别器
632
00:34:34,506 --> 00:34:38,108
this view is gonna start recognizing pan gestures,
这个 view 就开始识别拖动手势
633
00:34:38,110 --> 00:34:41,378
okay? And when it does, it's gonna send this method to
当确实有拖动手势发生时,它会调用 controller
634
00:34:41,380 --> 00:34:46,516
self which is the controller, got it? Okay, makes sense,
中的处理方法
635
00:34:46,518 --> 00:34:48,985
that's what we're doing there? All right, so now,
我们在处理方法中干什么呢?
636
00:34:48,987 --> 00:34:50,420
let's talk about how we implement this handler?
现在,我们讨论一下如何实现处理方法
637
00:34:50,422 --> 00:34:52,656
This method right here, this pan thing, okay?
这个 pan 方法
638
00:34:52,658 --> 00:34:57,060
What does that look like to implement? To,
它应该如何处理呢?
639
00:34:57,062 --> 00:34:59,663
to understand how we're gonna implement such a method, we
为了明白我们在这个方法中如何处理手势事件
640
00:34:59,665 --> 00:35:02,766
needed to understand a little bit more about the concrete
我们需要知道关于
641
00:35:02,768 --> 00:35:04,668
subclasses of UI gesture recognizer. So
UIGestureRecognizer 继承实体类的一些知识
642
00:35:04,670 --> 00:35:09,272
let's look at pan, okay? So pan, UIPanGestureRecognizer,
关于UIPanGestureRecognizer
643
00:35:09,274 --> 00:35:12,943
it has a few methods on it that are specific to panning,
有一些专门处理拖动手势的方法
644
00:35:12,945 --> 00:35:15,045
okay? For example, it has translationInView.
比如,translationInView 方法
645
00:35:15,047 --> 00:35:19,783
It takes a UIView and tells you how far the pan has moved
它获取一个 UIView 参数,然后返回这个拖动手势
646
00:35:19,785 --> 00:35:23,053
in that view's coordinate system, okay?
在这个 view 的坐标中移动了多远
647
00:35:23,055 --> 00:35:25,789
Which is exactly what you want for a pan cuz you wanna know
这个方法确切的告诉你关于拖动手势你想知道的
648
00:35:25,791 --> 00:35:29,459
where you are, so calls wanted to know how far you moved?
也就是这个拖动手势移动了多远
649
00:35:29,461 --> 00:35:32,129
It even has velocity in view, we'll tell you how fast
甚至还有在这个 view 中,手指开始拖动的速度有多快
650
00:35:32,131 --> 00:35:34,998
the pan is happening, okay? If you're ripping it around
如果你在周围非常非常非常缓慢地
651
00:35:35,000 --> 00:35:36,833
versus going really really really really slow.
拖动手指
652
00:35:36,835 --> 00:35:39,002
Like if you had a drawing app, when you're going really slow,
像你有个画画的应用,当你非常缓慢地拖动手指
653
00:35:39,004 --> 00:35:40,270
you might be drawing really carefully.
这时就需要仔细绘图了
654
00:35:40,272 --> 00:35:41,271
And if you're zipping it around,
或者你在周围快速移动手指
655
00:35:41,273 --> 00:35:43,874
now you're making big arcs or something, who knows? And
你就需要画一个大的弧度之类的(因为手指移动速度快)
656
00:35:43,876 --> 00:35:47,511
it even has set translation, which is setting this.
在配置手势识别器时,甚至需要设置 translation
657
00:35:47,513 --> 00:35:48,845
Why would you ever want to set this?
为什么每次还得设置这个参数呢?
658
00:35:48,847 --> 00:35:53,583
Well, if you don't set this, okay? Then this translationVew
如果你不设置,之后
659
00:35:53,585 --> 00:35:56,620
is going to be the cumulative translationInView,
translationView 会变成一个递增的 translationView
660
00:35:56,622 --> 00:36:00,090
okay? It's how far the pan has moved since it started,
这个是你的拖动手势从开始到现在拖动的距离
661
00:36:00,092 --> 00:36:04,227
the cumulative thing. If you what you want instead is, how
是一个递增的值,如果你不设置
662
00:36:04,229 --> 00:36:07,831
much it's changed since last time you told me about it,
手势内容会是上一次你拖动的距离
663
00:36:07,833 --> 00:36:11,468
okay? You can constantly reset this to zero.
应将这个值重置为 0
664
00:36:11,470 --> 00:36:13,637
If you constantly reset this translation to zero,
如果你再下一次将得到这个值时
665
00:36:13,639 --> 00:36:14,504
then the next time you get it,
不断将它设置为 0
666
00:36:14,506 --> 00:36:18,408
it's gonna be the translation incremental, okay? Tiny bit,
这就会是一个递增的 translation
667
00:36:18,410 --> 00:36:21,411
little tiny bit it moves since the last time, okay?
基于每一次最后拖动的一点点距离
668
00:36:21,413 --> 00:36:24,748
So you'll see in the demo that we we will reset this to zero
一会儿在 demo 中你会看到我们每次将其重置为 0
669
00:36:24,750 --> 00:36:28,218
every time, cuz we just want the incremental panning, okay?
因为我们想要一个慢慢递增的拖动手势
670
00:36:28,220 --> 00:36:32,956
All right, so the abstract superclass of UI pan gesture
UIPanGesture 这个抽象的基类
671
00:36:32,958 --> 00:36:37,761
also provides a very important var, which is the state.
也提供了一些重要的属性,比如说手势的状态
672
00:36:37,763 --> 00:36:40,730
Okay, I told you that these gesture recognizers go through
我之前说过手势识别器是在不断检测
673
00:36:40,732 --> 00:36:44,568
a state machine, okay? And this is how you can find out
状态机的状态,你可以在处理方法中
674
00:36:44,570 --> 00:36:48,371
in your handler what state the gesture is in.
发现手势的状态
675
00:36:48,373 --> 00:36:52,409
So they all start around in this state possible, okay?
所以他们在 possible 状态开始识别
676
00:36:52,411 --> 00:36:55,478
UIGestureRecognizerState.Poss- ible. Then for
UIGestureRecognizerState.Possible
677
00:36:55,480 --> 00:36:59,015
a discrete gesture, like a swipe, once the swipe happens,
对于一个离散状态的手势,比方说扫动
678
00:36:59,017 --> 00:37:02,652
it immediately goes to the state recognized, okay?
这个手势很快就被识别到
679
00:37:02,654 --> 00:37:03,720
It recognized the swipe. So
手势识别器识别到扫动手势
680
00:37:03,722 --> 00:37:07,591
your handler gets called, the state will be recognized. For
处理方法被调用,这是状态就是 Recognize
681
00:37:07,593 --> 00:37:11,828
a continuous gesture like a pan or a pinch, okay? It goes
对于一个连续状态的手势,比方说拖动或捏合
682
00:37:11,830 --> 00:37:15,599
to the state Began as soon as the pan gets held down, and
当手势开始时,状态就是 Began
683
00:37:15,601 --> 00:37:18,768
as soon as the pan starts moving it keeps going to
手势一开始移动,状态就立即变为 Changed
684
00:37:18,770 --> 00:37:21,471
the state Changed. Change, change, change, change,
随着手势变化,状态一直是 Changed Changed Changed
685
00:37:21,473 --> 00:37:23,306
change, the handler keeps getting called over and
这样处理方法一直被调用调用调用
686
00:37:23,308 --> 00:37:25,208
over and over and over and over with Changed, okay?
因为状态一直是 Changed
687
00:37:25,210 --> 00:37:28,812
And every time this translation is changing, okay?
每次手势状态变化就会是 Changed 状态
688
00:37:28,814 --> 00:37:33,883
And then, when the finger goes up, then it goes to Ended.
最后,当手指抬起,状态就变为 Ended
689
00:37:33,885 --> 00:37:38,188
Make sense? Now there's couple other states here, Failed and
明白了吗?还有一些状态,Failed
690
00:37:38,190 --> 00:37:41,925
Cancelled. Like if you're doing the middle of a pan and
和 Cancelled,比如你正在做一个拖动手势
691
00:37:41,927 --> 00:37:43,760
a phone call comes in, okay?
一个电话进来了
692
00:37:43,762 --> 00:37:45,996
Then you're gesture is gonna get cancelled,
这个拖动手势就 Cancelled 了
693
00:37:45,998 --> 00:37:49,032
okay? So you might or might not have to deal with that.
通常你不用特别的处理这个状态
694
00:37:49,034 --> 00:37:52,035
If you're dealing with your pan incrementally,
如果前面一直在处理这个拖动手势
695
00:37:52,037 --> 00:37:54,638
then who cares if it gets interrupted?
对于它突然被打断,我们并不需要关心这个
696
00:37:54,640 --> 00:37:55,605
You were moving the thing incrementally.
因为你打断之前你已经做了相关处理了
697
00:37:55,607 --> 00:37:58,074
If you are dealing with your pan with some big
如果你需要在拖动手势结束时
698
00:37:58,076 --> 00:38:01,678
thing at the end, when the pan ends, then you better do in
做一些操作,你最好在
699
00:38:01,680 --> 00:38:04,547
here what you were gonna do when it ended, okay?
状态变为 Ended 时操作
700
00:38:04,549 --> 00:38:07,617
But a lot of times you don't care cuz you're doing all
通常我们不用担心被打断
701
00:38:07,619 --> 00:38:09,919
the work in Changed so Cancelled and Failed is
因为大多数时候,操作都在状态为 Changed 时完成了
702
00:38:09,921 --> 00:38:13,256
just like Ended but you don't notice the finger going up.
Cancelled 和 Failed 就类似与一种手指没抬起时就 Ended 状态
703
00:38:13,258 --> 00:38:16,793
Well, no big deal. Okay, so now that I have all this
这不是什么大问题,现在
704
00:38:16,795 --> 00:38:17,927
information about a pan gesture,
我有了关于拖动手势的所有信息
705
00:38:17,929 --> 00:38:21,831
how do I make a handler that handles a pan gesture, okay?
我的处理方法应该如何处理这个拖动手势呢?
706
00:38:21,833 --> 00:38:23,500
This is what the code would look like. This code is gonna
处理方法在这里
707
00:38:23,502 --> 00:38:25,969
be in my controller because I said the controller is gonna
这些应该写在 controller 中,因为我之前说过
708
00:38:25,971 --> 00:38:30,440
handle the pan. And first I'm gonna look at the state, okay?
controller应该处理这个手势, 首先我要看手势的状态
709
00:38:30,442 --> 00:38:35,111
Of the, okay, yeah, I'm sorry, here's that pan_:),
不是,先看 pan_:)
710
00:38:35,113 --> 00:38:37,380
you see, this pan takes an argument,
这个 pan 方法有一个参数
711
00:38:37,382 --> 00:38:39,983
okay? Instead of underbar there I could have put gesture
与写下划线相反的是,这里我写参数名为 gesture
712
00:38:39,985 --> 00:38:42,218
because that's what I called it here, gesture, but
因为后面是这么调用的,这是内部参数名
713
00:38:42,220 --> 00:38:45,188
I don't care. But, this one take an argument,
这里有一个参数
714
00:38:45,190 --> 00:38:47,190
you could have pan with no arguments here and
你也可以在这里不写参数
715
00:38:47,192 --> 00:38:50,293
nothing there and this would not pass the pan gesture along
不写参数的话就不会将拖动手势作为参数传递
716
00:38:50,295 --> 00:38:52,896
as an argument. But, here I need the pan gesture,
但在这里我需要这个拖动手势
717
00:38:52,898 --> 00:38:56,499
cuz I need to know how far the pan is gone, okay? So, next
因为我需要知道这个手势我拖动了多远
718
00:38:56,501 --> 00:39:00,370
I'm gonna switch on the state that the pan gesture is in and
下面,switch 这个拖动手势的 state
719
00:39:00,372 --> 00:39:05,608
if it's Changed Or Ended, then I am going to update something
如果 state 是 Changed 或 Ended,我希望更新
720
00:39:05,610 --> 00:39:08,311
in my controller, okay? Otherwise,
在 controller 中的一些东西
721
00:39:08,313 --> 00:39:10,947
I'm gonna ignore it, okay? If it's Cancelled or Failed or
剩下的 state 会直接被忽略
722
00:39:10,949 --> 00:39:15,151
if it Began, I don't care. I'm only interested when it moves,
在 Cancelled、Failed、Began 状态中不用处理,我只关心它移动的时候
723
00:39:15,153 --> 00:39:17,053
okay? So that's why I'm only looking at this.
这就是为什么我只找关于移动的状态
724
00:39:17,055 --> 00:39:19,656
Now, I wanna show you this kinda cool thing in Switch.
现在,我想给你看一些在 Switch 中很 cool 的东西
725
00:39:19,658 --> 00:39:22,459
Hopefully you got this in your homework? But there's this
希望你在作业中早已读过
726
00:39:22,461 --> 00:39:25,061
thing, fallthrough. Remember that Swift cases
就是 fallthrough,在 Swift 里
727
00:39:25,063 --> 00:39:28,631
don't fall through to the next case like they do in C, okay?
不像 C 跳转到下一个 case 方法(将 case 后语句置为空)
728
00:39:28,633 --> 00:39:31,368
But you can force them to fall through with fallthrough.
但是你可以通过添加 fallthrough 关键字跳转
729
00:39:31,370 --> 00:39:34,504
Now, I probably wouldn't write code like this,
代码也可以不这么写
730
00:39:34,506 --> 00:39:37,841
I would just say case.Changed, .Ended, okay?
直接写 case.Changed, .Ended
731
00:39:37,843 --> 00:39:39,442
That's a easier way to do this, but
这种方式更简单
732
00:39:39,444 --> 00:39:42,912
I just wanted to show you fallthrough, okay? All right,
我只是想秀一下 fallthrough 关键字
733
00:39:42,914 --> 00:39:46,616
so here inside, when things have Changed or Ended,
在这儿,当状态是 Changed 或 Ended
734
00:39:46,618 --> 00:39:50,653
I'm gonna get the translation in the pannableView. Okay,
可以得到 pannableView 的translation
735
00:39:50,655 --> 00:39:53,957
remember this to that outlet? I'm gonna get that translation
记得前面那个 outlet 吗?
736
00:39:53,959 --> 00:39:56,559
from the gesture. And then I'm gonna go and
获得手势的 translation
737
00:39:56,561 --> 00:39:59,629
update something that depends on where the pan is.
然后更新一些取决于手势发生位置的操作
738
00:39:59,631 --> 00:40:02,966
I've got, I know where the pan, how far it has moved. So
现在我知道手势在哪里发生了、手势挪动了多远
739
00:40:02,968 --> 00:40:06,836
I'm just gonna update what, something that needs that.
给需要这些信息的操作更新信息
740
00:40:06,838 --> 00:40:10,039
And then I'm gonna set the translation back to 0.
之后,将 translation 重置
741
00:40:10,041 --> 00:40:13,910
Okay, this also could be CGPoint.Zero if you want. But
需要的话,这将置为 CGPointZero
742
00:40:13,912 --> 00:40:15,078
see, I'm gonna set it back to 0 so
我想让它变为0
743
00:40:15,080 --> 00:40:17,113
that the next time I get called to my handler,
当下一次我调用处理方法时
744
00:40:17,115 --> 00:40:19,983
next time it comes back around, this translation will
又走到这一步
745
00:40:19,985 --> 00:40:23,520
be the incremental translation from the last time it sent it
translation 将从上一次我拖动到的地方继续拖动
746
00:40:23,522 --> 00:40:28,992
to me. Everybody get that? Please? Okay? All right.
大家明白吗?
747
00:40:28,994 --> 00:40:30,560
So that's it. That's what the handler looks like.
在处理方法中大概就写这些
748
00:40:30,562 --> 00:40:32,896
Pretty straight forward. Okay. We'll see this in the demo,
处理的方法很简单,我们待会儿在 demo 中会继续讲这些内容
749
00:40:32,898 --> 00:40:36,933
it's not too bad, okay? Let's briefly talk about some of
我们简单讲一下除了拖动手势
750
00:40:36,935 --> 00:40:39,102
the other concrete gestures besides pan,
其他的手势实体类
751
00:40:39,104 --> 00:40:41,571
there's pinches. Pinches is two fingers down,
比如捏合手势,捏合手势指
752
00:40:41,573 --> 00:40:44,741
pinching in and out like this, okay? And here what
两个手指像这样
753
00:40:44,743 --> 00:40:48,378
you're getting instead of the translation, it is the scale.
754
00:40:48,380 --> 00:40:51,581
So if I start my fingers here and I bring them out twice as
如果我把手指间距放宽到之前两倍
755
00:40:51,583 --> 00:40:55,185
wide that will be a scale of two. If I start him here and
这样 scale 会变为 2
756
00:40:55,187 --> 00:40:57,654
move him in half way, that's a scale 0.5.
如果我缩小到一半,scale 会变为 0.5
757
00:40:57,656 --> 00:41:01,791
Okay, so I'm scaling whatever on my screen and again I can
重复的在屏幕上进行捏合操作
758
00:41:01,793 --> 00:41:06,663
get velocity, how fast I'm pinching. Rotation is two
我可以得到 velocity 参数,代表我捏合的速度
759
00:41:06,665 --> 00:41:10,767
fingers turning them, okay. Like you're turning a knob and
旋转指两根手指转圈的手势,就像你旋转把手时
760
00:41:10,769 --> 00:41:14,337
it will tell you how many radians you've turned, okay.
会知道你旋转了多少度
761
00:41:14,339 --> 00:41:18,374
And again velocity, how fast you're turning. Swipe gesture,
这个手势也有 velocity 参数表示你旋转的速度
762
00:41:18,376 --> 00:41:21,544
okay. The swipe gesture you usually have to configure
轻扫手势,这个手势通常需要你配置参数
763
00:41:21,546 --> 00:41:24,147
before you use it. You have to say, do you want a left
在你用之前,你必须标明扫动方向
764
00:41:24,149 --> 00:41:27,484
going swipe, or right going swipe or up or down. Okay, and
是左是右,是上是下
765
00:41:27,486 --> 00:41:31,421
also two fingers swipe, or one finger or three fingers okay.
也需要配置手势是一个、两个还是三个手指操作
766
00:41:31,423 --> 00:41:34,324
You have to configure that then you add the gesture
你需要在添加手势之前配置好这些参数
767
00:41:34,326 --> 00:41:37,026
recognizer and it will only recognize it if all those
在当前手势符合这些你配置的参数之后
768
00:41:37,028 --> 00:41:39,896
things are true. Two finger swipe up has all the,
手势才会被识别到,你想识别到两根手指在这里扫动
769
00:41:39,898 --> 00:41:41,397
it has to be a two finger swipe up.
你的手势识别器就必须设置为两根手指的扫动
770
00:41:41,399 --> 00:41:44,334
Otherwise it doesn't recognize it, okay. So
不然的话,这个手势就不能被识别到
771
00:41:44,336 --> 00:41:48,037
that's swipe. Tap, which seems like it would be discreet,
这就是轻扫手势,下面轻击手势
772
00:41:48,039 --> 00:41:49,939
is not quite a discreet gesture, but
轻击手势不是一个很单一的手势
773
00:41:49,941 --> 00:41:52,108
you also configure it with how many taps. You know,
你仍需要配置点几下
774
00:41:52,110 --> 00:41:55,945
is it a single tap, or a double tap? And two fingers,
只是点一下还是双击?
775
00:41:55,947 --> 00:41:59,415
or one? Okay, you configure that, add it, and here, you're
是两根手指点还是一根? 你需要配置这些,添加手势
776
00:41:59,417 --> 00:42:03,853
gonna look for .ended to find out when the tap has happened,
然后当手势开始后,识别手势状态是否结束
777
00:42:03,855 --> 00:42:07,290
okay. That's because a tap actually has intermediate
这是因为轻击的手势有中间状态
778
00:42:07,292 --> 00:42:10,593
things going on like a double tap has a, the first tap. And
比方说,双击时第一下点完到第二下的状态
779
00:42:10,595 --> 00:42:12,929
then the second tap. And it might go through a state
这会在你处理方法中经历一个
780
00:42:12,931 --> 00:42:16,533
change that will call your handler, but
状态的变化
781
00:42:16,535 --> 00:42:20,236
it's not done yet. Okay and you can't look at recognized,
但是手势仍没结束,这时识别器就无法识别
782
00:42:20,238 --> 00:42:23,139
at recognized with tap because it's not a discreet gesture.
因为这不是一个简单手势
783
00:42:23,141 --> 00:42:25,675
Because it goes through those intermediate phases.
它经历了一个中间的状态
784
00:42:25,677 --> 00:42:29,245
So you really look for .ended for tap, okay.
所以你确实需要找到 ended 状态
785
00:42:29,247 --> 00:42:33,283
All right, so let's do a demo. Let's see all this in action
好的,现在我们开始在 demo FaceIt 中添加这些操作
786
00:42:33,285 --> 00:42:38,054
with our facet and what we're gonna do. First, I'm gonna add
首先需要做什么,我先添加
787
00:42:38,056 --> 00:42:41,190
a pinch gesture that makes our face get bigger and
一个可以让 face 变大变小的捏合手势
788
00:42:41,192 --> 00:42:43,493
smaller. Okay, we're just gonna pinch in and out and
通过手势的捏合调整 face 的大小
789
00:42:43,495 --> 00:42:47,530
adjust our scale. And then I'm gonna add a pan gesture.
然后,我需要添加一个拖动手势
790
00:42:47,532 --> 00:42:48,398
Actually I'll probably do a swipe.
哦不,我加的是轻扫手势
791
00:42:48,400 --> 00:42:53,002
Let's do a swipe gesture, that modifies our model, okay.
我们用轻扫手势修改我们的模型
792
00:42:53,004 --> 00:42:55,939
We're gonna use it to make our model happier or sadder.
用这个手势使模型快乐一点或者悲伤一点
793
00:42:55,941 --> 00:42:59,375
If I make it happier or sadder, then we'll adjust our
如果模型中的数据变化了,我们需要调整
794
00:42:59,377 --> 00:43:04,914
face view, okay. Let's do that. Back to our facet here.
faceView 的状态,好了,回到FaceIt工程中
795
00:43:04,916 --> 00:43:09,419
All right, so how are we gonna do this?
我们如何做呢?
796
00:43:09,421 --> 00:43:12,722
First thing we need to do is add a gesture recognizer,
首先,我们需要添加手势识别器
797
00:43:12,724 --> 00:43:16,759
okay. And again I said inside faceView didset is a good
再重复一遍我之前说的,faceView 的 didSet 方法是一个
798
00:43:16,761 --> 00:43:19,095
place to do that. So now I'm gonna expand this back out.
合适的时机添加手势识别器,现在我需要把括号中内容扩大
799
00:43:19,097 --> 00:43:21,998
Because I'm gonna add more code than just that one,
因为这里有一些代码超过一行了
800
00:43:22,000 --> 00:43:25,034
thing here, okay. And I'm still gonna update you,
添加完手势识别器,updateUI
801
00:43:25,036 --> 00:43:28,037
UI here, but I'm also going to add a gesture recognizer.
我添加完了手势识别器
802
00:43:28,039 --> 00:43:30,807
I know that this didSet is only gonna be called once
这个 didSet 方法只会在
803
00:43:30,809 --> 00:43:34,110
when my outlet is first hooked up to my UI, so that's perfect
outlet 绘制在UI上时被调用一次
804
00:43:34,112 --> 00:43:36,512
cuz I only wanna add the gesture recognizer once. And
所以这是一个添加手势识别器的完美时机
805
00:43:36,514 --> 00:43:39,582
I know that the faceView is the guy who has to recognize
faceView 需要识别到这个手势
806
00:43:39,584 --> 00:43:43,953
the gesture. Only views can recognize gestures. So
只有 view 可以识别手势
807
00:43:43,955 --> 00:43:48,024
I'm going to add a gesture recognizer.
我添加一个手势识别器
808
00:43:48,026 --> 00:43:50,994
Okay and you can see that when you add a gesture
你可以发现,当你添加一个手势识别器
809
00:43:50,996 --> 00:43:52,462
recognizer to a UI view it takes
给一个 view
810
00:43:52,464 --> 00:43:56,132
a gesture recognizer an argument. So I'm gonna create
它会把这个手势识别器当做参数
811
00:43:56,134 --> 00:44:00,703
one here, a UI pinch gesture recognizer, okay. And
所以我在这儿写 UIPinchGestureRecognizer
812
00:44:00,705 --> 00:44:04,374
that recognizer takes that target and action as they are,
创建这个识别器需要 target 和 action
813
00:44:04,376 --> 00:44:07,443
as the two arguments, right? So the target here,
作为两个参数
814
00:44:07,445 --> 00:44:11,381
in this case, since that pinch is only changing the scale
在这个例子中,因为捏合手势只是改变了
815
00:44:11,383 --> 00:44:13,216
of the view, it's not changing my model,
这个 view 的大小,并不会改变我的model
816
00:44:13,218 --> 00:44:18,488
I'm gonna let the face view handle this thing itself. Now,
所以我让 FaceView 自己处理这个手势
817
00:44:18,490 --> 00:44:21,157
for the FaceView to handle it, it's going to have to make
如果 FaceView 自己处理这个方法,但我需要在controller中
818
00:44:21,159 --> 00:44:25,728
some sort of gesture handler available to my controller.
调用这个方法,这个 FaceView 的方法必须可以让 controller 调用到
819
00:44:25,730 --> 00:44:28,765
So it's gonna have the public method right,
所以我需要在这里写一个 public 方法
820
00:44:28,767 --> 00:44:30,133
that handles a pinch.
来处理 pinch 手势
821
00:44:30,135 --> 00:44:32,969
So we're gonna have to add that to FaceView which will do
下一步,我们应该给 FaceView 添加处理方法
822
00:44:32,971 --> 00:44:36,606
next. And the action here is gonna be a selector,
这里的 action 必须是一个 selector
823
00:44:36,608 --> 00:44:41,477
it's gonna be a FaceView selector. FaceView selector.
是 FaceView 的 selector
824
00:44:41,479 --> 00:44:44,614
And I'm gonna call it changeScale, okay.
我叫它 changeScale
825
00:44:44,616 --> 00:44:49,318
And I am definitely going to need the argument there.
我这里确实需要参数
826
00:44:49,320 --> 00:44:52,422
So I'm gonna do that, as well, okay.
我马上添加参数
827
00:44:52,424 --> 00:44:56,459
Maybe this is a little easier to see if I do this.
我处理的方法很简单
828
00:44:56,461 --> 00:45:02,865
Like that. Okay. So it gives me an error here because
这儿有一个 error
829
00:45:02,867 --> 00:45:05,635
of course this method doesn't exist. So it's nice that it
因为这个方法还没有写,这样很好
830
00:45:05,637 --> 00:45:08,404
actually checks to make sure that this exist, okay.
因为每次会检查这个方法是否存在
831
00:45:08,406 --> 00:45:11,140
So let's go ahead and add this method right here, okay. This
现在让我们直接给 FaceView 添加这个方法
832
00:45:11,142 --> 00:45:14,644
is the method that's going to handle when a pinch happens,
这个方法将在每次捏护手势发生时被调用
833
00:45:14,646 --> 00:45:16,446
okay. We're gonna put that in our FaceView, so
我们在 FaceView 中写这个方法
834
00:45:16,448 --> 00:45:18,347
I'm gonna go over here to FaceView.
回到 FaceView
835
00:45:18,349 --> 00:45:20,883
Put it up here towards the top since it's gonna be public,
在上面一点写这个方法,因为他是公用的方法
836
00:45:20,885 --> 00:45:23,486
okay. It has to be public, because the controller
因为 controller 希望在处理手势的时候
837
00:45:23,488 --> 00:45:27,657
wants the gesture handler to call it. So changeScale. It's
调用这个方法,改变 FaceView 的大小
838
00:45:27,659 --> 00:45:31,427
going to have a recognizer as its argument, okay?
这里需要将一个手势识别器作为参数
839
00:45:31,429 --> 00:45:36,566
That UIPinchGestureRecognizer. Okay,
这个参数是 UIPinchGestureRecognizer
840
00:45:36,568 --> 00:45:39,902
so it's nice that when we're asked to handle the gesture,
每次我们需要处理这个手势时,
841
00:45:39,904 --> 00:45:42,505
the recognizer is passed along to us.
controller 将识别器作为参数传递给我们,这样很好
842
00:45:42,507 --> 00:45:44,540
And, this is really easy to implement,
实现起来也很简单
843
00:45:44,542 --> 00:45:49,879
I'm just gonna switch on the recognizer state. And
我需要 switch 手势识别器的状态
844
00:45:49,881 --> 00:45:52,115
if the state is changed or
如果这个状态 Changed
845
00:45:52,117 --> 00:45:57,186
ended, and yes you can do it that way in switches, in Swift
或者 Ended ,在 Swift 你可以这么写
846
00:45:57,188 --> 00:46:00,656
you can have multiple things there. If it's changed or
你可以写多个 case 在这里
847
00:46:00,658 --> 00:46:03,993
ended then I'm just gonna set my scale to be multiplied
如果手势状态 Changed 或者 Ended,我需要通过
848
00:46:03,995 --> 00:46:08,965
by the recognizer scale. Okay cuz here's my scale up here.
手势的大小设置 view 的大小,这是原来的 scale
849
00:46:08,967 --> 00:46:13,436
And so if this pinch goes 2.0 twice as wide,
如果捏合手势变成两倍大小
850
00:46:13,438 --> 00:46:16,272
then I want my scale to be twice of what it was. And
然后我就需要重置 scale 的大小为两倍
851
00:46:16,274 --> 00:46:17,673
if it goes down to 0.5, I want my scale
如果手势变为原来一半,我的 scale
852
00:46:17,675 --> 00:46:20,743
to be 0.5 of what it was. Okay, but this is gonna
就需要变为 0.5 这个过程
853
00:46:20,745 --> 00:46:24,380
be repeatedly called as that thing is moving in and out, so
随着手势的变化会被重复调用
854
00:46:24,382 --> 00:46:30,219
I need to make sure here that I reset the recognizer scale
所以我总是需要重置识别器的大小为1
855
00:46:30,488 --> 00:46:33,589
to 1.0 all the time. So that, as it moves out,
随着手势变化
856
00:46:33,591 --> 00:46:37,126
it's going to be give me the incremental scale in and out.
这里需要一个递增的 scale 值
857
00:46:37,128 --> 00:46:40,630
Because I'm actually applying the scale change every single
因为每次移动时都会应用这个 scale
858
00:46:40,632 --> 00:46:45,468
time it moves, every .Changed here I'm applying the scale,
在手势 Changed 时,我需要应用 scale
859
00:46:45,470 --> 00:46:49,005
okay. Now of course we need to have the default,
当然也需要这个 default
860
00:46:49,007 --> 00:46:53,709
state which is cancel, it covers cancelled and began and
当手势 Cancel 等状态,他包括了Cancel、Began
861
00:46:53,711 --> 00:46:53,876
all those things.
等等状态
862
00:46:53,878 --> 00:46:56,179
And it those case statuses I'm just gonna do nothing because
当识别到这些状态我什么也不做
863
00:46:56,181 --> 00:46:59,215
I'm actually tracking the scale and implementing it
因为实际上追踪 scale 变化和执行这些变化
864
00:46:59,217 --> 00:47:02,585
as it goes along, so I don't even care when it ends, okay.
是一直进行的,所以不用关心什么时候手势结束了
865
00:47:02,587 --> 00:47:06,389
I mean, it's nice when the fingers go up to do that one
在手指抬起时再做一次
866
00:47:06,391 --> 00:47:09,158
last adjustment of the scale but it's not crucial. Okay,
尺寸大小的调整是很好,但是并不是必须的
867
00:47:09,160 --> 00:47:11,661
and if we were cancelled, we wouldn't get that opportunity
当他 Cancelled 我们不需要获取到这个时机
868
00:47:11,663 --> 00:47:15,131
anyway. Okay, everyone understand that?
大家明白了吗?
869
00:47:15,133 --> 00:47:21,737
This is a public method, yeah. >> [INAUDIBLE]
这是一个 public 方法
870
00:47:21,739 --> 00:47:22,238
>> Well, yeah, see I'm, this
[提问]
871
00:47:22,240 --> 00:47:24,507
is why I ask three or four times, everybody getting this.
这就是为什么我问了三四次大家明白了吗
872
00:47:24,509 --> 00:47:25,808
And everyone was kind of like yeah, I think so.
大家都是一副我懂了的样子
873
00:47:25,810 --> 00:47:30,179
Okay. The reason for this is, if I don't do this, okay,
为什么这么做呢?因为如果我不重置 scale 大小
874
00:47:30,181 --> 00:47:34,517
then as I pinch, I'm gonna get the cumulative scale, okay. So
在 pinch 手势中,我会得到一个累积的 scale
875
00:47:34,519 --> 00:47:37,520
let's say I start here, okay. And I start moving it out,
我这么说,手势开始移动来
876
00:47:37,522 --> 00:47:39,021
moving it out. And it's getting called changed,
移动去,它会调用 Changed
877
00:47:39,023 --> 00:47:43,726
change, change, change, okay. First time it's 1.1, 1.2, 1.3,
Changed、Changed,scale 会变为 1.1, 1.2, 1.3,
878
00:47:43,728 --> 00:47:46,128
1.4, 1.5, 1.6, 2.0, okay.
1.4, 1.5, 1.6, 2.0
879
00:47:46,130 --> 00:47:49,198
So it's getting called seven times with all those numbers.
这些数字代表 Changed case 会被执行七次
880
00:47:49,200 --> 00:47:52,501
Imagine if I took my scale and multiplied it times by 1.0.
想象一下,我首先将这个 scale 乘以 1.0
881
00:47:52,503 --> 00:47:54,937
And then I multiplied it times 1.2, 1.3, 1.4.
之后乘以 1.2, 1.3, 1.4.
882
00:47:54,939 --> 00:47:57,306
Now the scale's getting gigantic. Okay,
现在这个 scale 会变得十分巨大
883
00:47:57,308 --> 00:48:00,943
that's not what I want. What I want is the incremental scale.
这可不是我想要的,我想要的是一个递增的 scale
884
00:48:00,945 --> 00:48:03,679
So if I keep resetting it to 1, then when it gets a little
所以我一直将 scale 重置为1
885
00:48:03,681 --> 00:48:07,116
bigger, I'm gonna get 1.01 again, okay? Reset to 1,
当 scale 变大时,会乘以 1
886
00:48:07,118 --> 00:48:11,621
1.01 again, you see? Exactly, I'm not by the scale. Now,
再乘以 1.01 你明白了吗?我不会直接乘以之前的 scale
887
00:48:11,623 --> 00:48:14,757
the alternative is, I could just have the pinch do nothing
也可以在这里不重置
888
00:48:14,759 --> 00:48:17,793
until you let go and then I could set the scale, okay?
直接让 scale 变化,之后再重置
889
00:48:17,795 --> 00:48:18,928
But, since I'm doing it incrementally,
但是,因为我想要让 scale 递增
890
00:48:18,930 --> 00:48:23,966
I have to keep resetting it to 1, okay? Good question. All
就需要每次将它重置为 1
891
00:48:23,968 --> 00:48:27,436
right, so now if we go back to our FaceViewController,
现在,我们回到 FaceViewController 中
892
00:48:27,438 --> 00:48:29,939
you'll see that we don't have an error here anymore on this
你可以看到这时这里就不再有 error 了
893
00:48:29,941 --> 00:48:33,876
line because we've actually defined this handler, okay? So
因为我确实定义了处理手势的方法
894
00:48:33,878 --> 00:48:39,415
now when we run, and we pinch. We're gonna update the scale,
现在我们运行、做捏合手势,这个 view 的 scale 就会被更新了
895
00:48:39,417 --> 00:48:40,683
hopefully, of our face.
希望如此
896
00:48:40,685 --> 00:48:42,952
So here's our face. He doesn't look very happy, but
这是 face ,它看起来不怎么高兴
897
00:48:42,954 --> 00:48:45,755
I'm going to make him smaller, so we won't notice, okay? Now,
但是一会儿我们让它高兴些,现在先不用在意
898
00:48:45,757 --> 00:48:48,958
you might ask, how am I gonna pinch with my mouse, okay?
你可能会问,我只有一个鼠标,怎么做捏合手势
899
00:48:48,960 --> 00:48:51,594
I've got my mouse here, I can't, just a little,
我把鼠标放这里,怎么捏合呢?
900
00:48:51,596 --> 00:48:55,564
how am I gonna do it? And the answer is, Option, okay?
答案是,Option 键
901
00:48:55,566 --> 00:48:56,198
If you're running the simulator and
如果你运行模拟器
902
00:48:56,200 --> 00:48:59,669
you hold down Option, look. Two fingers come up there,
按下 Option键后,就出现两根手指了
903
00:48:59,671 --> 00:49:04,874
okay? Now you can mouse down and drag and it's like you’re
你可以用鼠标移动它
904
00:49:04,876 --> 00:49:09,512
pinching. You see, pinching in pinching out,
看起来你想在做捏合手势,放大放小
905
00:49:09,514 --> 00:49:15,351
okay? Got it, so you can see these gesture hands
你看这些手势
906
00:49:15,353 --> 00:49:19,255
are super easy to implement, okay? We probably had four
做起来很简单的,我们可能
907
00:49:19,257 --> 00:49:22,725
lines of code there, of actual lines of code. All right,
就写了几行代码吧
908
00:49:22,727 --> 00:49:25,761
now, let's do another gesture recognizer, okay? Let's have
现在,我们添加另一个手势识别器
909
00:49:25,763 --> 00:49:28,464
this one modify our model. So what I'm gonna do here is,
这个手势会改变我们的模型,所以我需要在这里
910
00:49:28,466 --> 00:49:32,368
I'm gonna make it so that when I swipe down, he gets sadder.
当我向下扫动时,它看起来更悲伤
911
00:49:32,370 --> 00:49:33,069
And when I swipe up,
向上扫动时
912
00:49:33,071 --> 00:49:37,039
he gets happier in a model sense, okay? Okay,
它看起来高兴一点
913
00:49:37,041 --> 00:49:38,007
I'm gonna make the model happier.
需要改变 model 属性让它更高兴
914
00:49:38,009 --> 00:49:40,643
I'm not gonna move my mouth curvature. I'm gonna change
我需要设置嘴巴弯曲的程度,这将会改变 model 属性
915
00:49:40,645 --> 00:49:42,812
the model. Now of course, every time I change the model,
每一次我改变了 model
916
00:49:42,814 --> 00:49:44,981
that's going to change my mouth curvature. So
就会改变嘴巴的弯曲程度
917
00:49:44,983 --> 00:49:47,149
it's gonna work. But we're gonna change the model.
我们需要改变 model
918
00:49:47,151 --> 00:49:50,353
So to do that, I'm gonna do a swipe gesture.
为了实现这个功能,我将加一个轻扫的手势
919
00:49:50,355 --> 00:49:51,887
Now, a swipe gesture we have to configure.
轻扫手势需要我们做一些配置
920
00:49:51,889 --> 00:49:55,725
So I'm to create a local variable to hold it, okay?
创建一个局部变量保存这个手势识别器
921
00:49:55,727 --> 00:50:00,396
I am gonna call this local variable,
这个局部变量叫做
922
00:50:00,398 --> 00:50:05,201
my happierSwipeGestureRecognizer,
happierSwipeGestureRecognizer
923
00:50:05,203 --> 00:50:10,806
and so I say UISwipeGestureRecognizer.
UISwipeGestureRecognizer 方法创建
924
00:50:10,808 --> 00:50:11,007
SwipeGestureRecognizer and same arguments though,
与之前一样的参数
925
00:50:11,009 --> 00:50:15,411
Oops,
926
00:50:15,413 --> 00:50:19,982
target and action, okay? So the target here, okay,
需要设置 target 和 action,target 就是这个
927
00:50:19,984 --> 00:50:23,953
is not going to be the faceView because it modifies
target 不会是 faceView 了
928
00:50:23,955 --> 00:50:28,457
the model, it's gonna be self. Okay,
因为这个手势会改变 model,应该是 self
929
00:50:28,459 --> 00:50:31,560
controller has to be the one handling this one, okay?
controller 应该来处理这个手势
930
00:50:31,562 --> 00:50:36,032
And the action, which is the selector, is going to be
action 应该是一个 controller 的方法
931
00:50:36,501 --> 00:50:41,804
a FaceViewController selector, not a FaceView one.
并不是 FaceView 中的方法了
932
00:50:41,806 --> 00:50:45,107
And we'll call this thing we'll do,
我们会调用这个处理方法
933
00:50:45,109 --> 00:50:48,511
we'll make this be the happier one, so this is swipe up, so
我们希望这个方法能让 FaceView 开心些
934
00:50:48,513 --> 00:50:52,748
we'll call this increaseHappiness, okay?
这个方法被命名为 increaseHappiness
935
00:50:52,750 --> 00:50:55,017
And I don't need the swipe gesture
调用这个方法时,我就不需要这个轻扫手势了
936
00:50:55,019 --> 00:50:57,353
because it's either gonna be recognized or not. So
可以将它作为参数传递或者不传
937
00:50:57,355 --> 00:50:58,854
I'm not gonna have any arguments
我不需要任何参数
938
00:50:58,856 --> 00:51:03,092
to that increaseHappiness method, okay?
来调用 increaseHappiness
939
00:51:03,094 --> 00:51:05,061
Okay, I'll do the same thing here so
这里我会写这个
940
00:51:05,063 --> 00:51:08,297
you can see this a little better, okay? So this is not,
这样写好一些
941
00:51:08,299 --> 00:51:11,233
this guy right here is not going to have any arguments.
这个方法不需要写任何参数
942
00:51:11,235 --> 00:51:15,304
So that's gonna be a func, okay? increaseHappiness,
所以只是一个 increaseHappiness
943
00:51:15,306 --> 00:51:18,641
no arguments. And when the increaseHappiness happens,
不包括参数,当 increaseHappiness 被调用时
944
00:51:18,643 --> 00:51:22,144
what are we gonna do to our model, okay? Well,
我们需要修改model
945
00:51:22,146 --> 00:51:25,581
we're just gonna say that our facial expression's mouth
我们 expression.mouth
946
00:51:25,583 --> 00:51:30,753
equals the expression's mouth.happierMouth.
等于 expression.mouth.happierMouth
947
00:51:30,755 --> 00:51:34,323
Okay, so if you look at our facial expression over here,
如果你回过头看 FacialeExpression 类
948
00:51:34,325 --> 00:51:37,293
okay, mouth okay?
枚举类型 Mouth
949
00:51:37,295 --> 00:51:40,863
This enum has two methods, sadderMouth and happierMouth,
这个枚举有两个方法,sadderMouth 和 happierMouth
950
00:51:40,865 --> 00:51:44,133
which will take the current mouth and make it sadder or
这个方法将改变当前的表情,更悲伤
951
00:51:44,135 --> 00:51:47,236
happier and return a new mouth, okay? So
或者更高兴,并且返回一个新的 Mouth 枚举类型
952
00:51:47,238 --> 00:51:51,941
it's just gonna go more happy or less happy along here.
使表情高兴一点或者是更高兴
953
00:51:51,943 --> 00:51:57,880
Okay, so we got that. Now so that's gonna be fine.
好了,我们现在写完了
954
00:51:57,882 --> 00:52:02,017
This swipe gesture though, we have to add it to our view.
这个轻扫手势我们需要添加到 view 上
955
00:52:02,019 --> 00:52:03,085
We have to do addGestureRecognizer.
我们需要 addGestureRecognizer
956
00:52:03,087 --> 00:52:06,021
We haven't done that. And we also have to configure it.
这一步还没写,我们还需要配置这个手势识别器
957
00:52:06,023 --> 00:52:08,791
So let's configure it. This is happier. So
这个是更高兴的手势
958
00:52:08,793 --> 00:52:13,596
I'm gonna have the hype, the SwipeGestureRecognizerDirect-
所以我需要配置 SwipeGestureRecognizerDirection
959
00:52:13,598 --> 00:52:18,834
ion. Okay, which is the direction of the swipe,
设置轻扫手势的 direction 为
960
00:52:18,836 --> 00:52:22,138
= .Up. Okay, so you're swiping up, that's for
Up, 好了,当你向上扫动时
961
00:52:22,140 --> 00:52:23,572
happier, and now we, and
表情看起来会更高兴
962
00:52:23,574 --> 00:52:28,177
it will have it be a single finger swipe, not two. And so
是一个手指扫动不是两个
963
00:52:28,179 --> 00:52:33,382
now I can just say faceView.addGestureRecognizer-
现在,我们可以给 faceView 添加手势识别器
964
00:52:33,384 --> 00:52:38,988
(happierSwipeGestureRecogniz- er), okay?
happierSwipeGestureRecognizer
965
00:52:39,357 --> 00:52:43,259
Make sense there? All right, let's go try this.
明白了吗?
966
00:52:43,261 --> 00:52:45,761
Now notice every time we change the expression here,
现在每一次我们改变了 expression
967
00:52:45,763 --> 00:52:48,430
it's gonna automatically update our UI because of this
将会自动更新 UI
968
00:52:48,432 --> 00:52:52,201
didSet. You see that? We changed this expression, boom,
因为expression 的 didSet 方法里
969
00:52:52,203 --> 00:52:55,571
updateUI. So, let's try it. All right, so,
有 updateUI 再运行一次
970
00:52:55,573 --> 00:52:56,238
hopefully this is still working.
希望能奏效
971
00:52:56,240 --> 00:52:58,741
Pinching, yes, still working. Now I'm gonna swipe up and
捏合手势还可以进行,现在我想
972
00:52:58,743 --> 00:53:03,245
sure enough, he's getting happier and happier. Okay, but
向上扫动,它变得高兴一点儿了
973
00:53:03,247 --> 00:53:05,481
we better do swipe down also, okay?
但是向下扫动没有效果
974
00:53:05,483 --> 00:53:08,350
We don't have all only swipe up, let's get swipe down in
我们不能只写向上扫动的方法,还得写向下扫动的
975
00:53:08,352 --> 00:53:10,719
there. In fact, I'm gonna do that by copying and
事实上,因为向上和向下的代码差不多一样
976
00:53:10,721 --> 00:53:13,455
pasting because it's almost the same as this.
我要复制粘贴这段代码
977
00:53:13,457 --> 00:53:18,360
I'm just gonna replace this happier here with sadder.
将 happier 替换为 sadder
978
00:53:21,465 --> 00:53:25,501
And instead of it being an up swipe, it's gonna be a down
将向上替换为向下
979
00:53:25,503 --> 00:53:28,871
swipe. And instead of increasing the happiness,
增加高兴值变成
980
00:53:28,873 --> 00:53:30,973
it's going to decrease the happiness.
降低高兴值
981
00:53:30,975 --> 00:53:37,479
So I need a new method here called decreaseHappiness.
现在我需要一个新的方法叫 decreaseHappiness
982
00:53:38,216 --> 00:53:42,451
And this one's going to get the sadderMouth, okay?
设置为 sadderMouth
983
00:53:42,453 --> 00:53:45,454
Everyone understand, it's real important to understand here
大家都明白了吗?明白这个十分重要
984
00:53:45,456 --> 00:53:48,457
that we have recognizers that as they're being handled,
我们写好了手势识别器
985
00:53:48,459 --> 00:53:49,658
they're being handled by the controller.
手势被 controller 所识别
986
00:53:49,660 --> 00:53:53,829
And the controller's modifying the model. And since, and
controller 也将改变 model
987
00:53:53,831 --> 00:53:56,865
every time the model gets modified, it calls updateUI.
每一次,model变化了,就会更新 UI
988
00:53:56,867 --> 00:54:00,469
updateUI is the thing that's going to update, in this case,
updateUI 就是在这个例子中就是更新 faceView
989
00:54:00,471 --> 00:54:04,940
the mouth curvature. Everybody understand that flow? Okay, so
嘴巴的弯曲程度,大家都明白这个流程了吗?
990
00:54:04,942 --> 00:54:11,647
let's make sure that swipe down is working. All right,
现在让我们看看轻扫手势生效了吗
991
00:54:11,649 --> 00:54:16,118
so swipe down, sad. Swipe up, happy, okay? Sad,
向下扫动,变悲伤,向上扫动,变高兴了
992
00:54:16,120 --> 00:54:21,023
happy, got it? All right, let's do one more gesture.
让我们再做些手势
993
00:54:21,025 --> 00:54:24,593
This one, when you tap, he's gonna open and close his eyes.
当你点击时,它会睁开或者闭上它的眼睛
994
00:54:24,595 --> 00:54:27,162
Every tap will toggle the opening and closing the eyes.
每一次轻击就会在睁眼或闭眼中切换
995
00:54:27,164 --> 00:54:30,165
Now I'm gonna do this one a little bit different, because
现在,我想用不一样的方法写这个手势
996
00:54:30,167 --> 00:54:32,868
we added all these gesture recognizers in code here.
因为我们在这里添加了所有的手势识别器代码
997
00:54:32,870 --> 00:54:35,271
You see how this adding GestureRecognizer in code?
你想看看我怎么添加新手势吗?
998
00:54:35,273 --> 00:54:41,977
You can actually add these things in your storyboard,
你完全可以在 storyboard 中添加手势
999
00:54:41,979 --> 00:54:46,115
okay? So let's go over to our storyboard, right here,
现在来到 storyboard
1000
00:54:46,117 --> 00:54:51,687
okay? Let's get our controller onscreen at the same time,
同时,让 controller 同时出现在屏幕上
1001
00:54:51,689 --> 00:54:53,989
Automatic. So here's our controller over here.
1002
00:54:53,991 --> 00:54:58,761
Here's our faceView over here. If I wanna add a gesture to my
1003
00:54:58,763 --> 00:55:02,364
faceView right here from my storyboard, I go over here to
1004
00:55:02,366 --> 00:55:05,200
the bottom, same place I get buttons and things like that.
1005
00:55:05,202 --> 00:55:09,605
If you scroll way down, you'll see there are gesture
1006
00:55:09,607 --> 00:55:12,675
recognizers here, you see? Rotation, pinch, swipe, pan.
1007
00:55:12,677 --> 00:55:16,645
Here's tap. I'm gonna take tap out and drag it onto the view
1008
00:55:16,647 --> 00:55:19,948
I want to recognize the tap, which is my faceView. And
1009
00:55:19,950 --> 00:55:22,951
when you do that, it actually appears along the top here.
1010
00:55:22,953 --> 00:55:25,054
See that Tap Gesture Recognizer right there? And
1011
00:55:25,056 --> 00:55:28,424
you can even inspect it. You could set how many taps,
1012
00:55:28,426 --> 00:55:31,727
how many touches, okay, how many fingers. Okay,
1013
00:55:31,729 --> 00:55:36,598
you can also Ctrl+drag from it over here, let's do it down
1014
00:55:36,600 --> 00:55:42,137
a little lower, here. Okay, you can Ctrl+drag and
1015
00:55:42,139 --> 00:55:47,776
create the gesture recognizer action, okay, right here.
1016
00:55:47,778 --> 00:55:52,381
So I'm gonna call this toggleEyes,
1017
00:55:52,383 --> 00:55:53,449
okay? And the argument,
1018
00:55:53,451 --> 00:55:55,951
I'm gonna make it to a tap gesture instead of any object.
1019
00:55:55,953 --> 00:55:59,521
We all know to get rid of the any object in these little,
1020
00:55:59,523 --> 00:56:03,359
dialogues. Okay, so here's my tap gesture right here.
1021
00:56:03,361 --> 00:56:07,363
Let's go ahead and go back to full screen,
1022
00:56:07,365 --> 00:56:10,699
okay? So, here's my tap gesture for toggleEyes. So,
1023
00:56:10,701 --> 00:56:14,603
in here I need to toggle my eye, so I'm just going to say,
1024
00:56:14,605 --> 00:56:21,643
if the recognizer's state is ended. Okay? So the tap ended,
1025
00:56:21,645 --> 00:56:24,446
so I'm gonna take action here and do the toggle.
1026
00:56:24,448 --> 00:56:28,250
Then, depending on what the expression's current eyes are,
1027
00:56:28,252 --> 00:56:30,519
if it, they're open, then I'm gonna be the,
1028
00:56:30,521 --> 00:56:34,990
make the expression's eyes be closed. And
1029
00:56:34,992 --> 00:56:37,860
if it's, if the eyes are closed, then I'm gonna make
1030
00:56:37,862 --> 00:56:42,598
the expression's eyes open. And if the express,
1031
00:56:42,600 --> 00:56:46,168
if the expression is squinting then I'm going to do nothing
1032
00:56:46,170 --> 00:56:51,106
cuz I don't know how to handle squinting. Okay make
1033
00:56:51,108 --> 00:56:57,079
sense? Sorry notice I used recognizer.state here.
1034
00:56:57,081 --> 00:57:00,015
By default when you Ctrl+drag it calls a thing sender.
1035
00:57:00,017 --> 00:57:02,684
I really like to call this recognizer. So
1036
00:57:02,686 --> 00:57:05,654
that you remember that it's a recognizer that we're dealing
1037
00:57:05,656 --> 00:57:09,425
with here, okay, gesture recognizer. Okay so
1038
00:57:09,427 --> 00:57:15,230
let's try that. All right,
1039
00:57:15,232 --> 00:57:19,001
your face. We can make him happier and we can open and
1040
00:57:19,003 --> 00:57:22,704
close his eyes. Okay? Maybe later in the quarter,
1041
00:57:22,706 --> 00:57:23,071
we'll talk about animation,
1042
00:57:23,073 --> 00:57:27,676
we'll talk about animating the eye it will turn so he blinks.
1043
00:57:27,678 --> 00:57:31,880
Okay? And I'm gonna post code after lecture because
1044
00:57:31,882 --> 00:57:34,283
the time here I wanna make sure we get to the next thing.
1045
00:57:34,285 --> 00:57:37,753
Where I'm gonna show how to do rotation to move those
1046
00:57:37,755 --> 00:57:38,420
eyebrows. Okay?
1047
00:57:38,422 --> 00:57:42,558
So, rotate the eyebrows down or rotate them up. Okay?
1048
00:57:42,560 --> 00:57:44,026
I'll show you how to do a rotation disk, so
1049
00:57:44,028 --> 00:57:46,762
that'll be in the code that I post. Okay,
1050
00:57:46,764 --> 00:57:50,866
any questions about gestures? Pretty straightforward, okay?
1051
00:57:50,868 --> 00:57:54,136
All right, let's get back to our
1052
00:57:54,138 --> 00:57:58,674
slides. >> Is
1053
00:57:58,676 --> 00:58:04,279
that odd gesture recognizer code automatically
1054
00:58:04,281 --> 00:58:06,782
added to the UIView? >> Okay so
1055
00:58:06,784 --> 00:58:11,453
the question was is that eye gesture recognize your code
1056
00:58:11,455 --> 00:58:12,721
that I put in there do the tap.
1057
00:58:12,723 --> 00:58:15,657
Is it already automatically added to the UIView?
1058
00:58:15,659 --> 00:58:19,695
And the answer, you mean the add gesture recognizer?
1059
00:58:19,697 --> 00:58:20,829
Cog it made? >> Yeah.
1060
00:58:20,831 --> 00:58:22,598
>> Yeah, that, basically,
1061
00:58:22,600 --> 00:58:23,532
when you can control,
1062
00:58:23,534 --> 00:58:27,002
when I dragged the tap gesture onto the face view,
1063
00:58:27,004 --> 00:58:27,135
the add gesture recognizer was gonna happen. Make sense?
1064
00:58:27,137 --> 00:58:30,372
that made it so
1065
00:58:30,374 --> 00:58:35,410
Good question, though. Okay, so, back to the slides here.
1066
00:58:35,412 --> 00:58:39,214
Now we're gonna start talking about MVCs working together.
1067
00:58:39,216 --> 00:58:43,352
Okay? This is a slide from when I was talking about MVCs
1068
00:58:43,354 --> 00:58:45,621
and we talked about we wanted to build big apps, and
1069
00:58:45,623 --> 00:58:47,089
we're gonna do that with multiple MVCs.
1070
00:58:47,091 --> 00:58:50,959
So, we're gonna start talking about how we actually do that.
1071
00:58:50,961 --> 00:58:55,063
All right? The way we combine MVCs as
1072
00:58:55,065 --> 00:58:59,268
I talked about before is that we're gonna have an MVC, okay?
1073
00:58:59,270 --> 00:59:04,506
Whose view is made up of other MVCs. Okay?
1074
00:59:04,508 --> 00:59:09,378
Now iOS provides for us some MVCs that are like this.
1075
00:59:09,380 --> 00:59:13,248
Okay, MVCs whose view is other MVCs. Now, it is possible for
1076
00:59:13,250 --> 00:59:17,286
you to create your own MVCs that have other MVCs as view,
1077
00:59:17,288 --> 00:59:19,721
but it is really beyond the scope of this class.
1078
00:59:19,723 --> 00:59:22,424
Okay? So in this class we are only gonna use the ones that
1079
00:59:22,426 --> 00:59:27,596
are provided by iOS. These MVCs that use other MVCs as
1080
00:59:27,598 --> 00:59:30,799
their view. Now specifically, we are gonna talk about
1081
00:59:30,801 --> 00:59:33,936
TabBarController. Everyone know what that is all right?
1082
00:59:33,938 --> 00:59:34,369
You've seen that in an iOS
1083
00:59:34,371 --> 00:59:37,306
AVS the little tabs on the bottom and each time you click
1084
00:59:37,308 --> 00:59:39,474
on a different tab, what appears on top?
1085
00:59:39,476 --> 00:59:45,280
A different MVC, okay. Also SplitViewController mostly you
1086
00:59:45,282 --> 00:59:48,717
see this on an iPad you can all see on an iPhone 6 plus.
1087
00:59:48,719 --> 00:59:53,355
This is where you're splitting your screen into two halves.
1088
00:59:53,357 --> 00:59:53,522
Okay, not really halves.
1089
00:59:53,524 --> 00:59:56,925
Two parts, one side's usually smaller that the other, okay?
1090
00:59:56,927 --> 01:00:00,295
So, each side a different MVC. So, you see how
1091
01:00:00,297 --> 01:00:03,231
we combine two MVCs right there. And then, of course,
1092
01:00:03,233 --> 01:00:08,003
UINavigationController, the number one combiner of MVCs.
1093
01:00:08,005 --> 01:00:09,938
Okay, this is something where you have
1094
01:00:09,940 --> 01:00:12,808
an MVC, it's got a button in it, you click it, and a new
1095
01:00:12,810 --> 01:00:15,243
MVC appears, where there's a back button at the top,
1096
01:00:15,245 --> 01:00:18,981
and you've click it to go back to the one you were on. Okay?
1097
01:00:18,983 --> 01:00:21,950
See navigation controllers in almost every iOS app that's
1098
01:00:21,952 --> 01:00:24,553
ever been designed ever except for
1099
01:00:24,555 --> 01:00:27,155
maybe games that are all one MVC.
1100
01:00:27,157 --> 01:00:29,558
You're gonna see navigation controllers. Okay so
1101
01:00:29,560 --> 01:00:32,160
let's talk a little bit about how all these things work.
1102
01:00:32,162 --> 01:00:33,895
So let's talk about the TabBarControllers and
1103
01:00:33,897 --> 01:00:35,831
the simplest of them all, right?
1104
01:00:35,833 --> 01:00:37,232
You've seen this right it's got the tabs along
1105
01:00:37,234 --> 01:00:39,468
the bottom here. I've got this one the dashboard this is
1106
01:00:39,470 --> 01:00:42,704
the health app I think. Got the dashboard selection here,
1107
01:00:42,706 --> 01:00:47,142
and this entire space is being owned and managed
1108
01:00:47,144 --> 01:00:51,613
by a Dashboard MVC. Not the TabBarController MVC, but
1109
01:00:51,615 --> 01:00:55,417
the dash part MVC. And in fact this dashboard is controlling
1110
01:00:55,419 --> 01:00:58,453
not only the contents of here. But it tells you,
1111
01:00:58,455 --> 01:01:00,889
our TabBarcontroller what icon to put here and
1112
01:01:00,891 --> 01:01:04,926
what word to put here. Okay? These tabs over here, these
1113
01:01:04,928 --> 01:01:09,564
other tabs are, their contents are controlled by other MVCs.
1114
01:01:09,566 --> 01:01:14,269
Okay? So, this var, tab bar item that you see right there.
1115
01:01:14,271 --> 01:01:18,306
It's kind of a little bag of goodies that each MVC,
1116
01:01:18,308 --> 01:01:20,509
remember an MVC is controlled by its controller.
1117
01:01:20,511 --> 01:01:22,778
So this is a method in UIViewController.
1118
01:01:22,780 --> 01:01:25,781
And it has things in there like the icon and the text for
1119
01:01:25,783 --> 01:01:28,617
the tab, okay? In TabBarItem. So you can take a look at
1120
01:01:28,619 --> 01:01:31,420
the TabBarItem and you'll immediately see. What's going
1121
01:01:31,422 --> 01:01:34,156
on there. All right. So, let's click on a different one,
1122
01:01:34,158 --> 01:01:38,160
like Health Data, and we get a completely different MVC here.
1123
01:01:38,162 --> 01:01:39,127
This is the Health Data MVC.
1124
01:01:39,129 --> 01:01:41,396
Completely unrelated to this one. Okay?
1125
01:01:41,398 --> 01:01:44,332
Utterly and completely unrelated to this one.
1126
01:01:44,334 --> 01:01:48,403
Okay? Here, one thing to notice about this is,
1127
01:01:48,405 --> 01:01:51,406
if there are more than five tabs down here.
1128
01:01:51,408 --> 01:01:55,177
This actually still works. It'll automatically replace
1129
01:01:55,179 --> 01:01:57,512
the 5th one with a tab here that says More.
1130
01:01:57,514 --> 01:02:01,283
And when you click on More it'll have a UITabBar
1131
01:02:01,285 --> 01:02:04,753
controlled UI in here that has all the other tabs in here.
1132
01:02:04,755 --> 01:02:07,656
And you can pick them. Or you can actually choose to put
1133
01:02:07,658 --> 01:02:11,393
them down here, okay, which four you want down here. Okay,
1134
01:02:11,395 --> 01:02:12,494
and that happens automatically for you.
1135
01:02:12,496 --> 01:02:14,329
You don't have to do anything to make that work.
1136
01:02:14,331 --> 01:02:18,033
I don't really recommend UIs that have more than five tabs
1137
01:02:18,035 --> 01:02:20,802
down here. It's a little bit of a pain for people to have
1138
01:02:20,804 --> 01:02:24,806
to have that more thing, but it's not out of the question.
1139
01:02:24,808 --> 01:02:26,274
Obviously the functionality's in there.
1140
01:02:26,276 --> 01:02:29,277
If you can do it without it, that'd be nice. Okay here's
1141
01:02:29,279 --> 01:02:32,681
a couple more tabs here is the source's one another MVC
1142
01:02:32,683 --> 01:02:35,617
another so these are four completely separate MVCs.
1143
01:02:35,619 --> 01:02:37,119
They have nothing to do with each other okay.
1144
01:02:37,121 --> 01:02:40,722
The only thing that might link them maybe they share a model.
1145
01:02:40,724 --> 01:02:43,725
That's possible okay but maybe even not.
1146
01:02:44,194 --> 01:02:46,528
Okay so a tab bar is what's you gonna use when you
1147
01:02:46,530 --> 01:02:51,967
have MVCs they're completely unrelated. Okay? All right.
1148
01:02:51,969 --> 01:02:52,634
What about SplitViewController?
1149
01:02:52,636 --> 01:02:56,171
So, SplitViewController puts two MVCs side by side. So,
1150
01:02:56,173 --> 01:02:59,441
let's imagine your calculator was a graphing calculator.
1151
01:02:59,443 --> 01:03:00,242
Okay, instead of just a calculator,
1152
01:03:00,244 --> 01:03:03,278
it could graph, what's going on over here.
1153
01:03:03,280 --> 01:03:05,447
Then on the left here, you'd have your calculator, and
1154
01:03:05,449 --> 01:03:08,550
on the right, you'd have a totally different MVC.
1155
01:03:08,552 --> 01:03:10,485
Which is a graph view calculator,
1156
01:03:10,487 --> 01:03:13,588
calculator graph view, or something like that. Okay,
1157
01:03:13,590 --> 01:03:18,693
we call this one on the left the Master View Controller and
1158
01:03:18,695 --> 01:03:22,764
we call the one on the right the Detail View Controller.
1159
01:03:22,766 --> 01:03:25,767
Okay, when you take your iPad here and
1160
01:03:25,769 --> 01:03:29,905
you rotate it, you still get both. Its just that
1161
01:03:29,907 --> 01:03:32,974
the detail will usually take over the whole screen. And
1162
01:03:32,976 --> 01:03:37,145
you can slide from the right, you kind of swipe in from, or
1163
01:03:37,147 --> 01:03:41,082
sorry from the left. And you'll see that the master
1164
01:03:41,084 --> 01:03:46,154
slides in on top of the detail. Okay. So
1165
01:03:46,156 --> 01:03:47,989
that's what view, how it, how it arrange.
1166
01:03:47,991 --> 01:03:52,227
NavigationController, so NavigationController it's like
1167
01:03:52,229 --> 01:03:57,132
a stack of cards. Okay? Where each card is a different MVC.
1168
01:03:57,134 --> 01:03:59,100
So here I have an all settings MVC,
1169
01:03:59,102 --> 01:04:02,003
okay, inside of a navigation controller Now,
1170
01:04:02,005 --> 01:04:05,006
this navigation controller is responsible for
1171
01:04:05,008 --> 01:04:07,342
drawing this title bar at the top, okay?
1172
01:04:07,344 --> 01:04:09,978
This whole area in the top where it says Settings. But
1173
01:04:09,980 --> 01:04:12,480
it's not responsible for the actual contents
1174
01:04:12,482 --> 01:04:15,550
of what's in there. That word Settings is determined,
1175
01:04:15,552 --> 01:04:19,754
just like in tab bar situation, with by this MVC,
1176
01:04:19,756 --> 01:04:25,660
okay? This MVC down here has a, property on it. It's view,
1177
01:04:25,662 --> 01:04:27,929
it's view controller called Navigation Item. And
1178
01:04:27,931 --> 01:04:31,833
navigation item has properties in it like the title, the,
1179
01:04:31,835 --> 01:04:36,171
any buttons that go here, maybe a back button title,
1180
01:04:36,173 --> 01:04:37,472
those kind of things.
1181
01:04:37,474 --> 01:04:40,809
All would be in this navigation item of this all
1182
01:04:40,811 --> 01:04:44,512
Settings MVC, not of the navigation controller,
1183
01:04:44,514 --> 01:04:49,084
right? So, here if I touched on General and
1184
01:04:49,086 --> 01:04:52,554
I wanted the general settings, then of course, the general
1185
01:04:52,556 --> 01:04:57,392
settings would slide in, okay? And replace the whole contents
1186
01:04:57,394 --> 01:05:01,596
here with a general settings MVP, yet another new MVC. And
1187
01:05:01,598 --> 01:05:04,633
notice the back button by default will be set to have
1188
01:05:04,635 --> 01:05:07,903
the name of the previous MVC. That other MVC is still in
1189
01:05:07,905 --> 01:05:11,473
the navigation controller, it's just underneath, okay?
1190
01:05:11,475 --> 01:05:14,542
So this is stack of cards, I just put another MVC on top,
1191
01:05:14,544 --> 01:05:17,612
so the other one's still there, okay? And
1192
01:05:17,614 --> 01:05:21,349
you can also put toolbar items at the bottom here, okay?
1193
01:05:21,351 --> 01:05:24,185
That's also controlled by whatever MVC is showing.
1194
01:05:24,187 --> 01:05:27,022
So this tool by are, barItems is a property on
1195
01:05:27,024 --> 01:05:30,926
UIViewController, it's an array of UI toolbarItem, and
1196
01:05:30,928 --> 01:05:34,796
it'll let you put toolbarItems down here, okay? Little,
1197
01:05:34,798 --> 01:05:38,934
small buttons, okay? So again, if I click, let's say sorry.
1198
01:05:38,936 --> 01:05:42,404
Yeah, the back button got the title there, but you can also
1199
01:05:42,406 --> 01:05:45,040
set the title to something different if you want,
1200
01:05:45,042 --> 01:05:48,843
okay? So let's say I clicked on Accessibility right here,
1201
01:05:48,845 --> 01:05:52,847
I get another new MVC. Now I have three MVCs on my stack,
1202
01:05:52,849 --> 01:05:56,051
okay? Let's go ahead and click on another one here,
1203
01:05:56,053 --> 01:05:59,387
a larger text right there. Now I got another one, so
1204
01:05:59,389 --> 01:06:03,725
now I have four MVCs on my stack, okay? Now,
1205
01:06:03,727 --> 01:06:07,028
I'm going to go and click on Accessibility,
1206
01:06:07,030 --> 01:06:11,766
up in the top there, the back button. Now what happens? Now,
1207
01:06:11,768 --> 01:06:17,072
this MVC gets thrown away, in the trash, out of the heap,
1208
01:06:17,074 --> 01:06:21,776
gone forever, okay? Now there's only three MVCs left,
1209
01:06:21,778 --> 01:06:23,511
okay? The other one's completely gone.
1210
01:06:23,513 --> 01:06:25,847
It's important to understand on a MVC,
1211
01:06:25,849 --> 01:06:29,851
when you go back, the one that got thrown off literally gets
1212
01:06:29,853 --> 01:06:33,254
thrown away, okay? And so, if we go back again, now that one
1213
01:06:33,256 --> 01:06:36,591
got thrown away. Now there's only two MVCs in the world,
1214
01:06:36,593 --> 01:06:39,327
okay, on top of each other. And of course if we go back to
1215
01:06:39,329 --> 01:06:43,231
the beginning, now we get back to here, this MVC we'll call
1216
01:06:43,233 --> 01:06:45,567
the root view controller of the navigation controller.
1217
01:06:45,569 --> 01:06:48,770
It's just one that's at the bottom of the deck. Okay,
1218
01:06:48,772 --> 01:06:53,008
it's always in there. Okay, so lets talk about how that
1219
01:06:53,010 --> 01:06:56,444
navigation controller works from our MVC standpoint, okay?
1220
01:06:56,446 --> 01:07:00,181
Let's say I have an MVC here. And I just have too much
1221
01:07:00,183 --> 01:07:03,551
functionality. I just can't fit all the functionality in
1222
01:07:03,553 --> 01:07:05,687
my view, I need to put this somewhere else.
1223
01:07:05,689 --> 01:07:09,257
So I create another MVC, okay? Cuz somebody has to control
1224
01:07:09,259 --> 01:07:12,394
this extra functionality. So there's another controller,
1225
01:07:12,396 --> 01:07:16,531
okay? A whole other MVC here, okay? And I want these two,
1226
01:07:16,533 --> 01:07:19,901
they're related, okay? So somehow in this view, I,
1227
01:07:19,903 --> 01:07:24,005
I get to this UI because this UI kinda all goes together in
1228
01:07:24,007 --> 01:07:26,808
some way. It's not related in some mechanism, maybe it's
1229
01:07:26,810 --> 01:07:30,578
master detail kind of effect or something like that.
1230
01:07:30,580 --> 01:07:32,614
So how am I gonna make this work, okay? So
1231
01:07:32,616 --> 01:07:34,949
I'm using UINavigationController. And
1232
01:07:34,951 --> 01:07:36,117
here's a UINavigationController.
1233
01:07:36,119 --> 01:07:40,588
It's view, looks like this, okay? It's blank, okay?
1234
01:07:40,590 --> 01:07:42,223
It's got room up here though for a title,
1235
01:07:42,225 --> 01:07:45,093
back button, maybe some other buttons over here. But
1236
01:07:45,095 --> 01:07:48,096
it never lives like this because there is an outlet
1237
01:07:48,098 --> 01:07:51,399
called rootViewController, okay? Which is hooked up to
1238
01:07:51,401 --> 01:07:56,404
some MVC. And when it's hooked up, the contents of the view
1239
01:07:56,406 --> 01:07:59,841
of the navigation controller are the contents of this MVCs
1240
01:07:59,843 --> 01:08:03,945
view. Right, make sense, kinda obvious? Now
1241
01:08:03,947 --> 01:08:08,016
there's probably some button in here that if I touch it,
1242
01:08:08,018 --> 01:08:12,120
means I wanna show this UI over here, I want this MVC. So
1243
01:08:12,122 --> 01:08:14,789
if I touch it, what the navigation controller does,
1244
01:08:14,791 --> 01:08:16,825
it moves over here, shows this view.
1245
01:08:16,827 --> 01:08:19,561
This one's still around, okay? It's just not showing but
1246
01:08:19,563 --> 01:08:23,098
it's still in the UINavigationController stack,
1247
01:08:23,100 --> 01:08:25,333
okay? It shows this guy right here.
1248
01:08:25,335 --> 01:08:28,069
Notice we get this automatic back button right there.
1249
01:08:28,071 --> 01:08:31,806
If we press that back button, okay? Then it's going to
1250
01:08:31,808 --> 01:08:34,742
go back here, and look what happened to this MVC.
1251
01:08:34,744 --> 01:08:39,214
Gone, okay, completely got eliminated. If I press this
1252
01:08:39,216 --> 01:08:42,851
button again to go back, it would create a new one,
1253
01:08:42,853 --> 01:08:49,090
okay? A brand new instance of this MVC, okay? [INAUDIBLE] So
1254
01:08:49,092 --> 01:08:52,494
that's how the Navigation Controller is working from
1255
01:08:52,496 --> 01:08:56,030
an MVC standpoint. All right, [COUGH] now in these MVCs,
1256
01:08:56,032 --> 01:08:59,000
how do I get at the other MVCs that are, I'm sharing
1257
01:08:59,002 --> 01:09:02,604
the navigation controller or the split view controller or
1258
01:09:02,606 --> 01:09:05,306
the tab bar controller, how do I get to them,
1259
01:09:05,308 --> 01:09:07,909
send them messages, talk to them in some way?
1260
01:09:07,911 --> 01:09:10,778
And the answer is through this very important var in UI
1261
01:09:10,780 --> 01:09:13,915
viewController, okay? Sorry, in UI tab bar controller,
1262
01:09:13,917 --> 01:09:17,185
UI split view controller, and UI navigation controller,
1263
01:09:17,187 --> 01:09:17,952
they all have this var.
1264
01:09:17,954 --> 01:09:20,088
And it's an array of view controllers,
1265
01:09:20,090 --> 01:09:22,790
it's all the view controllers in this thing. All
1266
01:09:22,792 --> 01:09:25,927
the tabs in the tab bar, both this master and the detail and
1267
01:09:25,929 --> 01:09:30,498
the split view, all the things on the navigation stack. And
1268
01:09:30,500 --> 01:09:33,168
navigation controller. It can be an optional,
1269
01:09:33,170 --> 01:09:35,203
like the tab bar case, it can be an optional.
1270
01:09:35,205 --> 01:09:37,805
I don't think it's an optional for the other two.
1271
01:09:37,807 --> 01:09:39,774
You're have to look at documentation there for
1272
01:09:39,776 --> 01:09:43,745
that. But in a tab bar this is just the array.
1273
01:09:43,747 --> 01:09:45,713
I believe it's from left to right, okay?
1274
01:09:45,715 --> 01:09:48,316
So it's all the MVCs from the left tab to the right, and if
1275
01:09:48,318 --> 01:09:51,553
you have more then it spills into the order in the more
1276
01:09:51,555 --> 01:09:55,957
tab. For split view, [0] in this array is the master and
1277
01:09:55,959 --> 01:09:59,360
[1] is the detail. And for navigation controller,
1278
01:09:59,362 --> 01:10:02,230
[0] is the bottom card and 1, 2, 3, 4, 5, 6,
1279
01:10:02,232 --> 01:10:05,633
7 is going up the stack, okay? So the last one that's
1280
01:10:05,635 --> 01:10:10,605
visible would be the last one in this array, okay? Got it?
1281
01:10:10,607 --> 01:10:13,508
Notice this is set table, it's get and set but we usually
1282
01:10:13,510 --> 01:10:16,177
don't set our viewControllers this way, okay?
1283
01:10:16,179 --> 01:10:19,914
Like split view, we usually wire it up in our storyboard.
1284
01:10:19,916 --> 01:10:22,817
In navigation controller we're usually navigating to those
1285
01:10:22,819 --> 01:10:27,555
other MVCs and tab bar we also set up in our storyboard,
1286
01:10:27,557 --> 01:10:31,893
okay? So, it is set table but usually we're getting them.
1287
01:10:31,895 --> 01:10:34,529
But, so that's great but if I'm inside
1288
01:10:34,531 --> 01:10:37,599
a splitViewController, how do I get the splitViewController
1289
01:10:37,601 --> 01:10:40,034
I'm in so that I can access this property? And
1290
01:10:40,036 --> 01:10:43,271
the answer is UIViewController has these three very important
1291
01:10:43,273 --> 01:10:45,540
properties, tabBarController, splitViewController and
1292
01:10:45,542 --> 01:10:48,843
navigationController, they're all optionals. They will tell
1293
01:10:48,845 --> 01:10:52,780
you the splitViewController you are in, if you're in one.
1294
01:10:52,782 --> 01:10:55,650
If you're not, it'll be nil, okay? It will also tell
1295
01:10:55,652 --> 01:10:58,886
you this one, what navigation controller you're in if you're
1296
01:10:58,888 --> 01:11:01,823
in one? And you could be in both a splitViewController and
1297
01:11:01,825 --> 01:11:04,559
a navigationController. Imagine a splitVieController
1298
01:11:04,561 --> 01:11:07,161
where the master is actually the navigationController
1299
01:11:07,163 --> 01:11:09,764
that you can navigate through, okay? So you can have
1300
01:11:09,766 --> 01:11:13,101
a navigationController inside a splitVewController and
1301
01:11:13,103 --> 01:11:15,536
any MVC that's in that navigationController,
1302
01:11:15,538 --> 01:11:18,806
this would be nil, non nil and this would also be non nill,
1303
01:11:18,808 --> 01:11:22,644
okay? Cuz it would be in, in both of those things. And
1304
01:11:22,646 --> 01:11:24,879
once you get the splitViewController or
1305
01:11:24,881 --> 01:11:25,913
navigationController in, you can
1306
01:11:25,915 --> 01:11:28,149
find the other MVCs using this thing up here.
1307
01:11:28,151 --> 01:11:31,019
So for example, if I have an MVC and it's the master in
1308
01:11:31,021 --> 01:11:33,988
a splitViewController, and I want to get the detail. I can
1309
01:11:33,990 --> 01:11:37,659
just say, splitViewController? Because it might be nil and
1310
01:11:37,661 --> 01:11:40,828
this a chaining. Remember, everyone hopefully remembers
1311
01:11:40,830 --> 01:11:44,165
chaining? I'm gonna get the view controllers, sub one.
1312
01:11:44,167 --> 01:11:47,368
Remember, number one is the detail. And that's gonna give
1313
01:11:47,370 --> 01:11:50,238
me the detail of the splitViewController I'm in,
1314
01:11:50,240 --> 01:11:53,808
if I'm in one, otherwise it'd be nil, this would be nil.
1315
01:11:53,810 --> 01:11:56,444
So this actually says detailVC: UIViewController,
1316
01:11:56,446 --> 01:11:59,647
but this would be detailVC: UIViewController? Okay?
1317
01:11:59,649 --> 01:12:04,452
This would be optional. That's a bug in the slides there.
1318
01:12:04,454 --> 01:12:08,189
Okay, so, how do we wire up these MVCs, how do we get this
1319
01:12:08,191 --> 01:12:14,095
all set up? Let's take a look at Split View and briefly,
1320
01:12:14,097 --> 01:12:17,265
Navigation Controller. And then on, in our next
1321
01:12:17,267 --> 01:12:20,702
lecture we'll talk about how we transition between these
1322
01:12:20,704 --> 01:12:23,738
various MVCs. Okay. especially in Navigation Controller,
1323
01:12:23,740 --> 01:12:26,274
how do we put things, more things on the stack? How do we
1324
01:12:26,276 --> 01:12:29,744
make it go to the next, thing? So, for split view controller,
1325
01:12:29,746 --> 01:12:32,647
you basically drag a split view controller out of your
1326
01:12:32,649 --> 01:12:36,184
little utilities, object palette in interface mode.
1327
01:12:36,186 --> 01:12:38,786
Just drag it out. Now, when you drag out a split view
1328
01:12:38,788 --> 01:12:41,422
controller, you're gonna get a whole bunch of other MVCs.
1329
01:12:41,424 --> 01:12:43,658
It tries to help you out by giving you Navigation
1330
01:12:43,660 --> 01:12:45,860
controllers with table views in it,
1331
01:12:45,862 --> 01:12:47,829
all that stuff, just delete all that stuff, okay?
1332
01:12:47,831 --> 01:12:50,465
You just want the split view controller. You, it's being
1333
01:12:50,467 --> 01:12:52,900
a little too helpful for the, you most of the time there,
1334
01:12:52,902 --> 01:12:55,703
kkay? So you're gonna drag that out, and then you're just
1335
01:12:55,705 --> 01:12:58,272
gonna Ctrl + drag from the split control, view controller
1336
01:12:58,274 --> 01:13:01,576
to the master, and the detail, okay? Looks like this.
1337
01:13:01,578 --> 01:13:02,477
Here's my split view control and
1338
01:13:02,479 --> 01:13:04,412
I dragged it out of the utilities pane.
1339
01:13:04,414 --> 01:13:07,715
I'm just Ctrl + dragging to my calculator, for example,
1340
01:13:07,717 --> 01:13:11,119
to be my master. When I do, this popover will appear and
1341
01:13:11,121 --> 01:13:14,222
the only two things that make sense to pick are master and
1342
01:13:14,224 --> 01:13:17,225
detail. Okay, these are the only two choices that make any
1343
01:13:17,227 --> 01:13:18,593
sense. Those don't make sense here.
1344
01:13:18,595 --> 01:13:22,330
So let's pick the master. It's gonna create a connection,
1345
01:13:22,332 --> 01:13:25,767
the master connection. Ctrl+drag down here. I'm gonna
1346
01:13:25,769 --> 01:13:29,704
pick Detail. It's gonna make a connection down there. Voila,
1347
01:13:29,706 --> 01:13:33,174
I've wired up my split view controller, okay?
1348
01:13:33,176 --> 01:13:38,246
All right, but split view can only do what it does on iPad,
1349
01:13:38,248 --> 01:13:42,383
right? There's really, on most iPhones, everything except for
1350
01:13:42,385 --> 01:13:42,550
the iPhone 6 Plus,
1351
01:13:42,552 --> 01:13:45,853
there's really not enough room to have a split view. So
1352
01:13:45,855 --> 01:13:49,557
we want to simultaneously put the MVCs that are our master
1353
01:13:49,559 --> 01:13:52,326
in details with split view into Navigation Controllers.
1354
01:13:52,328 --> 01:13:55,530
And then iOS is smart enough, if it's on iPhone only, okay,
1355
01:13:55,532 --> 01:13:58,266
iPhone 6 or iPhone 5 or something like that, it knows
1356
01:13:58,268 --> 01:14:00,868
not to use the split view controller, it will only use
1357
01:14:00,870 --> 01:14:04,205
the Navigation Controllers. Okay, so let' take a look and
1358
01:14:04,207 --> 01:14:05,306
see what that looks like. So,
1359
01:14:05,308 --> 01:14:07,909
here I've got that thing I just showed you, right?
1360
01:14:07,911 --> 01:14:08,810
The calculator, the graph,
1361
01:14:08,812 --> 01:14:10,411
here's the split view controller.
1362
01:14:10,413 --> 01:14:12,180
I'm just gonna take this guy and
1363
01:14:12,182 --> 01:14:14,115
put it inside a Navigation Controller. And
1364
01:14:14,117 --> 01:14:17,051
the way we do that, easiest way is to select it and
1365
01:14:17,053 --> 01:14:20,054
then say embed in Navigation Controller. Same way we did
1366
01:14:20,056 --> 01:14:22,824
embed in stack view, but here since we're choosing
1367
01:14:22,826 --> 01:14:25,860
a whole controller, we can't embed it in a stack view.
1368
01:14:25,862 --> 01:14:27,895
So we're gonna say embed in Navigation Controller. And
1369
01:14:27,897 --> 01:14:31,265
it's gonna embed this thing into a Navigation Controller,
1370
01:14:31,267 --> 01:14:33,301
which is gonna look like this. So split the controller,
1371
01:14:33,303 --> 01:14:36,571
this is still the master. Now this Navigation Controller,
1372
01:14:36,573 --> 01:14:40,274
this MVC, is the master and its root view controller
1373
01:14:40,276 --> 01:14:43,678
is the calculator. Okay, and the cool thing about this,
1374
01:14:43,680 --> 01:14:46,214
like I say, if you run this in an iPhone it'll ignore this
1375
01:14:46,216 --> 01:14:49,217
and just have your calculator in a Navigation Controller and
1376
01:14:49,219 --> 01:14:53,254
when you try to show a graph it'll put this as a stack,
1377
01:14:53,256 --> 01:14:55,490
a card stack, on this Navigation Controller.
1378
01:14:55,492 --> 01:15:00,161
Isn't that cool? Okay, so that way it'll work on both
1379
01:15:00,163 --> 01:15:02,930
platforms. Exactly the same storyboard.
1380
01:15:02,932 --> 01:15:08,469
All right, okay, yes, I'm just noticing, I'm mentioning here,
1381
01:15:08,471 --> 01:15:12,940
you can put this one inside a Navigation Controller too.
1382
01:15:12,942 --> 01:15:13,908
You don't really need to,
1383
01:15:13,910 --> 01:15:16,777
okay? Only the thing that is the root view controller
1384
01:15:16,779 --> 01:15:18,045
really needs to be inside of it, but
1385
01:15:18,047 --> 01:15:20,615
if you put it then you'll get a nice title on the top.
1386
01:15:20,617 --> 01:15:21,249
You see you've got a title there,
1387
01:15:21,251 --> 01:15:24,418
a calculator, you'll get that nice title here, even in
1388
01:15:24,420 --> 01:15:27,221
the split view case. The only thing to watch out for
1389
01:15:27,223 --> 01:15:30,958
is this detail will now be a Navigation Controller,
1390
01:15:30,960 --> 01:15:32,260
not a calculator graph controller.
1391
01:15:32,262 --> 01:15:36,998
You always have to be careful of that case. All right, so
1392
01:15:37,000 --> 01:15:37,265
that's it for today.
1393
01:15:37,267 --> 01:15:39,800
On Wednesday, we'll talk about what are called segues.
1394
01:15:39,802 --> 01:15:42,703
That's how we transfer from one MVC to another,
1395
01:15:42,705 --> 01:15:44,472
either in the split view or the Navigation Controller
1396
01:15:44,474 --> 01:15:48,543
case. We'll do a demo which is going to extend face it, okay,
1397
01:15:48,545 --> 01:15:51,212
it's going to call, be called emotions as you can imagine,
1398
01:15:51,214 --> 01:15:53,848
we're gonna show some emotions on our faces. Then I'm going
1399
01:15:53,850 --> 01:15:55,917
to start talking about the view controller lifecycle,
1400
01:15:55,919 --> 01:15:58,586
that's part of the two methods that I always delete
1401
01:15:58,588 --> 01:15:59,820
when I start a new thing.
1402
01:15:59,822 --> 01:16:02,356
We'll learn what those are, so you don't have to delete them.
1403
01:16:02,358 --> 01:16:06,494
Friday, really good optional section, which is source code
1404
01:16:06,496 --> 01:16:10,431
management. Remember I've been not clicking that button.
1405
01:16:10,433 --> 01:16:11,098
Now you'll know what that is.
1406
01:16:11,100 --> 01:16:13,501
And you can click that button from now on, if you want,
1407
01:16:13,503 --> 01:16:15,036
after Friday. And then next week,
1408
01:16:15,038 --> 01:16:17,338
we'll talk about protocols, delegations, scroll view,
1409
01:16:17,340 --> 01:16:21,509
all kinds of wonderful stuff. Okay, that's it. I'll be here
1410
01:16:21,511 --> 01:16:23,744
if you have any questions. >> For
1411
01:16:23,746 --> 01:16:23,777
more, please visit us at stanford.edu.
================================================
FILE: subtitles/6. Multiple MVCs, Segues, FaceIt, and View Controller Lifecycle.srt
================================================
1
00:00:00,001 --> 00:00:03,469
[MUSIC]
2
00:00:03,471 --> 00:00:08,040
>> Stanford University. >> Okay, well, this is
3
00:00:08,042 --> 00:00:13,212
lecture six of Stanford SC193 P Spring of 2016. And today we
4
00:00:13,214 --> 00:00:18,283
are gonna dive in more into this topic of multiple MVCs.
5
00:00:18,285 --> 00:00:20,652
If you remember, last lecture I talked a little about
6
00:00:20,654 --> 00:00:24,089
multiple MVCs and how we can combine them with tab bars,
7
00:00:24,091 --> 00:00:26,492
split view controllers and navigation controllers. And
8
00:00:26,494 --> 00:00:28,961
we showed a little bit how to wire them up. Well, today, I'm
9
00:00:28,963 --> 00:00:32,464
gonna talk a little about how we transition between them,
10
00:00:32,466 --> 00:00:35,334
right? It's one thing to wire them up in our storyboard, but
11
00:00:35,336 --> 00:00:38,237
as the user navigates our user interface, clicks on buttons
12
00:00:38,239 --> 00:00:42,408
and stuff, we want to navigate between these MVCs, okay?
13
00:00:42,410 --> 00:00:44,676
And then, time permitting at the end, I'm gonna talk
14
00:00:44,678 --> 00:00:46,979
a little bit more about this view controller lifecycle, the
15
00:00:46,981 --> 00:00:50,616
thing that I always delete. Okay? Those two methods.
16
00:00:50,618 --> 00:00:53,085
There's a few more methods in the view controller lifecycle
17
00:00:53,087 --> 00:00:55,854
that I'm gonna talk about, and again, time permitting,
18
00:00:55,856 --> 00:01:00,192
I'll do a quick little demo at the end. All right.
19
00:01:00,194 --> 00:01:03,529
So, segues. Okay, we've wired up these controllers of
20
00:01:03,531 --> 00:01:07,032
controllers like tab bar split fuse navigation controllers,
21
00:01:07,034 --> 00:01:09,268
and we've got all of our nice MVCs, and
22
00:01:09,270 --> 00:01:10,636
now we want to transition between them.
23
00:01:10,638 --> 00:01:12,571
We call those transitions segues, right?
24
00:01:12,573 --> 00:01:15,641
The word segue in English means a transition. And
25
00:01:15,643 --> 00:01:17,876
there's really four major kinds of segues.
26
00:01:17,878 --> 00:01:21,213
I'm gonna talk about some more rare segues towards the end of
27
00:01:21,215 --> 00:01:23,982
the quarter, but the main segues that you're gonna wire
28
00:01:23,984 --> 00:01:27,519
up in a story board, one is called a show segue.
29
00:01:27,521 --> 00:01:30,889
So, remember the navigation controller that's the stack of
30
00:01:30,891 --> 00:01:34,093
cards, right? The settings app I showed you where you're
31
00:01:34,095 --> 00:01:36,862
putting more stacks on the card. Well, transitioning to
32
00:01:36,864 --> 00:01:40,632
put a new card on the stack, okay, is done using a show,
33
00:01:40,634 --> 00:01:44,336
what we call a show sequeway cuz it shows another MVP or
34
00:01:44,338 --> 00:01:48,006
MVC. I'm thinking about Steph Curry, I guess, okay?
35
00:01:48,008 --> 00:01:53,145
MVC. And then, show detail is if you have a split view,
36
00:01:53,147 --> 00:01:55,380
okay, and something happens in the master and
37
00:01:55,382 --> 00:01:59,351
you want to show an MVC, a different MVC in the detail,
38
00:01:59,353 --> 00:02:04,423
okay? Show detail, that's the detail of a split view.
39
00:02:04,425 --> 00:02:07,259
Show detail also works in a navigation controller,
40
00:02:07,261 --> 00:02:09,728
okay? So, if you do show detail, it's this really
41
00:02:09,730 --> 00:02:12,564
the same as show in a navigation controller, unless
42
00:02:12,566 --> 00:02:17,503
that navigation controller is inside of a split view, okay?
43
00:02:17,505 --> 00:02:19,004
Show and show detail would be the same. But
44
00:02:19,006 --> 00:02:21,440
if a navigation controller's inside a split view,
45
00:02:21,442 --> 00:02:24,710
then show means show it in the Navigation Controller, and
46
00:02:24,712 --> 00:02:26,979
Show Detail means show it in the Split View Controller,
47
00:02:26,981 --> 00:02:30,382
right? Show the detail. Then there's Modal and Popover,
48
00:02:30,384 --> 00:02:33,352
I'm not gonna talk about those today. Modal is basically
49
00:02:33,354 --> 00:02:37,289
segue into takeover the entire screen with another MVC, okay?
50
00:02:37,291 --> 00:02:40,392
And a popover is almost exactly the same except for
51
00:02:40,394 --> 00:02:41,326
instead of taking over the entire screen,
52
00:02:41,328 --> 00:02:44,763
it still takes over the entire screen in terms of input, but
53
00:02:44,765 --> 00:02:46,832
it draws it in a little popover window and
54
00:02:46,834 --> 00:02:48,300
everything else is kind of grayed out and
55
00:02:48,302 --> 00:02:51,703
you can click on the grayed out to dismiss the popover.
56
00:02:51,705 --> 00:02:54,940
Okay? So those are the major kinds of segue that you wire
57
00:02:54,942 --> 00:02:55,607
up in your story boards.
58
00:02:55,609 --> 00:02:57,943
So, we're gonna talk about navigation controllers,
59
00:02:57,945 --> 00:02:59,444
split view controllers, tab bar today.
60
00:02:59,446 --> 00:03:01,446
So, for navigation and split view controllers,
61
00:03:01,448 --> 00:03:02,781
we're talking about show and show detail, okay?
62
00:03:02,783 --> 00:03:06,785
So, we're gonna talk about today. All right. It's very
63
00:03:06,787 --> 00:03:09,555
important to understand that segue virtually
64
00:03:09,557 --> 00:03:13,892
always create a new MVC. Okay? If you segue to an MVC,
65
00:03:13,894 --> 00:03:17,596
it's going to create it. You rarely, extremely rare,
66
00:03:17,598 --> 00:03:21,066
there's only one kind of segue where you're gonna segue to
67
00:03:21,068 --> 00:03:24,736
an MVC that already exists. Okay? So, segues create new
68
00:03:24,738 --> 00:03:27,906
MVCs, okay? Super important to understand. That's even true
69
00:03:27,908 --> 00:03:31,410
in a split view. If you do a show detailed segue,
70
00:03:31,412 --> 00:03:34,980
it replaces the MVC that's in the detail side.
71
00:03:34,982 --> 00:03:37,816
That's why we sometimes call a show detail a replace segue,
72
00:03:37,818 --> 00:03:42,254
okay? That's kind of its old name. All right? So, how do we
73
00:03:42,256 --> 00:03:45,357
make these segues happen, okay? Just like we always do,
74
00:03:45,359 --> 00:03:46,959
we control drag, okay? Just like we
75
00:03:46,961 --> 00:03:50,395
control drag to wire up our MVCs to our split view
76
00:03:50,397 --> 00:03:52,931
control, navigation control, we're also gonna control drag
77
00:03:52,933 --> 00:03:55,701
to determine how they segue up to each other, okay? How they
78
00:03:55,703 --> 00:03:59,204
transition. It's also possible to make segues happen in code.
79
00:03:59,206 --> 00:04:01,974
There's a method you call that says do this segue,
80
00:04:01,976 --> 00:04:05,177
perform segue, it's called, okay? So
81
00:04:05,179 --> 00:04:08,480
here is something wired up as of our last lecture, here.
82
00:04:08,482 --> 00:04:09,748
I've got a split view controller,
83
00:04:09,750 --> 00:04:14,653
and the detail is this little graphing thing at the bottom.
84
00:04:14,655 --> 00:04:17,189
And the master is a navigation controller with
85
00:04:17,191 --> 00:04:19,958
your little calculator view controller right there, okay?
86
00:04:19,960 --> 00:04:22,060
That calculator view controller up there,
87
00:04:22,062 --> 00:04:25,163
looking at this side today, that calculator up there might
88
00:04:25,165 --> 00:04:27,966
be exactly the same MVC as the one you're building in your
89
00:04:27,968 --> 00:04:32,437
homework, okay? All right? So, if we want to add a button to
90
00:04:32,439 --> 00:04:35,707
the calculator that caused this graph to show,
91
00:04:35,709 --> 00:04:38,377
maybe graph what's in your calculator. Wouldn't that be
92
00:04:38,379 --> 00:04:42,881
cool? Then we would Ctrl-drag from that little button.
93
00:04:42,883 --> 00:04:45,484
Can hardly see that button, but it's there. Ctrl-drag to
94
00:04:45,486 --> 00:04:48,987
the MVC we wanna segue to. Okay? So, that's how it works.
95
00:04:48,989 --> 00:04:52,524
You would just Ctrl-drag to. Now, when you Ctrl-drag to,
96
00:04:52,526 --> 00:04:55,394
it's gonna put up a little black window here,
97
00:04:55,396 --> 00:04:58,897
which I'll zoom in on, which is going to ask you what kind
98
00:04:58,899 --> 00:05:02,000
of segue do you want. Do you want a show? A show detail?
99
00:05:02,002 --> 00:05:05,203
A modal? Or a popover? Okay? So if you want this to appear
100
00:05:05,205 --> 00:05:08,707
as the detail in a split view, you're gonna pick Show Detail.
101
00:05:08,709 --> 00:05:10,409
Otherwise, it's just in a navigation controller,
102
00:05:10,411 --> 00:05:14,012
you're gonna pick Show, okay? Again, we'll talk about
103
00:05:14,014 --> 00:05:16,348
the other two later in the quarter. All right?
104
00:05:16,350 --> 00:05:19,151
So, you've got that wired up. As soon as you do that,
105
00:05:19,153 --> 00:05:20,852
you've get this little guy appearing here,
106
00:05:20,854 --> 00:05:23,522
this line with a little thing in the middle here.
107
00:05:23,524 --> 00:05:25,991
This is the segue. Okay? And this segue,
108
00:05:25,993 --> 00:05:29,261
just like a button or anything else, is inspectable. So
109
00:05:29,263 --> 00:05:32,264
if you go over here to the inspector and inspect it,
110
00:05:32,266 --> 00:05:34,666
it's going to let you set some things about the segue.
111
00:05:34,668 --> 00:05:38,537
The most important thing is its identifier, okay?
112
00:05:38,539 --> 00:05:41,340
Every segue needs an identifier, because in your
113
00:05:41,342 --> 00:05:43,975
code, you're gonna have to refer to that segue,
114
00:05:43,977 --> 00:05:46,411
and you do it by name. By this identifier.
115
00:05:46,413 --> 00:05:48,947
That's how you're gonna refer to this in your code.
116
00:05:48,949 --> 00:05:53,118
So it's critical that you give your segues names, okay?
117
00:05:53,120 --> 00:05:54,353
This identifier thing. And
118
00:05:54,355 --> 00:05:59,157
we'll see that in the demo. And the name of
119
00:05:59,159 --> 00:06:03,595
the identifier should probably describe what the segue does.
120
00:06:03,597 --> 00:06:07,366
It's usually a verb, like a verb phrase like show graph,
121
00:06:07,368 --> 00:06:09,835
do something. Cuz that's what segues do, they do something.
122
00:06:09,837 --> 00:06:15,240
So it's usually some kind of verb phrase. Okay. So, what,
123
00:06:15,242 --> 00:06:20,879
so, where in our code are we gonna use that identifier for
124
00:06:20,881 --> 00:06:21,046
the segue?
125
00:06:21,048 --> 00:06:23,882
It's really two places. One place that we don't use too
126
00:06:23,884 --> 00:06:27,185
much is when we want to make that segue happen from code.
127
00:06:27,187 --> 00:06:30,655
And we can do that with this method in UI view controller,
128
00:06:30,657 --> 00:06:31,356
okay? You have your controller.
129
00:06:31,358 --> 00:06:34,126
You all know what that is, okay? It's called perform
130
00:06:34,128 --> 00:06:37,095
segue with identifier. You give it the identifier, and
131
00:06:37,097 --> 00:06:40,565
then you can pass any object you want as the sender, okay?
132
00:06:40,567 --> 00:06:43,969
The sender is the object that is causing the segue to
133
00:06:43,971 --> 00:06:47,506
happen. Often that's a button, okay? But when you're doing it
134
00:06:47,508 --> 00:06:50,509
from code, you can send any object really that you want.
135
00:06:50,511 --> 00:06:53,645
Okay? And you'll see why we need that in a moment. Okay?
136
00:06:53,647 --> 00:06:55,514
So that's one reason you need the identifier.
137
00:06:55,516 --> 00:06:58,850
But a much more important reason is because when a segue
138
00:06:58,852 --> 00:07:01,620
happens, the MVC your segueing to,
139
00:07:01,622 --> 00:07:05,390
usually needs to be prepared to come on screen. Okay?
140
00:07:05,392 --> 00:07:06,391
It needs some information.
141
00:07:06,393 --> 00:07:09,628
Maybe it needs it's model to be set. That's a common thing.
142
00:07:09,630 --> 00:07:13,398
Maybe it needs some of it's drawing attributes to be set,
143
00:07:13,400 --> 00:07:18,804
okay? So that is happening in this preparation.
144
00:07:18,806 --> 00:07:19,771
So, this is so important.
145
00:07:19,773 --> 00:07:22,808
I'm gonna talk about the preparation in detail. Now,
146
00:07:22,810 --> 00:07:26,278
the preparation happens with a method that is sent to
147
00:07:26,280 --> 00:07:30,916
the view controller that is causing this segue to happen.
148
00:07:30,918 --> 00:07:31,750
Not the view controller on
149
00:07:31,752 --> 00:07:33,385
the other end that's being created, but
150
00:07:33,387 --> 00:07:35,120
the one that is causing it to happen, okay?
151
00:07:35,122 --> 00:07:38,190
That view controller is gonna be sent this message,
152
00:07:38,192 --> 00:07:41,993
prepare for segue. Okay? And it's job in this method is
153
00:07:41,995 --> 00:07:45,564
to prepare the MVC that's being created by the segue to
154
00:07:45,566 --> 00:07:49,000
do what it wants to do. Now, it's really important to
155
00:07:49,002 --> 00:07:52,637
understand with this multi-MVC world, that it's object
156
00:07:52,639 --> 00:07:55,841
oriented in that each MVC kind of has it's own world.
157
00:07:55,843 --> 00:07:59,444
Okay, they don't really wanna be talking back to each other.
158
00:07:59,446 --> 00:08:02,647
And the MVC on the ender end of a segue is really part
159
00:08:02,649 --> 00:08:06,218
of the view of the MVC that's causing the segue. Remember
160
00:08:06,220 --> 00:08:09,554
those graphics that I told you? So we really can't talk
161
00:08:09,556 --> 00:08:12,357
back to the one causing the segue. We would have to use
162
00:08:12,359 --> 00:08:14,926
some kind of blind structured communication because the view
163
00:08:14,928 --> 00:08:17,829
is not really allowed to talk to its controller, except for
164
00:08:17,831 --> 00:08:19,231
in these structured ways, right?
165
00:08:19,233 --> 00:08:20,398
The controller can talk to the view,
166
00:08:20,400 --> 00:08:23,134
no problem. And he's going to here. This is going to be
167
00:08:23,136 --> 00:08:26,204
the controller talking to its view by preparing
168
00:08:26,206 --> 00:08:26,471
that MVC. But
169
00:08:26,473 --> 00:08:29,641
you really wanna think of MVCs as living in their own world,
170
00:08:29,643 --> 00:08:32,210
self-contained, they have some public API,
171
00:08:32,212 --> 00:08:35,714
you set things in their public API, and they do what they do,
172
00:08:35,716 --> 00:08:38,984
okay? It's very tempting when you're writing code,
173
00:08:38,986 --> 00:08:41,052
especially maybe a lot of the code that you've written so
174
00:08:41,054 --> 00:08:43,555
far in classes, to think, well, I can just put a method
175
00:08:43,557 --> 00:08:45,924
here and a method there, and they can all call each other,
176
00:08:45,926 --> 00:08:48,426
and it's all fine. But it's not, you need to start
177
00:08:48,428 --> 00:08:51,630
thinking in terms of programming interfaces, okay,
178
00:08:51,632 --> 00:08:55,200
encapsulation, true object oriented programming here.
179
00:08:55,202 --> 00:08:58,403
Okay, I made you think about that a little bit in your
180
00:08:58,405 --> 00:08:58,436
calculator brain and the calculator view controller,
181
00:08:58,438 --> 00:09:01,640
assignment with
182
00:09:01,642 --> 00:09:03,642
right? The calculator brain has a public API,
183
00:09:03,644 --> 00:09:05,677
it's very clear what the public methods are,
184
00:09:05,679 --> 00:09:08,847
those are the only methods a controller can use, okay?
185
00:09:08,849 --> 00:09:09,915
Hopefully you didn't write your assignment,
186
00:09:09,917 --> 00:09:12,584
where they're each calling, private messaging each other,
187
00:09:12,586 --> 00:09:15,987
or things like that, okay? There's gotta be well-defined,
188
00:09:15,989 --> 00:09:17,956
what those objects encapsulate, okay.
189
00:09:17,958 --> 00:09:19,491
That's object oriented programming. So
190
00:09:19,493 --> 00:09:23,028
same thing here, but at the whole MVC level, right?
191
00:09:23,030 --> 00:09:26,331
We want the MVCs to be well-defined in what they do.
192
00:09:26,333 --> 00:09:30,101
All right, so let's talk about prepareForSegue here and,
193
00:09:30,103 --> 00:09:30,235
how you make it work, what all the parts of it are, okay? So
194
00:09:30,237 --> 00:09:33,572
what it does,
195
00:09:33,574 --> 00:09:35,140
first of all, you have the arguments here for
196
00:09:35,142 --> 00:09:38,443
prepareForSegue. The first argument is the segue. So
197
00:09:38,445 --> 00:09:42,113
this is a instance of a class called UIStoryboardSegue.
198
00:09:42,115 --> 00:09:45,717
And it contains interesting information about the segue,
199
00:09:45,719 --> 00:09:47,152
most notably the identifier.
200
00:09:47,154 --> 00:09:51,356
Okay, that identifier we typed in the inspector, and also,
201
00:09:51,358 --> 00:09:54,993
the controller of the MVC that you're segueing to.
202
00:09:54,995 --> 00:09:59,164
It just created it, and you can, cuz remember,
203
00:09:59,166 --> 00:10:01,866
segue is always created an MVC, so it just created
204
00:10:01,868 --> 00:10:03,835
that thing, and you can get a pointer to it
205
00:10:03,837 --> 00:10:07,072
from this segue argument right here, one of the properties on
206
00:10:07,074 --> 00:10:10,775
UIStoryboardSegue is the destination view controller.
207
00:10:10,777 --> 00:10:12,911
Okay, this other argument here, sender,
208
00:10:12,913 --> 00:10:16,915
it's the same sender as was in perform segue with identifier.
209
00:10:16,917 --> 00:10:20,452
This is just who caused this segue to happen. Usually,
210
00:10:20,454 --> 00:10:23,622
it's a button, okay. But it could be any object if you,
211
00:10:23,624 --> 00:10:26,658
if it started in code with perform segue with identifier,
212
00:10:26,660 --> 00:10:30,295
okay? So we'll, we'll see this in the demo sometimes why you
213
00:10:30,297 --> 00:10:35,300
would wanna know who this is, okay? All right, so
214
00:10:35,302 --> 00:10:36,234
here's the identifier,
215
00:10:36,236 --> 00:10:39,904
I'm getting it with the, var here called identifier,
216
00:10:39,906 --> 00:10:42,807
right, the property off of the segue thing.
217
00:10:42,809 --> 00:10:45,710
I'm checking here to make sure it's not nil. Because someone
218
00:10:45,712 --> 00:10:47,679
might have created a segue in the storyboard, and
219
00:10:47,681 --> 00:10:49,314
they forgot to set the identifier or
220
00:10:49,316 --> 00:10:51,282
they didn't set the identifier. So
221
00:10:51,284 --> 00:10:51,416
it could be nil, so
222
00:10:51,418 --> 00:10:54,119
I'm checking to make sure it's not nil here. And then
223
00:10:54,121 --> 00:10:57,088
I'm switching on it, okay? So I'm gonna switch on it.
224
00:10:57,090 --> 00:10:59,624
And in this case, I'm gonna look for Show Graph,
225
00:10:59,626 --> 00:11:02,627
which is in my previous slide, that's what I called,
226
00:11:02,629 --> 00:11:05,363
that's what the identifier for that thing was.
227
00:11:05,365 --> 00:11:09,200
And when I see that this is a Show Graph segue, then
228
00:11:09,202 --> 00:11:13,705
I'm going to try and prepare the destination of the segue,
229
00:11:13,707 --> 00:11:17,609
assuming that it is some sort of graph controller, right,
230
00:11:17,611 --> 00:11:18,176
some code controller,
231
00:11:18,178 --> 00:11:20,245
controller of an MVC that shows the graph. So
232
00:11:20,247 --> 00:11:22,080
I'm gonna have to prepare it, I'm gonna have to prepare it,
233
00:11:22,082 --> 00:11:25,216
okay. So the first thing that I'm gonna do is I'm gonna look
234
00:11:25,218 --> 00:11:28,420
at the destination view controller of the segue, where
235
00:11:28,422 --> 00:11:32,457
we're segueing to, okay. And I'm gonna see if it's a graph
236
00:11:32,459 --> 00:11:34,693
controller. And it better be, because that's the only
237
00:11:34,695 --> 00:11:38,730
way I know how to prepare for a segue called Show Graph. So
238
00:11:38,732 --> 00:11:42,367
this thing better be, but I am doing if let with as to,
239
00:11:42,369 --> 00:11:46,604
to see if it is, because destination view controller,
240
00:11:46,606 --> 00:11:50,041
its type is just UIViewController, okay. So
241
00:11:50,043 --> 00:11:53,311
I need to cast it with as just like I cast any
242
00:11:53,313 --> 00:11:54,546
object sometimes with as.
243
00:11:54,548 --> 00:11:56,915
Here I'm gonna cast this view controller, if I can,
244
00:11:56,917 --> 00:11:59,751
to be a specific kind of controller that I'm expecting
245
00:11:59,753 --> 00:12:02,754
this segue to be segueing to. Okay, so in this case,
246
00:12:02,756 --> 00:12:05,824
some sort of graph controller of some sort. All right,
247
00:12:05,826 --> 00:12:09,961
now once I've done that, now VC right here is gonna be
248
00:12:09,963 --> 00:12:13,498
of type graph controller. Okay, that's what if let
249
00:12:13,500 --> 00:12:16,434
this as that does, right, inside this curly brace,
250
00:12:16,436 --> 00:12:20,038
vc is gonna be of type graph controller. And so I can start
251
00:12:20,040 --> 00:12:24,743
sending messages and setting properties, public ones only,
252
00:12:24,745 --> 00:12:29,814
of this controller in order to prepare it to do its job. So
253
00:12:29,816 --> 00:12:32,117
if it's a graph controller, I don't know,
254
00:12:32,119 --> 00:12:34,919
it might wanna know the axes, the range of the axes.
255
00:12:34,921 --> 00:12:38,289
Somehow it might wanna know what function it's graphing,
256
00:12:38,291 --> 00:12:40,992
I don't know. Okay, but that is happening in here,
257
00:12:40,994 --> 00:12:43,728
where I'm preparing this graph controller that just got
258
00:12:43,730 --> 00:12:48,433
created cuz of a segue to do what it does. Okay, questions
259
00:12:48,435 --> 00:12:53,238
about that? All right, so it's really important
260
00:12:53,240 --> 00:12:55,840
to understand, and that's why it's in red here, I don't put
261
00:12:55,842 --> 00:12:59,077
things in red very often. But it's in red that when you're
262
00:12:59,079 --> 00:13:02,046
doing this prepare of that destination view controller,
263
00:13:02,048 --> 00:13:07,218
it was only just created just now, okay, femtoseconds ago,
264
00:13:07,220 --> 00:13:12,090
okay. So its outlets have not been set, okay. This happens
265
00:13:12,092 --> 00:13:15,393
before its outlets are set. This is quite annoying, okay,
266
00:13:15,395 --> 00:13:17,896
because usually when you wanna prepare a view controller,
267
00:13:17,898 --> 00:13:20,899
you wanna set things up in its outlets, like a calculator
268
00:13:20,901 --> 00:13:22,767
one, you'd want to set something in the display or
269
00:13:22,769 --> 00:13:25,403
something like that. But you can't do that, okay,
270
00:13:25,405 --> 00:13:26,171
unfortunately, you can't do that.
271
00:13:26,173 --> 00:13:28,306
This is happening before your outlets are set, so
272
00:13:28,308 --> 00:13:31,009
you have to just give it the data it wants, and you're just
273
00:13:31,011 --> 00:13:33,545
gonna have to hold on to that data. And then after its
274
00:13:33,547 --> 00:13:37,015
outlets are set, it can start parceling that out to its UI,
275
00:13:37,017 --> 00:13:41,052
okay? You will, at least once this quarter, get a crash,
276
00:13:41,054 --> 00:13:44,522
because you'll start preparing some view controller.
277
00:13:44,524 --> 00:13:47,792
You'll start accessing its outlets, it goes out with your
278
00:13:47,794 --> 00:13:50,361
still nil, and you're gonna get optional nil was
279
00:13:50,363 --> 00:13:53,097
accessed, okay. I'm gonna do it in my demo just to show
280
00:13:53,099 --> 00:13:55,300
you it happening because it can happen so much.
281
00:13:55,302 --> 00:13:58,770
All right, okay, so that's preparing,
282
00:13:58,772 --> 00:14:03,041
super important part of segues, okay, so preparing.
283
00:14:03,043 --> 00:14:06,110
Now there's a method you can also implement in your view
284
00:14:06,112 --> 00:14:09,113
controller in addition to prepareForSegue called,
285
00:14:09,115 --> 00:14:11,416
shouldPerformSegueWithIdentif- ier, okay.
286
00:14:11,418 --> 00:14:14,219
And that is basically gonna return a bool, whether or
287
00:14:14,221 --> 00:14:18,423
not that segue should happen. So you can prevent it, for
288
00:14:18,425 --> 00:14:19,891
example, let's say in your calculator,
289
00:14:19,893 --> 00:14:22,527
let's say you had a button that did a graph, and
290
00:14:22,529 --> 00:14:24,229
let's say it was a partial result, so
291
00:14:24,231 --> 00:14:27,999
you couldn't really graph it. This might return false, okay?
292
00:14:28,001 --> 00:14:31,703
And maybe you'll see that on your homework. All right, so
293
00:14:31,705 --> 00:14:33,905
the demo I'm gonna do is I'm gonna take FaceIt,
294
00:14:33,907 --> 00:14:36,407
that we have so far that draws that face, I'm gonna add
295
00:14:36,409 --> 00:14:39,510
a second MVC, okay. And this MVC's gonna have some emotions
296
00:14:39,512 --> 00:14:41,279
listened in it, and when you click on them,
297
00:14:41,281 --> 00:14:44,415
it's gonna show a face that reflects that emotion, okay?
298
00:14:44,417 --> 00:14:45,350
So I'm gonna have two MVCs, and
299
00:14:45,352 --> 00:14:53,358
we're gonna hook them up. Okay,
300
00:14:53,360 --> 00:14:57,161
so here we are back in FaceIt, exactly where we were.
301
00:14:57,163 --> 00:15:00,531
I haven't, added anything or changed anything, right?
302
00:15:00,533 --> 00:15:02,700
So we've got our face view controller right here,
303
00:15:02,702 --> 00:15:05,703
it's got its model, which is a facial expression. And
304
00:15:05,705 --> 00:15:08,506
then it's got its view, which is this face view thing right
305
00:15:08,508 --> 00:15:12,977
here. We add some gestures, we update our UI when this thing
306
00:15:12,979 --> 00:15:16,447
is first set, our update UI just takes the model,
307
00:15:16,449 --> 00:15:19,651
all these things out of the model, okay? And
308
00:15:19,653 --> 00:15:21,653
it turns them into things in the view,
309
00:15:21,655 --> 00:15:23,922
and and these are just the handlers for
310
00:15:23,924 --> 00:15:25,256
these gestures. And that's it, so
311
00:15:25,258 --> 00:15:28,760
that's all we have in our controller. And our story
312
00:15:28,762 --> 00:15:31,796
board is really, really simple, it only has this one
313
00:15:31,798 --> 00:15:35,433
view controller, okay, this face view controller. And
314
00:15:35,435 --> 00:15:38,503
what I'm gonna do right off the bat is add another MVC.
315
00:15:38,505 --> 00:15:41,572
So how do you add another MVC to your storyboard?
316
00:15:41,574 --> 00:15:45,043
Well, you go over here to your object palette, and just like
317
00:15:45,045 --> 00:15:48,579
from your object palette, you can do things like, adding
318
00:15:48,581 --> 00:15:51,816
buttons and labels, you can also add entirely new MVCs.
319
00:15:51,818 --> 00:15:54,485
And the way you do that is by grabbing this thing called
320
00:15:54,487 --> 00:15:57,889
view controller at the top. In dragging it out into your
321
00:15:57,891 --> 00:16:00,224
storyboard, okay. You can put it right there.
322
00:16:00,226 --> 00:16:03,628
You can move things around wherever you want. Okay, so
323
00:16:03,630 --> 00:16:07,098
now I have two MVCs in here. One really interesting thing
324
00:16:07,100 --> 00:16:12,704
to note is that an NVC always need to have a custom class.
325
00:16:12,706 --> 00:16:14,472
Okay, if it doesn't have a custom class,
326
00:16:14,474 --> 00:16:16,874
where are you going to put your outlets and actions and
327
00:16:16,876 --> 00:16:20,311
all that stuff. So just like this view controller,
328
00:16:20,313 --> 00:16:22,981
if you look at it's identity inspector, remember we changed
329
00:16:22,983 --> 00:16:25,650
it's identity and the identity inspector to be face
330
00:16:25,652 --> 00:16:27,719
view controller, okay. So that makes it so
331
00:16:27,721 --> 00:16:30,655
that this class is the controller for that thing, so
332
00:16:30,657 --> 00:16:34,859
it has a model and outlets and things like that. Okay we need
333
00:16:34,861 --> 00:16:37,128
to do the exact same thing with this view controller.
334
00:16:37,130 --> 00:16:40,164
It needs to have a custom class. So the way you create
335
00:16:40,166 --> 00:16:43,034
classes, you'll remember just like we did when we created
336
00:16:43,036 --> 00:16:46,938
Face view for example. Is we do new file here. Okay so
337
00:16:46,940 --> 00:16:50,174
I'm do new file. It's going to be iOS source, It's going to
338
00:16:50,176 --> 00:16:52,543
be Coco Touch class because it's going to be a sub class
339
00:16:52,545 --> 00:16:56,047
of UI view controller. Just like Face View controller was.
340
00:16:56,049 --> 00:16:58,983
So I'm gonna double click here. Here, subclass of,
341
00:16:58,985 --> 00:17:02,453
this is good, okay. Remember we did UI View before,
342
00:17:02,455 --> 00:17:05,523
now we want UI View controller as our class. We don't want
343
00:17:05,525 --> 00:17:07,859
to call it view controller, that's a very generic name, so
344
00:17:07,861 --> 00:17:10,862
I'm gonna call it Emotions View Controller because that's
345
00:17:10,864 --> 00:17:12,563
what it shows. This view controller shows emotions,
346
00:17:12,565 --> 00:17:16,134
okay. Put it in the same place we always put it,
347
00:17:16,136 --> 00:17:18,536
right here, same same as all our other code.
348
00:17:18,538 --> 00:17:22,373
And here we go. Here's our emotions view controller,
349
00:17:22,375 --> 00:17:24,509
again I'm going to remove these few controller
350
00:17:24,511 --> 00:17:28,513
life cycle methods for now. But
351
00:17:28,515 --> 00:17:30,114
notice also when I created this,
352
00:17:30,116 --> 00:17:34,819
I got this little method. What is this method? Prepare for
353
00:17:34,821 --> 00:17:38,256
segue. Okay, segues are so important to have in multiple
354
00:17:38,258 --> 00:17:40,825
view controllers. That when it gives you a blank one, it
355
00:17:40,827 --> 00:17:43,161
always gives you prepare for segue commented out, but it
356
00:17:43,163 --> 00:17:45,530
knows you're almost certainly gonna want to prepare for
357
00:17:45,532 --> 00:17:47,865
segue. Okay, that's how important this is. And indeed
358
00:17:47,867 --> 00:17:51,436
we are gonna have to implement this if we want clicking on
359
00:17:51,438 --> 00:17:54,172
emotions to go to our other MVC and do things, okay?
360
00:17:54,174 --> 00:17:57,008
But we're not going to do this right now. The next
361
00:17:57,010 --> 00:17:59,677
thing we're going to do, and you'll forget this step, okay?
362
00:17:59,679 --> 00:18:02,213
You will for sure forget this step multiple times.
363
00:18:02,215 --> 00:18:05,983
Which is we have to remember to go here and set in
364
00:18:05,985 --> 00:18:11,155
the identity inspector, the class of this thing to be that
365
00:18:11,157 --> 00:18:13,958
emotion's view controller. A lot of times you will create
366
00:18:13,960 --> 00:18:15,560
that emotion view control, you just dive right in,
367
00:18:15,562 --> 00:18:17,929
you start programming, and you forget to do this, and then
368
00:18:17,931 --> 00:18:19,931
you cannot connect any of your outlets, and you are like,
369
00:18:19,933 --> 00:18:22,300
what is wrong with this thing? Okay, well this is why.
370
00:18:22,302 --> 00:18:23,835
You forgot to type your identity, here,
371
00:18:23,837 --> 00:18:26,537
okay? So, now I have two, you see? This one says emotions
372
00:18:26,539 --> 00:18:29,040
view controller and this says face view controller, so
373
00:18:29,042 --> 00:18:32,510
two NVCs. Notice this little arrow right here.
374
00:18:32,512 --> 00:18:37,014
You see this arrow? This arrow tells the app when it runs,
375
00:18:37,016 --> 00:18:39,884
start with this view controller. So
376
00:18:39,886 --> 00:18:42,854
if I were to run right now it would show the face, but
377
00:18:42,856 --> 00:18:45,556
if I wanted to show my new view controller first I can
378
00:18:45,558 --> 00:18:49,560
just pick this arrow up and drop it on this one.
379
00:18:49,562 --> 00:18:52,630
Okay, so let's go ahead and run actually to verify that.
380
00:18:52,632 --> 00:18:58,536
We should get a nice blank screen. Okay?
381
00:18:58,538 --> 00:19:02,540
Here it is. Nice blank screen, working perfectly. Now,
382
00:19:02,542 --> 00:19:05,977
one thing to notice, also is, it, I got a warning here.
383
00:19:05,979 --> 00:19:09,113
Look, see this warning? Scene is unreachable due to lack of
384
00:19:09,115 --> 00:19:10,715
entry points. It's talking about this scene,
385
00:19:10,717 --> 00:19:14,385
okay? By the way, we call these MVCs in the storyboard.
386
00:19:14,387 --> 00:19:18,055
This is called a scene. Okay? A scene. So this scene
387
00:19:18,057 --> 00:19:21,025
is unreachable, because this arrow doesn't point to it.
388
00:19:21,027 --> 00:19:25,396
And nothing in here segues to it. So you can't get to it,
389
00:19:25,398 --> 00:19:27,431
okay, which makes sense. So
390
00:19:27,433 --> 00:19:30,434
it's good that it's warning us about that. All right let me
391
00:19:30,436 --> 00:19:33,871
build the UI of this guy right here. Okay? As I said it's
392
00:19:33,873 --> 00:19:37,642
just gonna have some emotions in here that you can click on.
393
00:19:37,644 --> 00:19:40,211
Normally you probably wanna do this with the table view.
394
00:19:40,213 --> 00:19:42,446
Okay a table and extensible list of them.
395
00:19:42,448 --> 00:19:45,016
But I can't I'm not teaching a table view until next week.
396
00:19:45,018 --> 00:19:46,484
So we're gonna use buttons. All right?
397
00:19:46,486 --> 00:19:52,156
So I'm just drag out four buttons here to be my
398
00:19:52,158 --> 00:19:57,828
the four emotions that I am going to have here.
399
00:19:57,830 --> 00:19:58,763
Sorry, put this in here.
400
00:19:58,765 --> 00:20:01,499
And when I drag it out, it does not really matter where I
401
00:20:01,501 --> 00:20:04,101
put it because I am going to stack view them. So
402
00:20:04,103 --> 00:20:07,939
let us go ahead and make the font a little bigger here, 40
403
00:20:07,941 --> 00:20:13,144
point. And so what buttons do we wanna have here? How about
404
00:20:13,146 --> 00:20:17,915
an angry button and we'll have three other buttons.
405
00:20:17,917 --> 00:20:24,922
How about happy and how about worried? Is that a,
406
00:20:24,924 --> 00:20:26,991
an emotion, worried? I don't, I don't know.
407
00:20:26,993 --> 00:20:30,995
It's good to make a face of it though. How about mischievous?
408
00:20:30,997 --> 00:20:34,865
Mischievous, is that how you spell that?
409
00:20:34,867 --> 00:20:36,667
Again I'm not sure that's an emotion, but
410
00:20:36,669 --> 00:20:39,870
it makes a good face. Okay so here I have my things,
411
00:20:39,872 --> 00:20:40,605
I'm going to stack view these.
412
00:20:40,607 --> 00:20:45,176
Just going to select them all and go embed in stack view.
413
00:20:45,178 --> 00:20:46,844
I don't want them left aligned like this, so
414
00:20:46,846 --> 00:20:49,947
I'm going to change their alignment to fill the space.
415
00:20:49,949 --> 00:20:53,317
I'll do fill equally although they're all the same font so
416
00:20:53,319 --> 00:20:55,019
they're all gonna be the same height anyway so
417
00:20:55,021 --> 00:20:58,823
it doesn't really matter there. So,
418
00:20:58,825 --> 00:21:02,660
I want these words to always be in the middle of my screen,
419
00:21:02,662 --> 00:21:04,895
even if I'm rotated like if I'm in landscape or
420
00:21:04,897 --> 00:21:07,565
portrait, I always want them in the middle. So I'm gonna
421
00:21:07,567 --> 00:21:10,735
use the dash blue lines to put it right in the middle.
422
00:21:10,737 --> 00:21:13,704
And now you're going to do the same trick we did before,
423
00:21:13,706 --> 00:21:14,739
which was rest to suggested constraints and
424
00:21:14,741 --> 00:21:17,475
it's going to use those blue lines
425
00:21:17,477 --> 00:21:20,444
to suggest some constraints to constrain this stack for
426
00:21:20,446 --> 00:21:23,180
you on here. And let's go look what it suggested,
427
00:21:23,182 --> 00:21:26,284
remember to do that we go over here to the size inspector.
428
00:21:26,286 --> 00:21:29,320
We go down to where it says constraints, and sure enough,
429
00:21:29,322 --> 00:21:32,023
align the center in the x-axis to superview and
430
00:21:32,025 --> 00:21:34,158
align the center in the y-axis to superview. Perfect,
431
00:21:34,160 --> 00:21:39,363
exactly what I want. So now if I run, it's gonna center this
432
00:21:39,365 --> 00:21:43,567
horizontally and vertically. Okay, even if I'm in
433
00:21:43,569 --> 00:21:48,406
landscape. Okay? So now, I've built this UI. Great,
434
00:21:48,408 --> 00:21:52,943
it's exactly what I want. And I had now have two NVCs. Now,
435
00:21:52,945 --> 00:21:56,347
I'm gonna show you all three ways of combining NVCs, tab
436
00:21:56,349 --> 00:21:59,583
bars, split view controllers, and navigation controllers.
437
00:21:59,585 --> 00:22:02,019
Let's start with tab bar controller, okay? How would I
438
00:22:02,021 --> 00:22:05,289
put these two things one in its, each in its own tab?
439
00:22:05,291 --> 00:22:10,161
Well, I'm gonna go down here, okay? To our object pallet.
440
00:22:10,163 --> 00:22:12,730
And if you look, about six or seven down, there's this.
441
00:22:12,732 --> 00:22:15,733
Tab bar controller, you see that? And if I drag this
442
00:22:15,735 --> 00:22:19,937
out here, okay, it'll put this tab bar out here.
443
00:22:19,939 --> 00:22:23,107
It unfortunately also brings some extra MVCs here,
444
00:22:23,109 --> 00:22:26,944
these little blank MVCs that I don't want. So I'm just gonna
445
00:22:26,946 --> 00:22:29,647
click on those and delete them. So get rid of that one.
446
00:22:29,649 --> 00:22:33,084
And get rid of that one. So all I have now is my tab bar
447
00:22:33,086 --> 00:22:37,088
controller. If you can read that, I'll zoom in a little.
448
00:22:37,090 --> 00:22:40,658
Okay see tab bar controller and then I've got my two
449
00:22:40,660 --> 00:22:43,994
MVC's here that I created. My emotions view controller and
450
00:22:43,996 --> 00:22:47,598
my face view controller. Now I for sure want
451
00:22:47,600 --> 00:22:50,868
the app to start with the tab bar controller, so I'm going
452
00:22:50,870 --> 00:22:53,003
to move that arrow. This is another thing you'll forget.
453
00:22:53,005 --> 00:22:56,140
You'll forget to move this arrow. Okay. And
454
00:22:56,142 --> 00:22:59,243
now I just want this to be in one tab and this in the other.
455
00:22:59,245 --> 00:23:02,012
I just do that with control drag. So control
456
00:23:02,014 --> 00:23:06,150
drag to this one, I'm going to hook up this view controllers.
457
00:23:06,152 --> 00:23:06,851
Remember the view controllers
458
00:23:06,853 --> 00:23:10,354
property in a tab bar is just an array of all the tabs.
459
00:23:10,356 --> 00:23:13,991
Okay, the MVCs is there in the tabs and do this one also,
460
00:23:13,993 --> 00:23:17,528
that in the view controllers array, okay. You can even see
461
00:23:17,530 --> 00:23:19,897
it's already starting to put the tabs on the bottom.
462
00:23:19,899 --> 00:23:22,933
If you wanted to edit what's in these tabs, like the names,
463
00:23:22,935 --> 00:23:26,137
you think, you double click down here something, but no,
464
00:23:26,139 --> 00:23:30,708
because this is the Tab Bar Controller and these titles
465
00:23:30,710 --> 00:23:35,246
and images are all set by the MVCs inside. Okay,
466
00:23:35,248 --> 00:23:38,482
in an object oriented way, right. So, if you wanted to
467
00:23:38,484 --> 00:23:40,484
set this you would actually go to each one of them.
468
00:23:40,486 --> 00:23:43,621
And you can even double click right here and put in whatever
469
00:23:43,623 --> 00:23:47,191
you want. You can also click here and inspect them, okay,
470
00:23:47,193 --> 00:23:50,594
if you go to the inspector, you can set images and also
471
00:23:50,596 --> 00:23:55,332
a bunch of system ones. Okay, so that's how you set that.
472
00:23:55,334 --> 00:23:59,403
So this is what our Storyboard looks like right now and
473
00:23:59,405 --> 00:24:06,844
if we run this. We'll see that we get a tab bar.
474
00:24:06,846 --> 00:24:09,313
See, it's got two tabs at the bottom here. And
475
00:24:09,315 --> 00:24:12,616
if I click between them, I get my two MVCs.
476
00:24:12,618 --> 00:24:16,187
Now this is completely inappropriate to what I'm
477
00:24:16,189 --> 00:24:19,690
building here because I would never want, if I clicked on
478
00:24:19,692 --> 00:24:23,360
one of these, for it to switch over to this tab, okay?
479
00:24:23,362 --> 00:24:27,498
That's because when you do a tab bar UI, listen closely,
480
00:24:27,500 --> 00:24:30,935
the user owns which tab is showing, okay?
481
00:24:30,937 --> 00:24:33,871
They get to choose by clicking on those tabs. You don't get
482
00:24:33,873 --> 00:24:36,240
to choose by throwing them over to one, okay?
483
00:24:36,242 --> 00:24:40,811
That is not appropriate for tab bar. Everyone got that? So
484
00:24:40,813 --> 00:24:43,681
things in a tab bar UI tend to be independent things,
485
00:24:43,683 --> 00:24:45,950
things that don't segue from one to another,
486
00:24:45,952 --> 00:24:48,786
you can't even make a segue from one tab to another, so no
487
00:24:48,788 --> 00:24:52,823
such segue. So we don't want to do a tab bar controller
488
00:24:52,825 --> 00:24:55,259
here, so I'm just going to delete my tab bar controller,
489
00:24:55,261 --> 00:24:57,962
okay? Now one thing you'll notice when I deleted my tab
490
00:24:57,964 --> 00:25:02,333
bar controller is, where's my arrow? Okay, it's gone. So
491
00:25:02,335 --> 00:25:05,102
now it's not going to, if I run, I'm actually going to get
492
00:25:05,104 --> 00:25:08,506
an error in the console saying I don't know where to start
493
00:25:08,508 --> 00:25:11,909
in my storyboard, basically. So how do you get that arrow
494
00:25:11,911 --> 00:25:13,511
back? Just click the one you want
495
00:25:13,513 --> 00:25:17,281
the arrow to be on, inspect it, and go right down here,
496
00:25:17,283 --> 00:25:19,550
very top, is initial view controller,
497
00:25:19,552 --> 00:25:22,786
if you turn that on, the arrow will come back there, okay?
498
00:25:22,788 --> 00:25:25,990
And you can move it, okay, down to here if you want.
499
00:25:25,992 --> 00:25:30,027
Okay? All right, so let's talk about split view controller.
500
00:25:30,029 --> 00:25:33,230
We haven't even done anything with iPad at all in this class
目前为止,这门课还没有任何涉及 iPad 应用的内容
501
00:25:33,232 --> 00:25:36,200
yet. So this is our first time doing it and we're gonna put
所以现在是我们第一次讨论关于
502
00:25:36,202 --> 00:25:40,004
this in split view controller where this is the master and
有 master controller 和 detail controller 的
503
00:25:40,006 --> 00:25:41,839
this is the detail, makes sense, right?
split view controller 明白了没?
504
00:25:41,841 --> 00:25:42,706
Because we're clicking in the master,
我们点这个展示 master controller
505
00:25:42,708 --> 00:25:46,110
this will be the detail that's shown from clicking on this.
点这个展示 detail controller
506
00:25:46,112 --> 00:25:47,545
So how do we do a split view controller?
我们怎么处理一个 split view controller 呢?
507
00:25:47,547 --> 00:25:50,514
Same way as tab bar. I'm gonna go down here, right below tab
给 tab bar 的一样,我在这里
508
00:25:50,516 --> 00:25:53,884
bar is split view controller. I'm gonna drag that out,
Tab Bar Controller 下面就是这个 Split View Controller
509
00:25:53,886 --> 00:25:56,987
I get a whole bunch of junk with it. We'll zoom out so
把这个拖出来,我就得到了一大堆垃圾(controller)
510
00:25:56,989 --> 00:26:00,858
you can see it. Okay. See, I got all these extra things, so
缩小了你就看见了,我得到了好多额外的东西
511
00:26:00,860 --> 00:26:02,526
I'm gonna, click on these extra things and
所以我要把这些额外的统统删掉
512
00:26:02,528 --> 00:26:05,896
delete them, don't want any of them. Okay. So I just have
一个都不想留,所以我现在只有
513
00:26:05,898 --> 00:26:08,832
the split view controller and my two guys here.
这个 split view controller 和 刚刚那两个 controller
514
00:26:08,834 --> 00:26:11,402
Of course I want the split view controller, again,
我想让这个 split view controller 变为最早展示出来
515
00:26:11,404 --> 00:26:14,471
to be the start so I'm going to put the arrow there. And
所以我把箭头放在这里
516
00:26:14,473 --> 00:26:18,676
then, in order to hook up master and detail, Ctrl+Drag.
然后,为了将 master、detail 与 split view controller 联系起来
517
00:26:18,678 --> 00:26:24,181
Master, Ctrl+Drag, detail. Okay,
Ctrl+Drag 选择 Master,Ctrl+Drag 选择 detail
518
00:26:24,183 --> 00:26:26,917
so that's how we set it up, couldn't be simpler. So let's
我们就是这么建立起他们的关系,再简单不过了
519
00:26:26,919 --> 00:26:30,854
run, but let's run on iPad, so I'm gonna, on iPad 2 here,
现在在 iPad 上运行,我用 iPad 2 模拟器
520
00:26:30,856 --> 00:26:34,425
we'll run and hopefully this will show up as a master and
运行起来,希望这个会作为 master
521
00:26:34,427 --> 00:26:42,566
this will be the detail. All right. So here's the iPad.
这个会作为 detail 展示出来,这是 iPad
522
00:26:42,568 --> 00:26:45,469
Here's the detail. You notice that when it's in portrait
这是 detail controller,你可能注意到当在竖屏状态下
523
00:26:45,471 --> 00:26:46,604
it fills the screen with the detail,
detail controller 充满了屏幕
524
00:26:46,606 --> 00:26:50,608
and you get the master by dragging out from the side.
通过拖动侧边,master controller 就展示出来了
525
00:26:50,610 --> 00:26:53,744
See that? Okay? That's how you get the master. And
看到了吗?竖屏就通过这种方式看到 master controller
526
00:26:53,746 --> 00:26:54,778
if you're in landscape,
如果横过来,变成横屏状态
527
00:26:54,780 --> 00:26:58,782
then they're both onscreen at the same time. Now, if I click
master 和 detail 就在屏幕上同时展示出来
528
00:26:58,784 --> 00:27:02,753
on these, I want this face to show me angry, show me happy,
如果我点 Angry,我希望这个脸变成生气的样子
529
00:27:02,755 --> 00:27:04,455
show me worried, show me mischievous, okay?
变成高兴、变成担忧、变成淘气的样子
530
00:27:04,457 --> 00:27:07,224
So how do I do that? This is where the segues come in.
这怎么做呢?这时我们就需要用到 segue 了
531
00:27:07,226 --> 00:27:10,561
Okay? This is where we have to have this view controller
我们需要让这个 view controller 转场
532
00:27:10,563 --> 00:27:12,396
segue to create a new one of these and
然后 create 一个detail controller
533
00:27:12,398 --> 00:27:15,132
when it creates this, it's going to create it with
当 create 时,会 create 一个有确切表情值的 controller
534
00:27:15,134 --> 00:27:18,769
the proper facial expression, okay? So it's going to prepare
所以,它会准备好表情值
535
00:27:18,771 --> 00:27:22,172
it with a facial expression and show it. Okay? So
然后将这个确切的表情展示出来
536
00:27:22,174 --> 00:27:26,276
let's do those segues. The way we do segues, also Ctrl+Drag.
我们来设置这些转场,同样是 Ctrl Drag 设置
537
00:27:26,278 --> 00:27:29,847
So, let me zoom in here so you can see this. All right?
我放大点你就可以看到了
538
00:27:29,849 --> 00:27:32,783
I'm gonna Ctrl+Drag from whichever of these buttons
我按住 segue 开始的 button,然后 Ctrl + Drag
539
00:27:32,785 --> 00:27:35,352
I want to segue from. I'm gonna segue from all four, so
我想从这4个button开始连 segue
540
00:27:35,354 --> 00:27:38,222
I'm gonna have four segues. So like angry here I'm
所以我应该有4个segue,像从这里按住 angry button
541
00:27:38,224 --> 00:27:42,493
just gonna Ctrl+Drag down to this MVC. And let go.
然后 Ctrl + Drag 到这个 MVC,然后松手
542
00:27:42,495 --> 00:27:44,728
And it says, well what kind of segue do you want?
这里会让你选择你想要的 segue 类型
543
00:27:44,730 --> 00:27:45,896
Show, show detail, modally or
Show, show detail, modally 或者是
544
00:27:45,898 --> 00:27:49,133
pop over. Well, we're in the split view controller here so
pop over。因为我们用的是 split 控制器,所以
545
00:27:49,135 --> 00:27:52,002
I want to show detail, right? Split view. Master detail.
我选择 show detail
546
00:27:52,004 --> 00:27:55,406
Show detail. Bam. There it is right there. Okay?
这个是split view,然后 master detail 用 show detail 连接,对吧?
547
00:27:55,408 --> 00:27:58,409
Created this little thing here. That's my segue. And
做了这些工作,这就是我的 segue
548
00:27:58,411 --> 00:28:01,578
this segue can be inspected. You can see it up here, okay?
这些 segue 是可以被检查的,你可以在这里看到他们
549
00:28:01,580 --> 00:28:04,214
This is the attributes inspector. And it's got
这是属性检查器,会显示 show detail 的类型
550
00:28:04,216 --> 00:28:07,184
the kind of show detail, also known as a replace segue.
也常常被叫做 replace segue
551
00:28:07,186 --> 00:28:11,055
It animates, it shows that this segue is going to happen
这是 Animates,这个说明这个 segue 可以用动画的形式展现
552
00:28:11,057 --> 00:28:13,991
in an animated fashion, so it's going to slide in or
是滑进或者别的方式
553
00:28:13,993 --> 00:28:16,427
whatever. It's not always makes sense to animate but
并不总是可以选择 Animates 的,有时候可以
554
00:28:16,429 --> 00:28:19,963
sometimes it does. Class and module, don't worry about
有时候不行,不用担心这些,比如 Class 和 Module
555
00:28:19,965 --> 00:28:21,665
that. That's for creating your own custom segues,
这些帮助你设置你自定义的 segue
556
00:28:21,667 --> 00:28:23,233
which we're not going to talk about in this class.
这些内容有一些超前,所以我们这节课不会讨论到
557
00:28:23,235 --> 00:28:26,303
A little bit too advanced. And then, here is the all
最重要的属性是 identifier
558
00:28:26,305 --> 00:28:29,440
important identifier. So here, we have to give any string
我们需要用字符串来唯一确定这个segue
559
00:28:29,442 --> 00:28:34,011
we want that identifies this segue. So I could call this,
比如,我可以叫这个segue
560
00:28:34,013 --> 00:28:38,682
for example, the angry segue, maybe I'd want a verb
为 angry segue
561
00:28:38,684 --> 00:28:41,885
phrase like show anger or something like that but
我应该用一个动词,比如 show anger 或者
562
00:28:41,887 --> 00:28:45,022
it's fine also maybe to have a nice simple word like anger.
就是一个单一的明确的名词,像 anger
563
00:28:45,024 --> 00:28:46,757
So that's my angry segue right there.
所以,这是我的 angry segue
564
00:28:46,759 --> 00:28:50,461
Okay. And so now let's wire up the other ones, Ctrl+Drag this
好,现在我们连一下剩余的控件
565
00:28:50,463 --> 00:28:54,331
one down here. This one is also going to be show detail.
在这里按 Ctrl + Drag,这个是 show detail
566
00:28:54,333 --> 00:28:58,569
They're all going to be show detail. I'll do worried also.
我想这个也应该是 show detail
567
00:28:58,571 --> 00:29:03,340
Show detail, oops, not present modally, get rid of that.
oops,不是 present modally,删掉这个
568
00:29:03,342 --> 00:29:08,278
Alright. Then here. Show detail.
这个也是 Show detail
569
00:29:08,280 --> 00:29:12,716
Mischievous. Show detail.
给淘气的也连上Show detail segue
570
00:29:12,718 --> 00:29:15,886
Okay, now I need to set the identifier for all of these.
现在,我需要给这些 segue 都设置 identifier 作为唯一标示
571
00:29:15,888 --> 00:29:19,223
What's really cool is if I click on one it'll tell me who
特别酷的是,如果我点击一个 segue,他会显示
572
00:29:19,225 --> 00:29:23,227
the sender of this segue is. So there's worried, okay.
这个 segue 的发送者是哪一个,这个是 worried
573
00:29:23,229 --> 00:29:27,030
Worried. Here is happy, and
这个是 happy
574
00:29:27,032 --> 00:29:30,200
we'll call this my happy segue.
我想把这个叫 happy segue
575
00:29:30,202 --> 00:29:34,238
This one, angry already set up. This one, mischievous,
这个,angry 已经设置好了
576
00:29:34,240 --> 00:29:38,876
okay. Mischievous. Hopefully I spelled that right. Okay, so
这个是 Mischievous,希望我正确地拼写了这几个词
577
00:29:38,878 --> 00:29:41,378
now we've got these segues set up so let's run and
现在我设置了这几个 segue,然后运行一下
578
00:29:41,380 --> 00:29:48,218
see what happens here. All right, angry, happy,
看看会发生什么
579
00:29:48,220 --> 00:29:51,855
still nothing's happening. What is the problem here?
什么也没发生,问题出在什么地方呢?
580
00:29:51,857 --> 00:29:54,158
Okay, well, it is actually segueing here.
Okay,这确实是 segue 的问题
581
00:29:54,160 --> 00:29:56,527
It's replacing that face with a new MVC, but
程序用了新的 MVC 来替换这张脸
582
00:29:56,529 --> 00:29:59,663
that new face has not been prepared to show the emotion
但是,这个新 face 还没有准备好展示这个表情
583
00:29:59,665 --> 00:30:02,533
we want so it's just showing the default emotion,
所以它仅仅展示在 storyboard 中展现的
584
00:30:02,535 --> 00:30:05,569
whatever happened to be in our storyboard, right?
默认表情,对吗?
585
00:30:05,571 --> 00:30:07,471
This face right here just happens to be, or or
这个 face 在这里是这么展示的
586
00:30:07,473 --> 00:30:10,374
it's actually what happens to be the default our model is is
或者会展示默认的 model 表现的表情
587
00:30:10,376 --> 00:30:14,611
that. Okay, so we need to, in all these segues, prepare
所以,我们需要给所有 segue 准备
588
00:30:14,613 --> 00:30:18,849
the MVC that we are segueing to. We do that in the source
其需要展示的 model,我们在 segue 的源头设置
589
00:30:18,851 --> 00:30:21,018
view controller, that is the emotions view controller.
在这里应该是 emotions 控制器
590
00:30:21,020 --> 00:30:23,287
This is the thing that implements Prepare for Segue.
实现 prepareForSegue 这个方法
591
00:30:23,289 --> 00:30:26,924
So I am just going to un comment out the comments here,
我要删掉这些注释
592
00:30:26,926 --> 00:30:31,595
okay, for my Prepare for Segue. Make lots of space.
为 prepareForSegue 挪一些空间
593
00:30:31,597 --> 00:30:33,363
Alright, so here we have Prepare for Segue,
在这里写上 prepareForSegue
594
00:30:33,365 --> 00:30:36,466
we get the two arguments, the segue which tells us a little
我们需要两个参数,segue 参数告诉我们
595
00:30:36,468 --> 00:30:37,401
bit about which segue it is and
这是哪个segue
596
00:30:37,403 --> 00:30:40,137
what the destination view controller is and then we have
它的目的 controller 是哪一个
597
00:30:40,139 --> 00:30:42,339
the sender. That's going to be those buttons,
还有一个 sender 参数,这应该是这些按钮
598
00:30:42,341 --> 00:30:44,675
like the angry button, the mischievous button.
像是 angry button、mischievous button
599
00:30:44,677 --> 00:30:45,509
That's what this is going to be,
这些应该就是这些控件
600
00:30:45,511 --> 00:30:46,977
it's going to be the UI button. Okay?
像是 UIButton
601
00:30:46,979 --> 00:30:49,913
This is any object because it is allowed to be any object.
这里必须是一些对象
602
00:30:49,915 --> 00:30:50,747
In this case it's a button, but
在这种情况下,就是 button
603
00:30:50,749 --> 00:30:53,083
it's allowed to be any object. All right.
但是,也可以允许是其他对象
604
00:30:53,085 --> 00:30:54,618
So what do we do here in preparing for
我们在这里应该写些什么来为 segue 做准备呢?
605
00:30:54,620 --> 00:30:57,354
segue? The first thing I'm going to do is get a hold
第一,我需要获取到
606
00:30:57,356 --> 00:30:59,089
of the destination view controller, and
segue 目标 controller
607
00:30:59,091 --> 00:31:01,692
make sure that it's a face view controller.
确保他是 face view controller
608
00:31:01,694 --> 00:31:04,695
Cuz if it's not, I don't really know how to,
如果他不是的话,我确实不知道应该
609
00:31:04,697 --> 00:31:04,761
the segue so I'm just going to do nothing in that case. So
怎么为这个 segue 做准备
610
00:31:04,763 --> 00:31:07,497
to prepare for
在这种条件下
611
00:31:07,499 --> 00:31:09,600
I'm going to create a little local variable, okay?
我需要创造一个局部变量
612
00:31:09,602 --> 00:31:13,537
I'm going to call it destinationvc, destination
叫做 destinationvc
613
00:31:13,539 --> 00:31:18,375
view controller. And that's just equal to the segue,
表示目的的 controller,这个等于
614
00:31:18,377 --> 00:31:21,979
destin, segue, destinationViewController,
segue 的 destinationViewController
615
00:31:21,981 --> 00:31:25,415
okay? Now this is a UIViewController, okay,
现在,这个就是一个 UIViewController
616
00:31:25,417 --> 00:31:30,087
of some sort, it's some sub-class of UIViewController.
但是,应该是 UIViewController 的子类
617
00:31:30,089 --> 00:31:32,556
I suppose it could even be UIViewController itself.
这应该就是 UIViewController 本身
618
00:31:32,558 --> 00:31:35,225
There's probably a subclass of it, and so I need to get
这里应该用一个他的子类,所以我需要获得其子类
619
00:31:35,227 --> 00:31:38,595
that subclass, I need to get this variable, in some way,
我需要让这个变量
620
00:31:38,597 --> 00:31:41,832
so that it's a subclass of it. Or I can't work with it.
变为其子类
621
00:31:41,834 --> 00:31:44,434
Right? Because, UIViewController, I wouldn't
要不然我不能将一些属于 faceViewController 的消息
622
00:31:44,436 --> 00:31:47,871
be able to send any of the face view controller messages
传给他
623
00:31:47,873 --> 00:31:51,642
to it, because it's just a generic UIViewController, so I
因为他只是一个通用的 UIViewController
624
00:31:51,644 --> 00:31:55,612
need to get myself a faceview controller so I'm gonna say,
所以我需要得到一个 faceViewController
625
00:31:55,614 --> 00:31:58,382
if I can let facevc, which is a new variable,
if let facevc
626
00:31:58,384 --> 00:32:01,785
equal the destinationvc As a FaceViewController.
等于之前的变量 destinationvc 作为 FaceViewController
627
00:32:01,787 --> 00:32:04,855
Then I'm ready to rock here. Okay, now I have
然后我准备在这里做操作
628
00:32:04,857 --> 00:32:07,324
a FaceViewController, okay? So I've just casted.
现在,我通过转型获得了一个 FaceViewController
629
00:32:07,326 --> 00:32:10,460
They're similar to how I can't work with any object,
这跟之前我不能做特殊操作的
630
00:32:10,462 --> 00:32:12,429
I really can't work with a generic UIViewController.
通用 UIViewController 类似
631
00:32:12,431 --> 00:32:15,265
I need the subclass. Otherwise how am I going to set its
我获得了他的子类,然后,
632
00:32:15,267 --> 00:32:19,269
model, okay? I have to be able to talk to it. All right. So
接下来,我需要设置这个 controller 的 model
633
00:32:19,271 --> 00:32:22,472
now I have this destination view controller.
现在我有了代表目标的 controller
634
00:32:22,474 --> 00:32:25,676
Now I need the identifier. So here I'm gonna say, if I can
现在,我需要标识符
635
00:32:25,678 --> 00:32:30,180
let the identifier equal the segue's identifier. Okay,
我需要让标识符等于 segue 的标识符
636
00:32:30,182 --> 00:32:33,183
so here I'm just checking to make sure it's not nil.
这样写是为了检查确保它不为空
637
00:32:33,185 --> 00:32:35,953
Now it should be angry, or mischievous, or happy.
他可以是 angry 、mischievous 或者是 happy
638
00:32:35,955 --> 00:32:38,355
One of those identifiers that I put in the stored word,
可以是我之前设置的任何一个标识符,对吧?
639
00:32:38,357 --> 00:32:40,824
right? And I could do something here like,
我应该写
640
00:32:40,826 --> 00:32:42,793
switch on the identifier.
switch 标识符
641
00:32:42,795 --> 00:32:46,964
And if it's angry then I'm going to have my face vc's
如果是 angry,我应该写让我的表情 controller
642
00:32:46,966 --> 00:32:50,634
expression be a facial expression that's angry, blah,
表现生气的表情
643
00:32:50,636 --> 00:32:51,668
blah, blah. I could do that but
blah,我可以这么写
644
00:32:51,670 --> 00:32:55,572
that's going to be allot of if then, blah, blah, okay, mess.
但是,会是一些很乱的代码
645
00:32:55,574 --> 00:32:58,308
So instead I'm going to do one of my favorite ways to
所以,相反的是,我会写一些我喜欢的
646
00:32:58,310 --> 00:33:01,878
program, as you're probably getting the,
编程风格的代码,你可能已经想到了
647
00:33:01,880 --> 00:33:05,048
idea about which is use a dictionary. Okay, so
对的,我们要用字典来表示
648
00:33:05,050 --> 00:33:08,752
I have this dictionary I'm gonna type in real fast, okay,
我快速的写一下这些代码
649
00:33:08,754 --> 00:33:12,556
and this dictionary has keys which are the identifiers, and
这些字典以这些标识符为键
650
00:33:12,558 --> 00:33:17,761
it has values which are the appropriate facial expression.
对应的值为合适的 facial expression 模型
651
00:33:21,100 --> 00:33:21,732
Okay, got that, so
明白了吗?
652
00:33:21,734 --> 00:33:25,402
instead of doing all of this switching on the identifier
与在这里 switch 这些标识符相反的是
653
00:33:25,404 --> 00:33:29,206
thing, I'm just going to say if I can let the expression
让 expression 变量等于我想要的
654
00:33:29,208 --> 00:33:32,275
that I want equal these emotional faces up here.
facial expression 模型
655
00:33:32,277 --> 00:33:38,081
Tha's this dictionary up here. Index by the identifier.
通过标识符来索引字典中的值
656
00:33:38,884 --> 00:33:41,184
Okay? Then I know the expression that I want.
然后,我获得了对应的表情模型
657
00:33:41,186 --> 00:33:45,489
And now I can prepare my face VC okay, by setting its
我可以为我要展示的 face VC 做准备
658
00:33:45,491 --> 00:33:48,992
expression equal to this expression. Okay, and remember
让他的 expression 模型等于这个 expression
659
00:33:48,994 --> 00:33:54,197
FaceVC.expression, that is just FaceViewControllers model
你记得吗? 在 FaceViewControllers 里它的模型是一个
660
00:33:54,199 --> 00:33:57,534
which is a public bar. This View Controller allows
公共变量,它允许
661
00:33:57,536 --> 00:33:59,669
its model to be set externally, publicly,
在外部设置其模型
662
00:33:59,671 --> 00:34:04,041
okay, which makes sense given this kind of model.
这让我们可以在对应的地方设置其 model
663
00:34:04,043 --> 00:34:06,043
Not all, and doesn't make sense always for
这样仅是在这个 viewcontroller 中
664
00:34:06,045 --> 00:34:08,912
every MBC to allow it's model to be set but often it makes
设置其 model ,并不是所有viewcontroller 都可以设置
665
00:34:08,914 --> 00:34:13,216
some sense. Alright, so now I've prepared this Faceview
现在我已经为这个 faceViewController 设置好了
666
00:34:13,218 --> 00:34:17,754
controller to do what it needs to do. Everybody got that? OK.
它需要的东西,大家都明白吗?
667
00:34:17,756 --> 00:34:26,396
So now let's run it and see if it's working. Okay.
现在我们运行一下,来看看是否正确显示
668
00:34:26,398 --> 00:34:30,267
So here we go, we have our iPad here, let's try angry.
这是我们的模拟器 iPad,点击 angry
669
00:34:30,269 --> 00:34:35,338
Crash a roo. Hmm. Wonder why that crashed? Well let's look,
哦,崩溃了,想一想为什么会崩溃呢?我们来检查下
670
00:34:35,340 --> 00:34:37,941
let's use the debugger. Okay, we haven't used the debugger
我们需要用一下 debugger 模式,我们还没有
671
00:34:37,943 --> 00:34:41,044
much in these demos but let's do it here. Seems like,
在这个 demo 中用过这个方法
672
00:34:41,046 --> 00:34:43,280
okay, whenever I have a crash, I always look first in
不管什么时候,程序崩溃了,我总是先查看
673
00:34:43,282 --> 00:34:45,615
the console, okay, because the console is always going to
控制台输出的信息,因为控制台总会告诉我一些
674
00:34:45,617 --> 00:34:47,918
tell me something about what happened. And here it says
关于崩溃的信息,这里显示
675
00:34:47,920 --> 00:34:51,288
unexpectedly found nil while unwrapping an optional value.
在解包一个可选值时,意外地发现了空异常
676
00:34:51,290 --> 00:34:53,857
Okay, well this is probably the number one crasher you're
这种异常你可能经常获得
677
00:34:53,859 --> 00:34:56,927
ever going to get, it's one of the few ways actually to crash
在 Swift 代码中,你可能经常
678
00:34:56,929 --> 00:34:59,963
Swift code is to have unwrapping and nil.
在解包时,发现空异常
679
00:34:59,965 --> 00:35:03,366
So it is happening on this orange line of code.
所以,在这一行,发生了这个异常
680
00:35:03,368 --> 00:35:10,607
I am looking here. Does anyone see an optional here? No?
我看这一行,有人发现这里有可选值了吗?
681
00:35:10,609 --> 00:35:13,110
Nobody sees an optional on this line of code?
没有人在这一行发现可选值吗?
682
00:35:13,112 --> 00:35:15,846
It doesn't look like it, does it? Because you do not see any
这一行看上去没有可选值吗?
683
00:35:15,848 --> 00:35:18,081
exclamation points. I am not unwrapping anything.
可能因为你没有看到感叹号,我没有解包任何值
684
00:35:18,083 --> 00:35:21,251
What is going on? Well actually what is face view,
发生了什么呢?其实是这个 faceVIew
685
00:35:21,253 --> 00:35:25,589
what's its type? Anyone remember? Our outlet,
它的类型有人记得吗?
686
00:35:25,591 --> 00:35:31,094
that's our outlet remember here? Right, it's a optional
这是我们的 outlet,还记得吗?
687
00:35:31,096 --> 00:35:36,900
face view, okay? It's implicitly unwrapped okay,
这是一个可选值得 faceView,他隐式解包了
688
00:35:36,902 --> 00:35:40,904
but it's still an optional. And I told you in the slides,
但是他确实是一个可选值,ppt 里我说过
689
00:35:40,906 --> 00:35:45,041
that your outlets are not set at the time you're preparing.
在你准备 segue 时,这些outlet还没有被设置
690
00:35:45,043 --> 00:35:49,179
So this face view right here, okay, is nil, let's look.
所以,这里的 faceView 为空
691
00:35:49,181 --> 00:35:52,215
Okay, here's where our variables are down here,
我们在这里可以看到这些变量
692
00:35:52,217 --> 00:35:55,952
clicking on "self", face view, sure enough nil.
点击 self,faceView 确实为nil
693
00:35:55,954 --> 00:35:58,822
So my pointer to my face view has not been set yet by iOS
在 iOS 中,这里的 faceView 还没有被设置
694
00:35:58,824 --> 00:36:01,725
and that's always gonna be the case when you're preparing so
在你准备 segue 时,这种情形经常发生
695
00:36:01,727 --> 00:36:05,529
be prepared for that happening okay. So what can we do about
所以,我们应该怎么做呢?
696
00:36:05,531 --> 00:36:10,066
it here? Okay well one thing we could say if face view does
我们可以设置如果 faceView 不为空
697
00:36:10,068 --> 00:36:14,738
not equal nil then we'll do all this updating UI stuff
我们来更新这些 UI
698
00:36:14,740 --> 00:36:17,874
right? K, by the way why is this happening,
顺便说下,为什么会这样呢?
699
00:36:17,876 --> 00:36:20,644
why are we on this line of code? If we look over here on
这行代码怎么了?
700
00:36:20,646 --> 00:36:22,946
the left it'll show us how us we got here.
如果我们看左边的展示给我们的信息
701
00:36:22,948 --> 00:36:24,915
Okay, so we're in this place right here,
然后再看对应的右边的代码
702
00:36:24,917 --> 00:36:28,919
okay, and let's see how we got here by clicking here. See,
我们点击一下看看这是什么
703
00:36:28,921 --> 00:36:33,156
we set our model which makes sense and it called up DUI, so
看,这里我们设置了我们的 model,然后更新了UI
704
00:36:33,158 --> 00:36:36,626
why are we setting our model, okay, let's go up more. Sure
为什么会走这一行呢,我们朝上点
705
00:36:36,628 --> 00:36:39,563
enough here we are preparing. Okay, setting our model.
因为在这里我们 prepare segue
706
00:36:39,565 --> 00:36:42,566
So this is right, preparing for segway, goes in here set
然后设置了model
707
00:36:42,568 --> 00:36:45,302
to our model. Wants to update UI, it's great.
设置了model 之后,更新UI
708
00:36:45,304 --> 00:36:48,271
Unfortunately our view is not been connected via outlet.
不幸的是,这里我们的view没有连接 outlet
709
00:36:48,273 --> 00:36:50,840
All right, so this is one way we could do it. By the way,
我们可以这样设置,避免他为空
710
00:36:50,842 --> 00:36:53,443
there is another trickier way to do it which is to
顺便说一下,还有另一个复杂的方法处理这个问题
711
00:36:53,445 --> 00:36:55,312
use question mark here.
在这里用问号
712
00:36:55,314 --> 00:36:57,981
You know that question mark works for optional chaining,
你们知道在等号的右手边处理
713
00:36:57,983 --> 00:36:59,683
right, on the right hand side of an equals?
用问号来处理可选链
714
00:36:59,685 --> 00:37:02,452
It also works on the left hand side. If you put a question
在等号的左边也可以处理
715
00:37:02,454 --> 00:37:05,155
mark there, that's and you can put as many of the question
你可以在等号的左边放置
716
00:37:05,157 --> 00:37:07,457
marks as you want on the left side of an equals. And
你希望放置的任意的问号
717
00:37:07,459 --> 00:37:10,760
if any of the things that you put a question mark are nil,
如果你在其后写问号的变量确实为空
718
00:37:10,762 --> 00:37:12,395
it just ignores the whole statement.
他会忽略掉整个状态
719
00:37:12,397 --> 00:37:15,799
Like it didn't even happen. Okay. So, that would be
就像没写这句话一样,这就是另一种方式了
720
00:37:15,801 --> 00:37:17,534
another way. But we would have to put a question mark on
但是我们需要在这里的每一个 faceView
721
00:37:17,536 --> 00:37:20,437
every single access of faceview in here. So,
后面都写上问号
722
00:37:20,439 --> 00:37:23,807
we might as well just check to see if it's not nil. Now, is
所以,还不如我在上面检查一下 faceView 是否为空
723
00:37:23,809 --> 00:37:28,011
this going to fix our problem? And the answer is, yes it is.
现在让我们看看这确实解决了我们的问题了没
724
00:37:28,013 --> 00:37:31,681
Because we do update UI when our model gets set, but
确实解决了,因为在 model 被重新设置后需要更新UI
725
00:37:31,683 --> 00:37:37,621
we also do update UI when this outlet gets set, remember?
我们需要在outlet被设置后,更新UI, 记得吗?
726
00:37:37,623 --> 00:37:40,523
So when the outlet later, after the prepare is done,
所以,当outlet被设置后,准备好了
727
00:37:40,525 --> 00:37:42,926
when the outlet comes along and gets set, boom,
outlet被设置了
728
00:37:42,928 --> 00:37:46,162
our UI will get updated with the model that we set during
我们的 UI 界面被更新为我们设置的 model
729
00:37:46,164 --> 00:37:49,065
prepare, okay? So it's going to work fine, okay?
这里就会正确运行,对吧?
730
00:37:49,067 --> 00:37:52,569
But this is part of the reason why when our outlets get set,
这就是为什么我们设置了 outlet
731
00:37:52,571 --> 00:37:54,838
we update UI and when our model gets set,
更新了UI 然后这时 model 才设置
732
00:37:54,840 --> 00:37:58,208
we update our UI because the order of them happening can be
我们更新 UI,因为这两项发生的顺序
733
00:37:58,210 --> 00:38:02,846
you know can vary. All right. Sound good alright.
有时候是不同的,看起来就应该这样
734
00:38:02,848 --> 00:38:04,347
So we fixed that, we checked this so
现在我们解决了这个问题,我们来检查下
735
00:38:04,349 --> 00:38:06,483
it's not gonna crash anymore, so let's run and
还会不会崩溃
736
00:38:06,485 --> 00:38:08,318
hopefully this is all working like a charm now.
我们运行一下,希望一切正常
737
00:38:08,320 --> 00:38:12,222
All right here we go ready? Angry,
现在看看对不对,Angry,
738
00:38:12,224 --> 00:38:16,192
happy, worried, mischievous.
happy, worried, mischievous.
739
00:38:16,194 --> 00:38:19,929
Okay. All right? So it's working great. Okay, and
运行起来很好
740
00:38:19,931 --> 00:38:26,002
let's try it this way. Slide out. Angry, happy, right?
现在我们这么做,侧滑,Angry, happy
741
00:38:26,338 --> 00:38:30,473
Working like a charm. Okay? Okay, all right.
看起来很好看,好的
742
00:38:30,475 --> 00:38:35,111
Now, what about iPhone? Okay, this is great on iPad.
现在,在 iPhone 上是什么样子呢?在 iPad 上运行良好
743
00:38:35,113 --> 00:38:38,248
Let's try running this on iPhone, and see what we get.
我们试着在 iPhone 上运行,看看是什么样
744
00:38:45,991 --> 00:38:48,525
All right, here's our thing. We got it here. Let's try
好的,我们的模拟器在这里
745
00:38:48,527 --> 00:38:53,496
happy. All right, we're happy! Actually, now we're not so
让我们尝试点击 happy,对的 happy
746
00:38:53,498 --> 00:38:57,100
happy because we're stuck. Okay, there's no way for
事实上,我们没有很开心,因为我们卡住了
747
00:38:57,102 --> 00:39:00,437
me to get back to choosing something else. Okay, I could
我没有办法返回去选择其他东西
748
00:39:00,439 --> 00:39:04,274
try rotating. No. I can try sliding out from the left.
我尝试着旋转,不行,我从左边滑动
749
00:39:04,276 --> 00:39:09,579
Nope, no. OK, split view does not split on an iPhone.
也不行,splitViewController 在 iPhone 上是不能分开的
750
00:39:09,581 --> 00:39:11,715
Only on the iPhone 6 Plus, which is
只有在 iPhone 6 Plus 上可以运行
751
00:39:11,717 --> 00:39:13,550
the only one big enough to really do it,
iPhone 6 Plus 是唯一一个够大的机型来展示 splitViewController
752
00:39:13,552 --> 00:39:16,419
okay? So we're stuck here. So this UI that we have in our
所以,我们卡住了,在故事板中这套 UI 不能正常的运行在
753
00:39:16,421 --> 00:39:19,122
storyboard is no good for iPhone, okay? Now we can still
iPhone里,我们现在仅仅可以
754
00:39:19,124 --> 00:39:21,491
make our guy blink, okay? But that's all we can do.
设置小人眨眼,但是我们只能做着一件事
755
00:39:21,493 --> 00:39:24,060
So unless we're happy just watching him blink all day
除非我们看着他光眨眼就满意了
756
00:39:24,062 --> 00:39:27,130
we need to fix this. So how are we gonna make this work on
我们需要解决这个问题,怎么让这个功能在
757
00:39:27,132 --> 00:39:30,800
iPhone? Okay. First we have to ask ourselves what would be
iPhone上也能正确运行呢?首先,我们需要思考
758
00:39:30,802 --> 00:39:32,902
an appropriate UI on iPhone. Okay,
在 iPhone 上的合适 UI
759
00:39:32,904 --> 00:39:35,538
and this is where navigation controller comes in.
这里就需要用到 navigationController 了
760
00:39:35,540 --> 00:39:37,507
Navigation controller would be great here because
NavigationController 用在这里会很合适
761
00:39:37,509 --> 00:39:40,777
you start the stack of cards with the angry mischievous
现在有一叠代表生气、淘气、担心的视图
762
00:39:40,779 --> 00:39:44,814
worried MVC. And then when you click the face it puts
当你另外点击了一个 face ,它将
763
00:39:44,816 --> 00:39:47,217
a new card on top which is the face. And then if
一个新的视图放在这个 face 之上
764
00:39:47,219 --> 00:39:49,786
you want a different one you just hit back, throw that card
然后,如果你想换一个表情,你只需返回
765
00:39:49,788 --> 00:39:52,756
away, and you are back to the angry mischievous worry choice
将之前的 face 扔掉,你就回到之前选择表情的视图了
766
00:39:52,758 --> 00:39:54,557
again, right? Okay, so it's perfect, okay,
对吗?这样很好
767
00:39:54,559 --> 00:39:57,193
navigation controller would be a perfect UI. So we need to
navigationController 是一个完美的 UI 控件
768
00:39:57,195 --> 00:40:00,830
put navigation controller into our story board, okay. So
所以我们需要将 navigationController 放进故事板中
769
00:40:00,832 --> 00:40:05,301
let's do that. Let's go back to our story board over here.
我们回到故事板
770
00:40:05,704 --> 00:40:06,703
There's a lot of space here.
把编辑空间弄大一点
771
00:40:06,705 --> 00:40:10,740
So where I'm gonna put this navigation controller? Well,
我应该把 navigationController 放哪儿呢?
772
00:40:10,742 --> 00:40:15,278
you really need one navigation controller around the root.
我们确实应该一个根控制器外放置一个 navigationController
773
00:40:15,280 --> 00:40:18,314
View controller. The base card, cards at the bottom.
这是最底下的 controller,在一叠 controller 下面
774
00:40:18,316 --> 00:40:21,251
Okay? So I can put a navigation controller here in
我可以像放置其他控件那样,从这里取出控件,然后放置
775
00:40:21,253 --> 00:40:24,154
the same way I put these other things by going down here. You
我可以这么放置 navigationController
776
00:40:24,156 --> 00:40:26,723
can see there's a navigation controller right here.
你可以看到,这个就是 navigationController
777
00:40:26,725 --> 00:40:28,758
I could drag it out. I could control, drag,
我可以把他拖出来,按住 control,然后 drag
778
00:40:28,760 --> 00:40:32,429
etc. There's actually a cooler way to do it, which is select
确实还有更酷的方法来做这件事
779
00:40:32,431 --> 00:40:36,232
your route MVC which is this guy. That's the card that's
先选中根控制器,就是这个
780
00:40:36,234 --> 00:40:39,068
at the bottom of the stack and go up to editor and
这个就是一叠视图中最下面的那个
781
00:40:39,070 --> 00:40:42,205
just like you did in bed and stack view, do embed and
然后选中 editor,就像把控件放进 stackView 中一样选中 embed in
782
00:40:42,207 --> 00:40:44,073
negotiation controller. You can also do it for
然后选择 navigationController,你也可以通过这种方式
783
00:40:44,075 --> 00:40:47,210
tab bar by the way, but do a navigation controller.
来添加 Tab Bar Controller,我们先选择 navigationController
784
00:40:47,212 --> 00:40:51,381
Now watch what happens here, boom! You put this
看看发生了什么,boom!
785
00:40:51,616 --> 00:40:55,318
inside a navigation control automatically wired it up.
这个控制器自动封装进了 navigationController
786
00:40:55,320 --> 00:40:58,855
Now whats kind of interesting about this is, it also made
特别有趣的是,它会自动将
787
00:40:58,857 --> 00:41:01,491
that navigation controller be the master of the split view.
navigationController 变为 splitView 中的 master
788
00:41:01,493 --> 00:41:03,927
In other words, it kind of wrapped the navigation
换句话说,它将根控制器包裹进 navigationController
789
00:41:03,929 --> 00:41:07,163
controller around the root con, the root view controller
而根控制器还保留了
790
00:41:07,165 --> 00:41:09,866
and kept that pointer to the master. So
之前作为 master 的指针
791
00:41:09,868 --> 00:41:14,204
now the master is actually a navigation controller,
所以,master 实际上就是一个 navigationController
792
00:41:14,206 --> 00:41:17,974
with this as it's root view controller. Still all these
这个控制器作为根控制器
793
00:41:17,976 --> 00:41:20,643
segways are still here. Okay, these segues are still,
而且还保留着这些 segues
794
00:41:20,645 --> 00:41:23,079
they didn't it didn't undo any of these segues. And
这些 segues 并没有被撤销
795
00:41:23,081 --> 00:41:28,485
this is still the detail of the split-view controller.
这个控制器还是原来split-view controller 中的 detail
796
00:41:28,687 --> 00:41:29,018
>> How did I put it in there?
797
00:41:29,020 --> 00:41:31,087
I selected this, which is my root-view-controller,
我选中根控制器
798
00:41:31,089 --> 00:41:36,159
and I went to editor, embed in, navigation controller.
然后选择 editor -> embed in -> navigationController
799
00:41:36,161 --> 00:41:40,663
Okay? So, now this is an interesting set up right here.
做这些挺有意思的
800
00:41:40,665 --> 00:41:43,099
Let's see if just fixed it, maybe it will just work.
现在,看看刚刚做的有没有起作用
801
00:41:43,101 --> 00:41:44,000
Because, you know, I hear,
因为,你知道的
802
00:41:44,002 --> 00:41:46,769
I got this, in a navigational controller, right now, so
我刚刚得到了一个 navigationController
803
00:41:46,771 --> 00:41:47,904
it should just work on a iPhone, right?
他会不会只在 iPhone 中生效呢?
804
00:41:47,906 --> 00:41:53,743
Let's try it. How many people think this going to work?
有多少人认为会起作用?
805
00:41:53,745 --> 00:41:56,246
Raise your hand if you think this is going to work.
如果你觉得会起作用,请举起手
806
00:41:56,248 --> 00:42:01,150
Nobody thinks it's gonna work. It is going to work! Gotcha.
没有人觉得这会起作用吗,事实上,会起作用的!
807
00:42:01,152 --> 00:42:03,186
Okay. So here I am in my navigation controller.
现在我看到的是 navigationController
808
00:42:03,188 --> 00:42:06,489
I'm going to go back. Here is my stuff. Let us try worried.
点击返回,这是选择表情的,点击担心的
809
00:42:06,491 --> 00:42:09,492
He looks worried. I am not worried though cuz I know it
确实看起来很担心,我倒不会再担心了
810
00:42:09,494 --> 00:42:14,364
is going to work. Okay so this worked fine, exactly what
因为我知道这起作用了
811
00:42:14,366 --> 00:42:18,001
we want on iPhone. Now my question to you is,
iPhone 上运行良好,我的问题来了
812
00:42:18,003 --> 00:42:21,604
is this still going to work on iPad? Okay.
在 iPad 上会起作用吗?
813
00:42:21,606 --> 00:42:25,308
Let's go look at that. Let's go back to iPad.
我们来看看在 iPad 上
814
00:42:25,310 --> 00:42:28,344
How many people think it's going to work on iPad still.
有多少人觉得在 iPad 上也会同样起作用呢?
815
00:42:28,346 --> 00:42:31,848
One. Two. Three. You guys are getting smart. Yes.
一,二,三,你们变聪明啦
816
00:42:31,850 --> 00:42:33,082
It's still going to work on iPad.
是的!在 iPad 上也会起作用
817
00:42:33,084 --> 00:42:36,419
Okay. It's pretty cool the way they made this work so you can
只在一个故事板中构建页面,但是能运行在
818
00:42:36,421 --> 00:42:39,789
build one storyboard that'll work fine on both platforms.
不同的平台上,确实是一件很酷的事情
819
00:42:39,791 --> 00:42:43,626
So here we go, this is iPad, we can turn it around,
现在,这是 iPad,转过来
820
00:42:43,628 --> 00:42:46,329
here it is, it is in a navigation controller,
页面在一个 navigationController 中
821
00:42:46,331 --> 00:42:48,264
you see? Which is actually kind of nice, cuz I can put
你们看到了没?这样确实很好
822
00:42:48,266 --> 00:42:53,403
a nice title in here for this MBC, okay. But when I click in
因为我可以在这上面写一个标题
823
00:42:53,405 --> 00:42:57,307
here, it's not going to slide a deck onto the deck of cards,
当我点击时,这个视图并不会侧滑出视线
824
00:42:57,309 --> 00:43:00,743
it's going to do the show detail, right, angry, happy,
它会正确显示 detail ,生气的、高兴的
825
00:43:00,745 --> 00:43:05,381
worried, mischievous. Okay? Everyone got that,
担心的、淘气的,对吗?大家明白
826
00:43:05,383 --> 00:43:08,651
how we do that? Now this kind of structure,
我们怎么做了吗?这是一种结构
827
00:43:08,653 --> 00:43:12,221
we have split view, navigation controller, and
我们先有 splitViewController, navigationController
828
00:43:12,223 --> 00:43:14,357
then the content for your master, and
然后是 master 的内容
829
00:43:14,359 --> 00:43:16,826
then the detail right here. Very common to build this,
然后是 detail 的内容,这是通常的做法
830
00:43:16,828 --> 00:43:18,928
because if you're building an app that works on both,
因为,如果你在做一个app,这是常规的做法
831
00:43:18,930 --> 00:43:22,298
this is what it's gonna look like basically. All right,
这是非常基本的操作
832
00:43:22,300 --> 00:43:22,498
so I'm gonna show you one
现在,我要向你展示
833
00:43:22,500 --> 00:43:26,402
last thing here of interest, which is these titles, okay.
这里最后一个有趣的点,就是标题
834
00:43:26,404 --> 00:43:31,874
So we have this little space for a title up here, so
我们这里给标题留了一个小空间
835
00:43:31,876 --> 00:43:35,445
I'd like to put a title here, okay. And how do I put a title
我要在这儿放置标题,我怎么在这儿放标题呢?
836
00:43:35,447 --> 00:43:37,647
up here? Again, remember that's object oriented, okay.
记住,这一切都是面向对象的
837
00:43:37,649 --> 00:43:41,250
That title comes from whatever MVC happens to be showing in
这些标题会在 navigationController
838
00:43:41,252 --> 00:43:43,019
this navigation controller at the time. So
展现MVC 的时候出现
839
00:43:43,021 --> 00:43:46,289
I go to the storyboard, to that MVC, which is this one,
我打开故事板,选择这个 MVC
840
00:43:46,291 --> 00:43:48,591
and could just double click on it, okay? So
双击一下,对吗?
841
00:43:48,593 --> 00:43:53,296
we'll call this one Emotions, okay. So now if I run,
标题叫做 Emotions ,如果我运行下
842
00:43:57,535 --> 00:43:58,368
I get a nice title up here,
我得到一个很好地标题 Emotions
843
00:43:58,370 --> 00:44:03,840
Emotions. What if I wanted a title over here? Okay,
如果这里也想有个标题呢?
844
00:44:03,842 --> 00:44:07,844
how would I do it if I wanted a title over here?
如果这个位置也想有标题,我应该怎么做呢?
845
00:44:08,079 --> 00:44:09,512
Well if you look on the iPhone,
你看在 iPhone 中
846
00:44:09,514 --> 00:44:13,650
okay if we go back and oops, run this on iPhone.
返回,要运行在 iPhone中
847
00:44:14,853 --> 00:44:19,122
Since both MVCs are in that navigation controller,
因为这些 MVC 都在navigation controller
848
00:44:19,124 --> 00:44:24,727
they both have titles Okay, so if I go here, here's emotions,
所以他们都有标题,这里有标题emotions
849
00:44:24,729 --> 00:44:28,865
angry. Look, there's room for a title up here. But
这里也有标题
850
00:44:28,867 --> 00:44:34,170
back on the iPad, that's not the case because the detail,
但是,回到 iPad 上,这里就没有标题了
851
00:44:34,172 --> 00:44:37,573
when you replace the detail it doesn't actually put it
因为,在 iPad上,当你换到 detail 页面时
852
00:44:37,575 --> 00:44:40,543
inside the same navigation controller that the master is
detail 并没有被包裹进 master 在的那个 navigationController
853
00:44:40,545 --> 00:44:44,247
in. So the master is in a navigation controller, but
所以,master 在 navigationController 中
854
00:44:44,249 --> 00:44:47,417
this is not. So if we wanted a title here,
而 detail 并不在,所以如果我们想要设置标题
855
00:44:47,419 --> 00:44:49,752
we have to put this guy in a navigation controller.
我们就需要把 detail 放进 navigationController里
856
00:44:49,754 --> 00:44:52,488
Even though we're not going to use the navigation controller
如果我们就是想用 navigationController 来展示个标题
857
00:44:52,490 --> 00:44:55,858
for anything except for the title on the split view case.
并不做其他事情,在 splitViewController 里
858
00:44:55,860 --> 00:44:56,392
We would still have to do it.
我们还得用 navigationController 来做
859
00:44:56,394 --> 00:45:01,264
And in fact, iOS makes it so that just works, okay?
事实上, iOS 就是这么设计的
860
00:45:01,266 --> 00:45:04,634
So I'm going to go here. Down to this guy. Okay and
所以,点击这个 controller
861
00:45:04,636 --> 00:45:09,706
I'm going to embed him in a navigation controller okay.
把他包裹进一个 navigationController
862
00:45:09,708 --> 00:45:12,475
So just have this, I have the face selected,
就这么做,点击一个表情
863
00:45:12,477 --> 00:45:17,280
embed puts him in there, okay. Now this
把他放进了一个 navigationController 里
864
00:45:17,282 --> 00:45:20,116
almost is going to work, okay, if we look at what this looks
如果我们运行看看这样设置是什么样的
865
00:45:20,118 --> 00:45:23,119
like. Okay? There's a little bit of an issue here because
可能是运行正常,可能有一些问题
866
00:45:23,121 --> 00:45:26,889
these segues are now going to this navigation controller.
因为这些 segues 现在开始指向 navigationController
867
00:45:26,891 --> 00:45:32,028
But let's take a look at what it looks like anyway. Okay,
我们还是看一下到底运行的怎么样吧
868
00:45:32,030 --> 00:45:34,363
on iPad look, they're both in navigation controller.
在 iPad 上,确实都在 navigation controller 里
869
00:45:34,365 --> 00:45:37,633
So I've room for a title here now, okay? And if I click,
我挪点空间来看看
870
00:45:37,635 --> 00:45:41,838
it doesn't work anymore. Okay, so I broke it by putting this
如果我点击了,这个表情并没有真正展现
871
00:45:41,840 --> 00:45:44,941
in a navigation controller. Now why, and by the way,
当我把它放置在 navigation controller 里了,反而破坏了这个表情
872
00:45:44,943 --> 00:45:47,944
does it work on iPhone? Yes, still works on iPhone. So
顺便看一下,在 iPhone 中正常吗?看起来很正常
873
00:45:47,946 --> 00:45:51,647
it did not break it on iPhone because iOS is smart
它没有破坏在 iPhone 上的显示,因为 iOS系统够聪明
874
00:45:51,649 --> 00:45:54,951
enough to know this is already in a navigation controller, so
它知道这个控制器已经在 navigation controller 里了
875
00:45:54,953 --> 00:45:57,453
I'm just gonna ignore this navigation controller and
所以在展示它时,会忽略它自己的那个 navigation controller了
876
00:45:57,455 --> 00:46:01,691
put this in here. See? So this looks the same, no change,
看到了吗?这看起来没啥不一样的
877
00:46:01,693 --> 00:46:02,825
still in a navigation controller.
仍然在 navigation controller 中
878
00:46:02,827 --> 00:46:05,361
iOS didn't put a navigation controller inside a navigation
iOS 不会将一个 navigation controller 放在另一个 navigation controller 里
879
00:46:05,363 --> 00:46:08,498
controller, in fact iOS never puts a navigation controller
事实上,iOS 永远都不会将一个 navigation controller
880
00:46:08,500 --> 00:46:10,299
inside another one. Okay, if it sees and
放在另一个 navigation controller 里
881
00:46:10,301 --> 00:46:12,235
encounters another one, it just ignores that one,
如果它发现它包裹在另一个 navigation controller 里,它会自动忽略它
882
00:46:12,237 --> 00:46:14,170
okay. It's like you're either in one or you're not,
你可以把它放进一个 navigation controller 之中或者不放
883
00:46:14,172 --> 00:46:15,972
you can't be in two at the same time. So
但是你不能同时包裹两个 navigation controller
884
00:46:15,974 --> 00:46:20,042
it worked great on the iPhone. So why did it break though?
所以,在 iPhone 中运行良好,但是,为什么崩溃了呢?
885
00:46:20,044 --> 00:46:22,145
Sorry, it worked great visually but it still,
它看起来运行良好,但是
886
00:46:22,147 --> 00:46:24,413
I didn't show it there but it didn't work to click.
在点击时,运行的并不良好
887
00:46:24,415 --> 00:46:26,549
Okay, just like in the iPad it didn't work to click. And
跟在 iPad 里一样,点击后效果不对
888
00:46:26,551 --> 00:46:30,019
why didn't it work to click? Well, we have to understand or
为什么点击的效果不对呢,这里我们必须了解或者
889
00:46:30,021 --> 00:46:32,955
prepare a little bit better there, okay?
准备了这点知识
890
00:46:32,957 --> 00:46:35,525
And in fact, let's take the time to do this.
事实上,会耗费我们一点时间来说明这个问题
891
00:46:35,527 --> 00:46:37,160
I'm gonna go in the debugger and show you this.
我将在 debugger 模式下说明这个问题
892
00:46:37,162 --> 00:46:40,797
So I'm gonna set a break point right here, okay?
我需要在这里打个断点
893
00:46:40,799 --> 00:46:43,199
In prepareForSegue because prepareForSegue is not
在 prepareForSegue 这个方法里打断点
894
00:46:43,201 --> 00:46:48,204
properly preparing that thing. So let's see why it's failing.
因为它没有很好地为这个 segue 做准备,我们来看看为什么失败了
895
00:46:53,044 --> 00:46:56,712
All right, so here we go. I'm going to hit happy. And
我们准备好了,运行,点击 happy
896
00:46:56,714 --> 00:46:59,015
it's going to break, okay, at this prepare.
到断点这里了,在 prepareForSegue 里面
897
00:46:59,017 --> 00:47:02,285
So it's trying to prepare that thing to be happy. And
所以,它准备为展示 happy 做一些事
898
00:47:02,287 --> 00:47:06,022
what is, let's look at this destination VC right here.
在这里看看 destinationVC
899
00:47:06,024 --> 00:47:11,794
Look what its type is. It's a UN navigation controller.
看看它的类型是什么,这是个 UINavigationController
900
00:47:11,796 --> 00:47:13,896
And that kind of makes sense, right?
这说明了一些问题,是吗?
901
00:47:13,898 --> 00:47:15,531
Because if you look at our storyboard,
因为在 storyboard 中
902
00:47:15,533 --> 00:47:19,669
these segues are going down to this navigation controller.
这些 segue 指向这些 navigation controller了
903
00:47:19,671 --> 00:47:21,938
Okay? So that's just a little bit of a twist.
所以这看起来有点纠结
904
00:47:21,940 --> 00:47:25,741
A little bit tricky. And all we have to do is look at this
有点复杂,我们需要判断这个 destinationvc
905
00:47:25,743 --> 00:47:28,811
destination VC and if it's a navigation controller,
如果他是一个 navigation controller
906
00:47:28,813 --> 00:47:32,381
then let's get moving inside of it. Okay. And
我们需要获得它里面包裹的东西
907
00:47:32,383 --> 00:47:36,519
work on that. Okay. So I'm just going to say here,
让我们这样做吧,我得在这里说明
908
00:47:36,521 --> 00:47:39,755
if I can let Navcon, which is a local variable,
写一个局部变量,navcon
909
00:47:39,757 --> 00:47:44,393
equal the destination VC as a UI navigation controller. So,
如果将 destinationvc 作为一个 UINavigationController 赋值给 navcon
910
00:47:44,395 --> 00:47:49,165
if the destination happens to be UINavigationController,
如果 destinationvc 确实就是一个 UINavigationController
911
00:47:49,167 --> 00:47:54,270
then I'm gonna let I'm just gonna set the destination VC
我将这个 destinationvc 等于这个
912
00:47:54,272 --> 00:47:58,307
equal to the navcon.visibleViewController.
navcon 的 visibleViewController
913
00:47:58,309 --> 00:47:59,942
Which is optional, so I'm gonna,
这是一个可选值,我打算
914
00:47:59,944 --> 00:48:01,277
actually I won't even unwrap it.
事实上,我不是要将它解包
915
00:48:01,279 --> 00:48:03,479
I'm gonna say if it's visible view control and
而是,如果它就是一个 visibleViewController
916
00:48:03,481 --> 00:48:06,048
if the navigation control doesn't have a visible view
或者这个 navigationController 没有 visibleViewController
917
00:48:06,050 --> 00:48:09,252
control then we'll just leave it to be the destination view
它会直接赋值为自身
918
00:48:09,254 --> 00:48:13,823
controller. This'll have to be bar. Okay? So
这样做更保险,是吗?
919
00:48:13,825 --> 00:48:16,959
here all I'm doing is, I'm just saying if I'm preparing
我要写的就是这些,如果在准备这个 segue 时
920
00:48:16,961 --> 00:48:19,695
to segue to something and it's a navigation controller,
它是一个 navigationController
921
00:48:19,697 --> 00:48:22,365
then instead look inside the navigation controller and
我会获取到 navigationController 里面的东西
922
00:48:22,367 --> 00:48:26,135
segue to that instead. Okay? Prepare that instead. Okay?
segue 会指向里面的 controller ,为展示这个做准备,是吗?
923
00:48:26,137 --> 00:48:29,038
Everyone understand what I'm doing there with these, this
大家都明白我在这里写了什么吗?
924
00:48:29,040 --> 00:48:37,647
little Three lines of code? >> [INAUDIBLE]
明白我写的这三行代码吗?
925
00:48:37,649 --> 00:48:38,447
>> Yes, so you're
对,你说
926
00:48:38,449 --> 00:48:39,048
saying, could I go
我能不能
927
00:48:39,050 --> 00:48:40,049
back here in the storyboard and
在 storyboard 做我刚刚写下的操作
928
00:48:40,051 --> 00:48:43,452
have all these segue things go down over this way instead?
将这些 segue 直接指向展示的 controller?
929
00:48:43,454 --> 00:48:44,086
You wouldn't actually want that,
事实上,你不会想这么做的
930
00:48:44,088 --> 00:48:47,523
because when the segue creates the MVC that it's gonna show,
因为当 segue 创造的这些 MVC 准备去展示时
931
00:48:47,525 --> 00:48:49,692
you want it to create a navigation controller. So
你希望他在一个 navigationController 里
932
00:48:49,694 --> 00:48:51,827
you actually want it to go into this navigation.
所以你希望在一个 navigationController 里看到那个 controller
933
00:48:51,829 --> 00:48:54,530
It's just that, when it comes to preparing it, you don't
这样当你准备展示它时
934
00:48:54,532 --> 00:48:56,365
really want to prepare the navigation controller,
你不是想为展示那个 navigationController 做准备
935
00:48:56,367 --> 00:48:58,567
you wanna prepare the thing inside. So
而是为 navigationController 里面的东西做准备
936
00:48:58,569 --> 00:49:02,104
that's why we're in this code just looking inside
这就是为什么我们在这些代码里
937
00:49:02,106 --> 00:49:04,206
the navigation controller to make the destination
将 destinationvc 赋值为 navigationController 里包裹的东西
938
00:49:04,208 --> 00:49:07,576
that we're preparing be the thing inside. Okay?
我们为里面的东西做准备,对吗?
939
00:49:07,578 --> 00:49:10,613
So little trickiness. You'll often have this little code.
有一点点难,你会经常写这种代码
940
00:49:10,615 --> 00:49:12,381
I'll actually show you later in the quarter kind of
事实上,一会儿我会用
941
00:49:12,383 --> 00:49:16,585
a little more sophisticated way to put this in there.
一个更常用的方法来设置这个
942
00:49:16,587 --> 00:49:21,824
You can put it there with one syntactical element,
你可以用一个更语义性的元素
943
00:49:21,826 --> 00:49:25,628
it's possible, but this I want to leave explicitly so
但是,我想更明确的设置这个
944
00:49:25,630 --> 00:49:29,065
you understand what's going on here. So now when we run it's
你也会明白这里做了什么,现在,我们运行一下
945
00:49:29,067 --> 00:49:32,101
going to work because when we segue to something
看看是否工作,因为我们在这里将 segue 指向了
946
00:49:32,103 --> 00:49:35,604
that's navigation controls, it can look inside. See, angry,
navigationController 里面的东西
947
00:49:35,606 --> 00:49:39,442
happy, worried, mischievous. And the last thing I wanna do
看看, angry、happy、worried、mischievous 都正确的展示了
948
00:49:39,444 --> 00:49:43,346
is set this title right here. Now this title was fixed.
我需要做的最后一件事就是设置 controller 的标题
949
00:49:43,348 --> 00:49:45,247
This is always the emotions MVC.
这需要在展示表情的 MVC 里设置标题
950
00:49:45,249 --> 00:49:49,285
But this MVC, its title kinda depends on what it's showing
但是这个 MVC,它的标题像是需要在展示它的地方来设置
951
00:49:49,287 --> 00:49:52,054
here, doesn't it? Alright like if we are showing angry it
是这样吗?就像是,如果我们要展示 angry
952
00:49:52,056 --> 00:49:53,556
would be nice if this title would be angry.
它的标题就是 angry,这样应该很不错
953
00:49:53,558 --> 00:49:55,958
It would be nice if this title were worried.
展示 worried 时,它的标题就是 worried
954
00:49:55,960 --> 00:49:59,662
Okay? So where can we get the appropriate title here?
所以,我们得在哪里获得这个对应的标题呢?
955
00:49:59,664 --> 00:50:02,098
we'll we can get it form the button that's asking us
我们可以从引发 segue 的按钮那里获得标题
956
00:50:02,100 --> 00:50:05,935
to segue. Right? If this angry button asks us to segue then
对吗?如果是这个 angry 按钮让我们来展示表情
957
00:50:05,937 --> 00:50:10,773
let's set this title to be the title of the angry button.
我们就将标题设置为这个 angry 按钮的标题
958
00:50:10,775 --> 00:50:15,911
Right? So, looking back here, in our "prepareForSegue".
对吗?回到 prepareForSegue 这里
959
00:50:15,913 --> 00:50:20,216
Is that title of the angry button available to us here?
我们可以用 angry 按钮的标题吗?
960
00:50:20,218 --> 00:50:25,087
Where? Where can I get it? From "sender", yeah, exactly.
在哪里,在哪里我能获得这个标题呢? sender!
961
00:50:25,089 --> 00:50:26,922
Okay. You guys are getting it. That's really good.
对的,你们已经明白这一点了,很好
962
00:50:26,924 --> 00:50:29,859
Sender is the button that's causing the segue to happen.
Sender 就是那个引发 segue 的按钮
963
00:50:29,861 --> 00:50:32,661
That's gonna be the angry button. So, it's any object.
这应该就是那个 angry 按钮,这可以是任何对象
964
00:50:32,663 --> 00:50:35,197
So I'm gonna have to cast it to be a button. But that's no
所以,我想让它转型为一个 button,这没什么问题
965
00:50:35,199 --> 00:50:38,734
problem. I'm just gonna say if I can let the sending button
我要写如果我让 sending button 作为一个 UIButton
966
00:50:38,736 --> 00:50:41,937
equal the sender as a UI button, okay? And
等于 sender,对吗?
967
00:50:41,939 --> 00:50:45,307
if not, if I'm segueing from code or something here,
如果它不能转为一个UIButton,或者我是用代码
968
00:50:45,309 --> 00:50:45,741
then this is not gonna work.
或者其他方法引发的 segue ,这样就不能正常工作了
969
00:50:45,743 --> 00:50:48,677
But so what. Okay? We just won't get the title we want.
我们就不能给 Face View Controller
970
00:50:48,679 --> 00:50:52,748
On our Face View Controller. But if it is then I can just
获取到我们想要的标题,如果能转为 UIButton
971
00:50:52,750 --> 00:50:58,087
set the Face View Controllers navigation item. Does everyone
我可以设置给 Face View Controller 设置它的 navigation item
972
00:50:58,089 --> 00:51:00,656
remember what navigation item is? It's kind of just a bundle
大家都记得 navigation item 吗?他就像一捆东西
973
00:51:00,658 --> 00:51:04,293
of things like a little bag of goodies that the navigation
像是 navigation controller 在展示它里面的 MVC时
974
00:51:04,295 --> 00:51:08,197
controller looks inside of when that MVC is showing.
navigationController 有的一袋东西
975
00:51:08,199 --> 00:51:13,602
To get information like the buttons or the title, okay.
为了得到这个 button 的属性,比如 title
976
00:51:13,604 --> 00:51:19,208
Equals the sending button's current title,
等于这个触发 button 的当前标题
977
00:51:19,210 --> 00:51:22,912
okay. So this little bag of goodies, navigation item,
我们在文档中看一下
978
00:51:22,914 --> 00:51:25,281
here let's look at it in the documentation, okay.
这一袋 navigationItem 里的东西
979
00:51:25,283 --> 00:51:28,250
I'm going to go here. The I View Controller,
我要打开这个,这是 ViewController
980
00:51:28,252 --> 00:51:29,218
it's like a navigation item.
这是那些 navigationItem
981
00:51:29,220 --> 00:51:31,754
You can see it house things like the title to show
你可以在目录里看到 title
982
00:51:31,756 --> 00:51:35,891
when this MVC is showing in the navigation controller.
当 MVC 在 navigationController 中展示时
983
00:51:35,893 --> 00:51:39,962
Back bar button item to use instead of the default one.
Back bar button item 就是默认的
984
00:51:39,964 --> 00:51:42,765
Left bar button items and right bar button items.
Left bar button item 和 right bar button item
985
00:51:42,767 --> 00:51:45,101
You can put you know things on the top, left and right.
你可以将你获得的东西放在上面,左面或者右面
986
00:51:45,103 --> 00:51:46,902
That's what's in this little bag of goodies. And
这就是这一袋东西里面的
987
00:51:46,904 --> 00:51:50,172
this bag of goodie is only looked in when you are on top
这一袋东西只会在
988
00:51:50,174 --> 00:51:54,110
the of a navigation controller stack.
navigationController 堆中最上面展示
989
00:51:54,112 --> 00:51:57,279
All right, so let's see if that works.
好的,让我们看看是否有用
990
00:52:00,585 --> 00:52:02,384
All right, here we go. Let's try angry,
运行一下,我们来点击 angry
991
00:52:02,386 --> 00:52:07,022
there it is, angry, happy, worried, mischievous. Okay,
这里的标题有 angry, happy, worried, mischievous
992
00:52:07,024 --> 00:52:10,626
and let's make sure that it's working on iPhone.
让我确认在 iPhone 中是否运行良好
993
00:52:19,237 --> 00:52:21,837
All right, let's go back here. Angry. Yes.
好的,让我们回到这里,angry
994
00:52:21,839 --> 00:52:23,539
Look he even got the title right. Happy.
看起来我们正确的设置了标题
995
00:52:23,541 --> 00:52:27,743
That's definitely happy. Worried. Okay?
happy,这确确实实是 happy,worried
996
00:52:27,745 --> 00:52:32,381
All right, everyone got that? So this is how we can build
好的,大家都明白了吗?这就是怎样
997
00:52:32,383 --> 00:52:36,085
these nice apps that work on both platforms, okay? They
做一个 app 可以适配各个平台,对吗?
998
00:52:36,087 --> 00:52:38,254
work in split views. They work in navigation controllers.
这是在 split view 里的,它们都在 navigationController 里面
999
00:52:38,256 --> 00:52:41,157
Now if you were building an iPhone only app, you could do
如果你只是做一个 iPhone 的 app
1000
00:52:41,159 --> 00:52:44,460
this exact same thing but you wouldn't need the split view.
你可以不用 splitView 就完成同样的事情
1001
00:52:44,462 --> 00:52:45,327
You could just take this view and
1002
00:52:45,329 --> 00:52:48,264
move your start Your little starting arrow right here.
1003
00:52:48,266 --> 00:52:50,633
You just move right up here to this navigation controller,
1004
00:52:50,635 --> 00:52:53,402
and you wouldn't need this navigation controller either,
1005
00:52:53,404 --> 00:52:57,139
okay, because you don't have this issue where this whole
1006
00:52:57,141 --> 00:53:00,176
thing is in a separate space like it is on iPad and iPhone.
1007
00:53:00,178 --> 00:53:02,845
They'd all be inside the navigation controller.
1008
00:53:03,347 --> 00:53:07,082
Okay? Now not surprisingly for you're homework I'm going to
1009
00:53:07,084 --> 00:53:09,285
ask you to do pretty much exactly this. Okay?
1010
00:53:09,287 --> 00:53:10,186
You're going to have a calculator.
1011
00:53:10,188 --> 00:53:12,121
You're going to have a calculator graph,
1012
00:53:12,123 --> 00:53:12,221
have to write the code for. So you're going to have two MVC's
1013
00:53:12,223 --> 00:53:14,089
which you're going to
1014
00:53:14,091 --> 00:53:16,358
and you're going to have to put it in the situation like
1015
00:53:16,360 --> 00:53:18,594
this that works on both iPad and iPhone. Okay?
1016
00:53:18,596 --> 00:53:23,899
And now you know how to do it. All right back to the slides.
1017
00:53:29,273 --> 00:53:31,173
Okay the View Controller Lifecycle.
1018
00:53:31,175 --> 00:53:32,641
So I've been mentioning this and we delete
1019
00:53:32,643 --> 00:53:35,311
those methods in there for the View Controller Lifecycle. So
1020
00:53:35,313 --> 00:53:38,547
now let's talk about what this View Controller Lifecycle is.
1021
00:53:38,549 --> 00:53:41,450
So View Controller Lifecycle It's really just a set of
1022
00:53:41,452 --> 00:53:44,620
methods that gets sent to your view controller as your
1023
00:53:44,622 --> 00:53:47,489
view controller goes through it's lifetime. Okay,
1024
00:53:47,491 --> 00:53:50,626
meaning it's lifetime is it gets created, it does things,
1025
00:53:50,628 --> 00:53:53,429
it dies. Okay, and as it goes through that life,
1026
00:53:53,431 --> 00:53:57,299
you get sent these nice messages. Okay? So the start
1027
00:53:57,301 --> 00:54:03,272
of the lifecycle is obviously the creation of your MVC. And
1028
00:54:04,508 --> 00:54:08,744
MVCs are almost always created out of storyboards I'd say 99%
1029
00:54:08,746 --> 00:54:11,280
of the time okay? That's how you've seen us do it and
1030
00:54:11,282 --> 00:54:13,616
that's how it's done, okay? It can be done in code but
1031
00:54:13,618 --> 00:54:18,120
I'm not even gonna show you how to do that in this course,
1032
00:54:18,122 --> 00:54:18,921
It's rarely done, okay?
1033
00:54:18,923 --> 00:54:21,490
The great thing about putting in a storyboard is that people
1034
00:54:21,492 --> 00:54:24,994
looking at your code can see what you intend visually,
1035
00:54:24,996 --> 00:54:25,995
okay? In terms of your MVC and
1036
00:54:25,997 --> 00:54:27,963
their layout and their segways to each other. So
1037
00:54:27,965 --> 00:54:31,467
that's why we try to do stuff in the storyboard. So 99% of
1038
00:54:31,469 --> 00:54:35,337
the time these MVCs are being created out of the storyboard.
1039
00:54:35,339 --> 00:54:38,574
After they're created they go through the following steps,
1040
00:54:38,576 --> 00:54:41,644
one they get prepared okay,
1041
00:54:41,646 --> 00:54:44,913
they get segwayed too they get prepared so that first.
1042
00:54:44,915 --> 00:54:48,317
Then outlet setting happens that's why we had that crusher
1043
00:54:48,319 --> 00:54:51,086
because outlet setting happens after prepare.
1044
00:54:51,088 --> 00:54:54,056
Then they appear and disappear. Okay,
1045
00:54:54,058 --> 00:54:58,627
now this can happen over and over. This thing is gonna
1046
00:54:58,629 --> 00:55:01,030
appear on screen, go away, appear on screen, go away. For
1047
00:55:01,032 --> 00:55:05,734
example, when we had the iPad version of our demo here, and
1048
00:55:05,736 --> 00:55:08,504
I would pull out the master, and then I would hide it, and
1049
00:55:08,506 --> 00:55:10,105
I'd pull out the master, and then I would hide it.
1050
00:55:10,107 --> 00:55:13,409
Okay, it was appearing and disappearing. Alright?
1051
00:55:13,411 --> 00:55:17,179
Then as this is all happening, geometry changes can happen to
1052
00:55:17,181 --> 00:55:20,015
this thing, mostly due to rotation from
1053
00:55:20,017 --> 00:55:23,886
portrait to landscape, changes the geometry. And then,
1054
00:55:23,888 --> 00:55:26,522
very rarely you can get into a low memory situation
1055
00:55:26,524 --> 00:55:29,358
where your view controller may be asked to participate in
1056
00:55:29,360 --> 00:55:31,660
freeing up some memory, super rare.
1057
00:55:31,662 --> 00:55:36,065
Alright So let's talk about all these methods, okay?
1058
00:55:36,067 --> 00:55:38,567
After instantiation and preparation and
1059
00:55:38,569 --> 00:55:42,071
outlet-setting, you get a very important method sent to you
1060
00:55:42,073 --> 00:55:45,908
called viewDidLoad. Okay Load refers to loading up your
1061
00:55:45,910 --> 00:55:49,478
outlets, okay? So viewDidLoad is an awesome place to put
1062
00:55:49,480 --> 00:55:51,980
initialization code in your I view controller.
1063
00:55:51,982 --> 00:55:57,319
It's why you almost never have to override View controller's
1064
00:55:57,321 --> 00:56:00,856
init method. Okay it has a very strange init method.
1065
00:56:00,858 --> 00:56:02,624
You don't even wanna think about it's so weird.
1066
00:56:02,626 --> 00:56:04,960
Okay? But luckily you never need to override because you
1067
00:56:04,962 --> 00:56:08,297
got viewDidLoad. Okay? You put all your initialization code
1068
00:56:08,299 --> 00:56:11,767
almost all into viewDidLoad, okay,
1069
00:56:11,769 --> 00:56:13,469
it's kind of like your init for view controllers,
1070
00:56:13,471 --> 00:56:17,039
it's awesome. And it's great there because you're prepared,
1071
00:56:17,041 --> 00:56:19,341
and your outlets are set. So it's like, whoa,
1072
00:56:19,343 --> 00:56:21,643
you are ready to really get initialized, okay, and
1073
00:56:21,645 --> 00:56:24,146
of course all your instance variables are initialize,
1074
00:56:24,148 --> 00:56:27,716
or you wouldn't exist, okay? One really good thing to do
1075
00:56:27,718 --> 00:56:32,087
here is to update your UI, so you notice we had update UI in
1076
00:56:32,089 --> 00:56:34,790
the outlet setter property observer for
1077
00:56:34,792 --> 00:56:37,826
face view, okay? More likely you'd put update UI
1078
00:56:37,828 --> 00:56:42,464
in your viewDidLoad, okay. Cuz you know by viewDidLoad,
1079
00:56:42,466 --> 00:56:44,166
everything is ready to be updated.
1080
00:56:44,168 --> 00:56:46,702
Okay, now you're still gonna want it in your models,
1081
00:56:46,704 --> 00:56:49,838
didSet, because someone might change your model after
1082
00:56:49,840 --> 00:56:50,672
you've been loaded, okay?
1083
00:56:50,674 --> 00:56:53,642
But no one's going to change your outlets after you've been
1084
00:56:53,644 --> 00:56:56,645
loaded, your outlets get set up and now you own them, okay?
1085
00:56:56,647 --> 00:56:59,615
So they're, no one's gonna set it to point to something else,
1086
00:56:59,617 --> 00:57:03,018
okay? Now one thing to be careful about viewDidLoad,
1087
00:57:03,020 --> 00:57:06,989
though, is geometry, okay? The geometry of your view is
1088
00:57:06,991 --> 00:57:11,693
still probably the square, okay, of your storyboard. So
1089
00:57:11,695 --> 00:57:14,296
it's useless to do any calculations in viewDidLoad,
1090
00:57:14,298 --> 00:57:16,098
that have anything to do with your geometry.
1091
00:57:16,100 --> 00:57:17,566
If you're looking at your bounds and trying to make
1092
00:57:17,568 --> 00:57:20,903
a calculation is a waste of time, because it's a square.
1093
00:57:20,905 --> 00:57:23,539
It's not, it hasn't been updated to the portrait or
1094
00:57:23,541 --> 00:57:26,008
landscape of the device you're running on. Okay,
1095
00:57:26,010 --> 00:57:28,610
so this is the one think you don't do in viewDidLoad.
1096
00:57:28,612 --> 00:57:30,212
The other thing I wouldn't do in viewDidLoad is
1097
00:57:30,214 --> 00:57:33,882
anything that's going to kick off something expensive, okay?
1098
00:57:33,884 --> 00:57:36,919
Computing expensive or network intensive, whatever.
1099
00:57:36,921 --> 00:57:38,454
There's a better place to do that.
1100
00:57:38,456 --> 00:57:40,656
That's because viewDidLoad, kay,
1101
00:57:40,658 --> 00:57:42,724
your view controller got created. There's,
1102
00:57:42,726 --> 00:57:45,294
it's not a 100% guarantee it's gonna appear onscreen.
1103
00:57:45,296 --> 00:57:47,796
It might or might not. Depends on what the user touches in
1104
00:57:47,798 --> 00:57:51,066
your UI. So you wouldn't wanna kick off something expensive
1105
00:57:51,068 --> 00:57:53,702
until you know you're gonna be onscreen. And that brings us
1106
00:57:53,704 --> 00:57:57,105
to viewWillAppear, kay? So, viewWillAppear is called right
1107
00:57:57,107 --> 00:58:00,108
before you get put onscreen. So you are sure when this is
1108
00:58:00,110 --> 00:58:02,010
called that you're going to be put onscreen.
1109
00:58:02,012 --> 00:58:04,780
This is a great place to start something expensive.
1110
00:58:04,782 --> 00:58:08,450
Now you're gonna learn starting next week that iOS
1111
00:58:08,452 --> 00:58:12,287
apps, you wanna build them multi-threaded, okay?
1112
00:58:12,289 --> 00:58:14,990
Multi-threaded means, you're gonna have multiple things
1113
00:58:14,992 --> 00:58:17,626
going on at the same time in your app. They'll be sharing
1114
00:58:17,628 --> 00:58:20,596
the processor, time sharing the processor basically,
1115
00:58:20,598 --> 00:58:22,030
unless you have multi-core I guess,
1116
00:58:22,032 --> 00:58:25,300
which I guess that's formed is a basically running multiple
1117
00:58:25,302 --> 00:58:28,370
things at the same time. Your UIs gonna be running in one
1118
00:58:28,372 --> 00:58:30,772
thread at a very high priority and then other things
1119
00:58:30,774 --> 00:58:33,542
like accessing the network or things that block, things that
1120
00:58:33,544 --> 00:58:35,677
take a long time, we are gonna be running another thread.
1121
00:58:35,679 --> 00:58:37,579
And we are gonna be running simultaneously,
1122
00:58:37,581 --> 00:58:40,249
because we never want our UI to be blocked. Okay,
1123
00:58:40,251 --> 00:58:42,951
when user touches on something it should immediately respond.
1124
00:58:42,953 --> 00:58:45,354
I don't care what you're doing. Okay, you don't wanna
1125
00:58:45,356 --> 00:58:47,589
touch on something and it does nothing, because you're out
1126
00:58:47,591 --> 00:58:49,124
trying to fetch something from the network and
1127
00:58:49,126 --> 00:58:50,726
the network is slow. That would be a nightmare,
1128
00:58:50,728 --> 00:58:52,961
okay? So viewWillAppear is we would, you
1129
00:58:52,963 --> 00:58:56,064
would fire off another thread to go do something expensive.
1130
00:58:56,066 --> 00:59:00,702
kay, now your view is going to instantly appear on
1131
00:59:00,704 --> 00:59:03,705
screen without that data that you tried to get,
1132
00:59:03,707 --> 00:59:05,173
or whatever the expensive thing is.
1133
00:59:05,175 --> 00:59:08,410
So you have to learn to program in iOS, so
1134
00:59:08,412 --> 00:59:09,511
that multi-threading works.
1135
00:59:09,513 --> 00:59:12,648
And mostly that's a matter of putting UIs on the screen
1136
00:59:12,650 --> 00:59:16,652
that might not have all the information yet. Okay,
1137
00:59:16,654 --> 00:59:19,254
they might be fetching some data from the network so they
1138
00:59:19,256 --> 00:59:23,258
have a little spinning wheel or something loading dot,
1139
00:59:23,260 --> 00:59:25,928
dot, [LAUGH] dot. Something that tells the user, yeah,
1140
00:59:25,930 --> 00:59:28,864
I know you wanna see this data. I'm working on it, but
1141
00:59:28,866 --> 00:59:30,866
it has to come on screen instantly, okay?
1142
00:59:30,868 --> 00:59:33,602
You can't block, wait to get you one and then come on.
1143
00:59:33,604 --> 00:59:36,505
It's just, you can't do that, it doesn't work especially
1144
00:59:36,507 --> 00:59:37,839
iOS. Like the network can be really slow and
1145
00:59:37,841 --> 00:59:40,809
it might be stall. All right, cell network can be super
1146
00:59:40,811 --> 00:59:43,545
slow, so you can't do that. So the viewWillAppear
1147
00:59:43,547 --> 00:59:45,447
is a good place to kick off that kind of stuff.
1148
00:59:45,449 --> 00:59:49,151
Now, geometry is more likely to be correct here,
1149
00:59:49,153 --> 00:59:51,086
because you are just about to come on screen. But,
1150
00:59:51,088 --> 00:59:53,555
I still wouldn't put geometry calculations in here.
1151
00:59:53,557 --> 00:59:54,022
There is a better place for
1152
00:59:54,024 --> 00:59:57,693
that as well. There is also a viewDidAppear.
1153
00:59:57,695 --> 00:59:59,995
This gets sent right after you have come on screen.
1154
00:59:59,997 --> 01:00:01,430
This is a good place to do things like start
1155
01:00:01,432 --> 01:00:04,433
an animation. Okay, cause now you know you're onscreen,
1156
01:00:04,435 --> 01:00:06,134
boom, you can start an animation or something like
1157
01:00:06,136 --> 01:00:10,205
that. Okay, similarly there's viewWillDisappear, okay
1158
01:00:10,207 --> 01:00:12,975
usually viewWillDisappear, you're going to undue
1159
01:00:12,977 --> 01:00:15,544
the things you did in view will appear, so here you might
1160
01:00:15,546 --> 01:00:19,615
stop your animation. Or stop looking at the gyro, okay,
1161
01:00:19,617 --> 01:00:21,650
because things, things are going to disappear, so
1162
01:00:21,652 --> 01:00:25,621
there's no point in looking at the gyro anymore And there is
1163
01:00:25,623 --> 01:00:29,591
also a viewDidDisappear. Okay. This is where you might free
1164
01:00:29,593 --> 01:00:33,495
up something you fetched from the network in viewWillAppear.
1165
01:00:33,497 --> 01:00:36,331
See how they kind of a mirror each other, the will did
1166
01:00:36,333 --> 01:00:39,668
appear and then they will and did disappear. Kinda do and
1167
01:00:39,670 --> 01:00:43,305
undo things as it goes on and off, and these appear and
1168
01:00:43,307 --> 01:00:46,742
disappear could be viewDidLoad is only called once.
1169
01:00:46,744 --> 01:00:50,245
Okay one time. This could be called repeatedly as your view
1170
01:00:50,247 --> 01:00:53,081
controller comes on and off screen, appears it disappears,
1171
01:00:53,083 --> 01:00:56,084
appears it disappears, so this could be called repeatedly,
1172
01:00:56,086 --> 01:00:58,086
okay. So you wouldn't wanna do something in here
1173
01:00:58,088 --> 01:01:00,022
like if you're gonna fire off a fetch on the network
1174
01:01:00,024 --> 01:01:02,424
that's expensive, you wouldn't wanna be doing it over and
1175
01:01:02,426 --> 01:01:04,760
over if you already have the information from the last time
1176
01:01:04,762 --> 01:01:08,497
it appeared. Kay, so be a little careful about that. So
1177
01:01:08,499 --> 01:01:10,699
if the geometry, if you wanna do something based in
1178
01:01:10,701 --> 01:01:12,668
geometry, where do you do that? Well,
1179
01:01:12,670 --> 01:01:14,269
there's lifecycle methods specifically for
1180
01:01:14,271 --> 01:01:18,006
that, viewWillLayoutSubviews and viewDidLayoutSubviews. So
1181
01:01:18,008 --> 01:01:21,643
every time your bounds changes and sometimes when it doesn't
1182
01:01:21,645 --> 01:01:24,880
change, be careful, you'll get these two messages.
1183
01:01:24,882 --> 01:01:29,051
ViewWillLayoutSubviews and viewDidLayoutSubviews. Yeah.
1184
01:01:29,053 --> 01:01:33,155
>> [INAUDIBLE]
1185
01:01:33,157 --> 01:01:33,755
>> Great question, so
1186
01:01:33,757 --> 01:01:37,492
the question is, would my bounds change if for example,
1187
01:01:37,494 --> 01:01:39,528
I added a navigation bar at the top or
1188
01:01:39,530 --> 01:01:42,030
maybe the tab bar at appeared in the tab bar or
1189
01:01:42,032 --> 01:01:45,133
I'm in the split view? And I appeared on the other side,
1190
01:01:45,135 --> 01:01:48,937
in other words, can it change due to the environment I'm in?
1191
01:01:48,939 --> 01:01:49,838
And the answer is, it can,
1192
01:01:49,840 --> 01:01:52,974
now it doesn't usually change with the navigation case,
1193
01:01:52,976 --> 01:01:55,811
because action that navigation bar is layered on top of
1194
01:01:55,813 --> 01:01:59,381
your view, it's kinda semi see through, if you look. If you
1195
01:01:59,383 --> 01:02:02,884
go look at navigation items,in the navigation bars, they're
1196
01:02:02,886 --> 01:02:05,887
actually kind of translucent and the thing is behind it, so
1197
01:02:05,889 --> 01:02:07,823
your bounds actually wouldn't probably change there.
1198
01:02:07,825 --> 01:02:09,758
But they might change if you're put into a tab bar,
1199
01:02:09,760 --> 01:02:12,627
because that space at the bottom might not overlap.
1200
01:02:12,629 --> 01:02:15,330
Okay, but the main time your balance changes when you
1201
01:02:15,332 --> 01:02:18,667
rotate, but it can happen other times too.
1202
01:02:18,669 --> 01:02:21,269
So between viewWillLayoutSubviews and
1203
01:02:21,271 --> 01:02:24,573
viewDidLayoutSubviews all that auto layout is gonna happen,
1204
01:02:24,575 --> 01:02:26,074
the stuff based on the constraints.
1205
01:02:26,076 --> 01:02:29,177
Remember all that blue dash lines and all that stuff?
1206
01:02:29,179 --> 01:02:29,711
That's gonna happen between.
1207
01:02:29,713 --> 01:02:33,248
So you can do stuff before all the blue lines, the autolayout
1208
01:02:33,250 --> 01:02:35,751
stuff is happening, or you can do it after, okay?
1209
01:02:35,753 --> 01:02:37,285
You rarely need to do anything here,
1210
01:02:37,287 --> 01:02:39,087
because usually the constraints are making all
1211
01:02:39,089 --> 01:02:42,390
the decisions about what to do with everything, okay? But
1212
01:02:42,392 --> 01:02:46,228
it is possible sometimes, you know, not in assignment three,
1213
01:02:46,230 --> 01:02:48,830
maybe in assignment six in this class. Maybe you might
1214
01:02:48,832 --> 01:02:52,434
wanna do something in viewWill or DidLayoutSubviews. But this
1215
01:02:52,436 --> 01:02:55,737
is the only place you wanna do anything that's geometry
1216
01:02:56,206 --> 01:03:00,942
dependent, okay? Yeah, one thing about these methods,
1217
01:03:00,944 --> 01:03:02,144
don't think that these are only gonna
1218
01:03:02,146 --> 01:03:04,179
be called when you're bound to actually changes.
1219
01:03:04,181 --> 01:03:07,783
The system is allowed to call these any time it wants, and
1220
01:03:07,785 --> 01:03:10,552
sometimes it might be drawing something to an offscreen
1221
01:03:10,554 --> 01:03:12,988
buffer to prepare to do an animation and so
1222
01:03:12,990 --> 01:03:14,756
it calls this, okay? But, and
1223
01:03:14,758 --> 01:03:17,425
then it actually draws on the screen and calls it again.
1224
01:03:17,427 --> 01:03:20,061
And the bounds have not actually changed, okay? So
1225
01:03:20,063 --> 01:03:23,265
don't expect that every single time this calls the bounds
1226
01:03:23,267 --> 01:03:25,834
will be different. If you do something really expensive as
1227
01:03:25,836 --> 01:03:28,436
a result of a bounds change, you might wanna keep track of
1228
01:03:28,438 --> 01:03:31,406
what the bounds were the last time you did that expensive
1229
01:03:31,408 --> 01:03:33,141
thing. And when this gets called,
1230
01:03:33,143 --> 01:03:36,344
make sure the bounds have actually changed, okay?
1231
01:03:36,346 --> 01:03:37,979
Again, you're not gonna need any of this for
1232
01:03:37,981 --> 01:03:39,247
assignment three, but it's good to know.
1233
01:03:39,249 --> 01:03:41,583
This can be recalled all the time, repeatedly, over and
1234
01:03:41,585 --> 01:03:46,021
over. Even if your bounds have not actually changed. Okay, so
1235
01:03:46,023 --> 01:03:48,523
auto rotation. Usually what happens in auto rotation.
1236
01:03:48,525 --> 01:03:51,693
When auto rotation is when you turn your phone from landscape
1237
01:03:51,695 --> 01:03:55,230
to portrait, it automatically changes your bounds to be tall
1238
01:03:55,232 --> 01:03:58,200
and skinny. And then wide and short, okay?
1239
01:03:58,202 --> 01:04:00,435
So autorotation is usually just a bounds change, so
1240
01:04:00,437 --> 01:04:03,538
you're just dealing with it in view did layout sub views, or
1241
01:04:03,540 --> 01:04:05,807
more likely, you just have constraints, and
1242
01:04:05,809 --> 01:04:08,043
the constraints just work, okay? So
1243
01:04:08,045 --> 01:04:10,512
there's really nothing to do, but it is possible to
1244
01:04:10,514 --> 01:04:14,449
get involved in autorotation, specifically in the animation.
1245
01:04:14,451 --> 01:04:16,918
If you watch an autorotation really, really closely,
1246
01:04:16,920 --> 01:04:20,155
you'll see that the views actually move. They move from
1247
01:04:20,157 --> 01:04:23,158
their position in portrait to where they are in landscape
1248
01:04:23,160 --> 01:04:25,427
in an animated fashion. They don't just jump there,
1249
01:04:25,429 --> 01:04:29,197
they kind of fly across the screen. And you can fly things
1250
01:04:29,199 --> 01:04:32,200
across the screen, too, if you want using this,
1251
01:04:32,202 --> 01:04:35,837
life cycle method called will, viewWillTransitionToSize. And
1252
01:04:35,839 --> 01:04:38,406
it's basically telling you, this is going to transition.
1253
01:04:38,408 --> 01:04:40,175
It has this thing called a transition coordinator.
1254
01:04:40,177 --> 01:04:41,676
You can look up the documentation on it. But
1255
01:04:41,678 --> 01:04:45,714
the main thing the transition coordinator has is a closure
1256
01:04:45,716 --> 01:04:50,185
that you can provide that will be executed alongside
1257
01:04:50,187 --> 01:04:52,520
the animation that it's doing to animate your,
1258
01:04:52,522 --> 01:04:56,391
your, rotation, okay? You won't need this in this class,
1259
01:04:56,393 --> 01:04:58,159
but I just wanna let you know this is here. Okay,
1260
01:04:58,161 --> 01:05:01,863
in low-memory situations, didReceiveMemoryWarning, okay,
1261
01:05:01,865 --> 01:05:05,367
this is only gonna happen if your app uses a lot of memory,
1262
01:05:05,369 --> 01:05:08,603
okay? iPhones today have so much memory,
1263
01:05:08,605 --> 01:05:08,803
it's ridiculous, okay?
1264
01:05:08,805 --> 01:05:11,740
So this is not really gonna happen, likely. But if you had
1265
01:05:11,742 --> 01:05:15,744
a memory leak where you're leaking videos in memory,
1266
01:05:15,746 --> 01:05:17,479
or images, huge images, or
1267
01:05:17,481 --> 01:05:19,915
something like that, it's possible you might get this.
1268
01:05:19,917 --> 01:05:23,285
When you get this, you should throw out any pointers to big
1269
01:05:23,287 --> 01:05:27,055
things you have in the heap that you can recreate, okay?
1270
01:05:27,057 --> 01:05:29,124
So you can re-download them from the web or
1271
01:05:29,126 --> 01:05:30,892
from your file system or something.
1272
01:05:30,894 --> 01:05:34,162
You should throw them out when you get this one. Okay,
1273
01:05:34,164 --> 01:05:35,864
going back to the very beginning of the lifecycle,
1274
01:05:35,866 --> 01:05:38,800
there's another interesting method called awakeFromNib.
1275
01:05:38,802 --> 01:05:42,103
Okay, awakeFromNib is sent to you really early before
1276
01:05:42,105 --> 01:05:45,807
preparation, before outlet setting, before all that, but
1277
01:05:45,809 --> 01:05:49,577
only if you're coming out of a storyboard. Okay, so it's this
1278
01:05:49,579 --> 01:05:53,181
really, really, really, really early method that gets called.
1279
01:05:53,183 --> 01:05:56,151
And it actually gets sent to every object that comes out of
1280
01:05:56,153 --> 01:05:58,253
a storyboard, not just view controllers,
1281
01:05:58,255 --> 01:06:00,288
okay? Now I mention it at the end here,
1282
01:06:00,290 --> 01:06:03,491
because I really don't want you using it that much, okay?
1283
01:06:03,493 --> 01:06:06,127
You're much better off putting things in viewDidLoad,
1284
01:06:06,129 --> 01:06:08,897
viewWillAppear, those kind of places than awakeFromNib,
1285
01:06:08,899 --> 01:06:11,366
okay? Some people find out about this, and they just put
1286
01:06:11,368 --> 01:06:13,969
all of their initialization junk into awakeFromNib, and
1287
01:06:13,971 --> 01:06:15,270
it's not really for that. It's for
1288
01:06:15,272 --> 01:06:16,738
really kind of exceptional circumstances,
1289
01:06:16,740 --> 01:06:19,040
where you really, really early you need to do something for
1290
01:06:19,042 --> 01:06:22,510
some reason, okay? Set, you're gonna need to set yourself
1291
01:06:22,512 --> 01:06:25,513
as a controller of some other object that something's
1292
01:06:25,515 --> 01:06:29,451
happening early, whatever. But I just wanna let you know it's
1293
01:06:29,453 --> 01:06:33,054
in there. Okay, so here's the summary of the lifecycle.
1294
01:06:33,056 --> 01:06:35,857
You're instantiated, usually from your storyboard.
1295
01:06:35,859 --> 01:06:37,258
awakeFromNib is then called.
1296
01:06:37,260 --> 01:06:39,327
Segue prep happens if you're being segued to,
1297
01:06:39,329 --> 01:06:42,130
which you almost always are when MVCs are being created.
1298
01:06:42,132 --> 01:06:44,666
It's because you're being segued to usually. Then
1299
01:06:44,668 --> 01:06:48,303
your outlets get set, okay, the things like display and
1300
01:06:48,305 --> 01:06:51,873
the calculator get set. Then viewDidLoad gets called, and
1301
01:06:51,875 --> 01:06:54,609
you can do a lot of nice initialization here that's not
1302
01:06:54,611 --> 01:06:57,412
geometry related. Then viewWillAppear and, and
1303
01:06:57,414 --> 01:06:59,247
DidAppear and then viewWillDisappear and
1304
01:06:59,249 --> 01:07:02,017
viewDidDisappear will be sent to you as you appear and
1305
01:07:02,019 --> 01:07:04,819
disappear on screen. Meanwhile, at any time,
1306
01:07:04,821 --> 01:07:06,221
whether you are onscreen or not,
1307
01:07:06,223 --> 01:07:07,489
whether you've appeared or not,
1308
01:07:07,491 --> 01:07:08,957
you can be set viewWillLayoutSubviews and
1309
01:07:08,959 --> 01:07:11,860
viewDidLayoutSubviews. Okay, and that's where you're going
1310
01:07:11,862 --> 01:07:14,429
to react to geometry changes if nec, if you need to outside
1311
01:07:14,431 --> 01:07:17,866
of constraints. Okay, usually constraints will just do it
1312
01:07:17,868 --> 01:07:19,000
for you. And if your memory gets
1313
01:07:19,002 --> 01:07:21,736
low at any time, you could get didReceiveMemoryWarning.
1314
01:07:21,738 --> 01:07:23,371
If you get didReceiveMemoryWarning and
1315
01:07:23,373 --> 01:07:24,205
you don't clean up your memory,
1316
01:07:24,207 --> 01:07:27,008
the system will probably kill you, okay? Just probably
1317
01:07:27,010 --> 01:07:32,013
your app will appear to crash to the user. Okay, so
1318
01:07:32,015 --> 01:07:34,983
I have a short demo here for View Controller Lifecycle,
1319
01:07:34,985 --> 01:07:38,219
and I'm just gonna throw some print statements into all of
1320
01:07:38,221 --> 01:07:41,389
those Lifecycle methods. Okay, that's all I'm gonna do. And
1321
01:07:41,391 --> 01:07:44,626
then we're gonna run our FaceIt, and we're gonna see,
1322
01:07:44,628 --> 01:07:47,028
as view controllers appear and disappear and
1323
01:07:47,030 --> 01:07:50,165
get created, what's happening in the Lifecycle,
1324
01:07:50,167 --> 01:07:52,333
okay? So that's all this demo is,
1325
01:07:52,335 --> 01:07:57,305
it's very brief. Okay, the way that I'm going to,
1326
01:07:57,307 --> 01:08:02,243
kind of print in the console is I have this, a little
1327
01:08:02,245 --> 01:08:06,648
extension here called, VCL, View Controller Lifecycle,
1328
01:08:06,650 --> 01:08:10,952
then I'm just gonna add to FaceIt, copy it in, and
1329
01:08:10,954 --> 01:08:14,255
I will post this, so you can look at it if you want.
1330
01:08:14,257 --> 01:08:18,426
But it's basically, it's just extending these classes to
1331
01:08:18,428 --> 01:08:20,628
override all of these, things. And
1332
01:08:20,630 --> 01:08:23,898
one thing that's cool is it will show you, the log which
1333
01:08:23,900 --> 01:08:27,001
instance of the Face View Controller, which instance of
1334
01:08:27,003 --> 01:08:30,038
the emotion controller, okay? So, that's kind of fun.
1335
01:08:30,040 --> 01:08:33,074
All right, so let's go ahead and run this, let's do it on,
1336
01:08:33,076 --> 01:08:37,178
iPhone first. All right, so this thing appeared,
1337
01:08:37,180 --> 01:08:41,483
all right, and it's showing the face, okay, this is kind
1338
01:08:41,485 --> 01:08:43,618
of like on the iPad, it would be showing the detail here.
1339
01:08:43,620 --> 01:08:46,221
It's showing a face, but we haven't chosen an emotion. And
1340
01:08:46,223 --> 01:08:48,890
let's look over here what happened. Both an Emotions
1341
01:08:48,892 --> 01:08:52,894
view controller and a Face view controller have been
1342
01:08:52,896 --> 01:08:56,431
created and instantiated, okay? Now that's because
1343
01:08:56,433 --> 01:08:59,734
the Emotion view controller is at the bottom of the stack and
1344
01:08:59,736 --> 01:09:01,302
a Face view controller is on top of it.
1345
01:09:01,304 --> 01:09:04,272
So they're both on the stack, that means they both exist.
1346
01:09:04,274 --> 01:09:08,409
However, notice that while the Emotions 1 has had viewDidLoad
1347
01:09:08,411 --> 01:09:10,612
called, because its outlets have all been set and
1348
01:09:10,614 --> 01:09:12,447
all that stuff, it has never appeared.
1349
01:09:12,449 --> 01:09:14,482
You see Emotions 1 never gets viewWillAppear,
1350
01:09:14,484 --> 01:09:17,652
because it never has appeared. We've never seen the angry,
1351
01:09:17,654 --> 01:09:20,021
mischievous, that has not appeared on screen. So
1352
01:09:20,023 --> 01:09:23,224
it hasn't appeared, whereas the Face 1 has, so
1353
01:09:23,226 --> 01:09:27,428
it's gotten viewWillAppear. It got, it laid out, okay,
1354
01:09:27,430 --> 01:09:29,898
viewWillLayoutSubviews and DidLayoutSubviews.
1355
01:09:29,900 --> 01:09:31,666
And then it got viewDidAppear, and sure enough,
1356
01:09:31,668 --> 01:09:35,904
here it is. Now I'm gonna press Back, the Back button,
1357
01:09:35,906 --> 01:09:39,474
and let's watch what happens, okay?
1358
01:09:39,476 --> 01:09:42,010
Face 1 got viewWillDisappear because of course,
1359
01:09:42,012 --> 01:09:45,813
it was gonna disappear. Emotions 1, that's this one,
1360
01:09:45,815 --> 01:09:49,150
got viewWillAppear cuz it's about to appear.
1361
01:09:49,152 --> 01:09:53,288
Then Emotions 1 got laid out a couple of times here.
1362
01:09:53,290 --> 01:09:57,892
Let's see some more here, there we go. Okay,
1363
01:09:57,894 --> 01:10:01,329
so Emotions 1 got laid out a couple of times here,
1364
01:10:01,331 --> 01:10:04,666
see if you will ask at the same exact size. So this
1365
01:10:04,668 --> 01:10:07,168
is why I'm saying be careful, that can happen multiple
1366
01:10:07,170 --> 01:10:10,271
times. Why might that happen? Again, maybe the system was
1367
01:10:10,273 --> 01:10:12,540
preparing to animate that thing sliding in, and
1368
01:10:12,542 --> 01:10:15,577
so it was drawing off screen, then it was drawing on screen,
1369
01:10:15,579 --> 01:10:17,445
and it was laying out both times,
1370
01:10:17,447 --> 01:10:18,813
it's called it both times. You don't know why,
1371
01:10:18,815 --> 01:10:20,615
you don't care why. You just have to be prepared for
1372
01:10:20,617 --> 01:10:23,518
the fact that it could call it multiple times. Then Face
1373
01:10:23,520 --> 01:10:27,388
1 got viewDidDisappear because the face disappeared,
1374
01:10:27,390 --> 01:10:31,893
it slid off. And Emotions 1 got viewDidAppear, because it
1375
01:10:31,895 --> 01:10:35,897
got replaced on top of it. Then notice Emotions 1 got lay
1376
01:10:35,899 --> 01:10:38,399
out again. Okay, even though it was already on screen.
1377
01:10:38,401 --> 01:10:40,068
Why, I don't know. Maybe this is the layout for
1378
01:10:40,070 --> 01:10:43,238
it actually appearing on screen. It's not clear. Okay,
1379
01:10:43,240 --> 01:10:45,373
everybody got that? Now I'm gonna click on one of these,
1380
01:10:45,375 --> 01:10:48,576
I'm gonna click on Happy, and let's watch what happens.
1381
01:10:48,578 --> 01:10:52,747
Okay, now most important thing to watch here, okay, is that
1382
01:10:52,749 --> 01:10:57,819
Face 1, that first face that we showed up is gone, see?
1383
01:10:57,821 --> 01:11:01,055
No more reference to Face 1. It has created a new face,
1384
01:11:01,057 --> 01:11:03,925
came out of the story board. Okay, this is what I'm talking
1385
01:11:03,927 --> 01:11:06,361
about. When you segue, it creates a new NVC.
1386
01:11:06,363 --> 01:11:10,331
That first Face 1 is out of the heap, it is gone, okay?
1387
01:11:10,333 --> 01:11:14,902
Every time you segue, you get a new one. But Emotions, okay?
1388
01:11:14,904 --> 01:11:18,606
The root, it's still Emotions 1, okay?
1389
01:11:18,608 --> 01:11:21,476
Since it sits on the root, the base of the card stack,
1390
01:11:21,478 --> 01:11:25,013
it never comes off. So, it just sits there forever, okay?
1391
01:11:25,015 --> 01:11:26,514
New cards come on and off, on and off.
1392
01:11:26,516 --> 01:11:27,815
But it's at the root, so it just sits there. So
1393
01:11:27,817 --> 01:11:32,020
it's always gonna be Emotions 1, okay? And then, again you
1394
01:11:32,022 --> 01:11:35,623
can see it goes through willDidLoad, viewWillAppear,
1395
01:11:35,625 --> 01:11:39,494
got laid out, Face 2 finally did appear, okay? And notice
1396
01:11:39,496 --> 01:11:42,597
that Emotions 1 did disappear because it got covered up
1397
01:11:42,599 --> 01:11:44,932
by this one. Okay, again if we go back and
1398
01:11:44,934 --> 01:11:47,268
I click a different one like Mischievous,
1399
01:11:47,270 --> 01:11:50,938
now we have Face 3, okay? Face 2 is gone forever but
1400
01:11:50,940 --> 01:11:54,108
still Emotions 1. Everyone understand that?
1401
01:11:54,110 --> 01:11:57,812
Okay, now let's run this on the iPad.
1402
01:12:06,056 --> 01:12:10,658
Okay, so here, okay, we're seeing the detail, okay?
1403
01:12:10,660 --> 01:12:14,329
Notice here that even though the master does not appear
1404
01:12:14,331 --> 01:12:17,699
on screen, it gets created. Not only does it get created,
1405
01:12:17,701 --> 01:12:20,702
it gets laid out. But it never got viewWillAppear and
1406
01:12:20,704 --> 01:12:23,671
viewDidAppear, because the master has never appeared,
1407
01:12:23,673 --> 01:12:28,843
okay? Whereas Face 1, it did get appear, and it appeared,
1408
01:12:28,845 --> 01:12:29,143
Now watch what happens if I rotate, okay, I'm gonna rotate
1409
01:12:29,145 --> 01:12:32,980
right?
1410
01:12:32,982 --> 01:12:36,250
to landscape. Okay, now when I rotate to landscape,
1411
01:12:36,252 --> 01:12:39,554
all of a sudden the master, Emotions 1, it got that
1412
01:12:39,556 --> 01:12:41,155
viewWillTransitionToSize thing.
1413
01:12:41,157 --> 01:12:45,293
That's the auto rotation thing I was telling you about, okay?
1414
01:12:45,295 --> 01:12:47,195
And Emotions 1 also got laid out and
1415
01:12:47,197 --> 01:12:51,399
told it would appear, and then it did appear, okay? Now Face
1416
01:12:51,401 --> 01:12:55,603
1 also got to participate in that rotation. See,
1417
01:12:55,605 --> 01:12:58,306
animatingAlongsideTransition. Okay, and
1418
01:12:58,308 --> 01:13:02,543
then here's Emotion 1 appear. Notice that inside here, okay,
1419
01:13:02,545 --> 01:13:06,581
there's no viewWillAppear or viewWillDisappear for Face 1.
1420
01:13:06,583 --> 01:13:08,383
That's cuz it was already on screen,
1421
01:13:08,385 --> 01:13:11,085
okay, so even though its bounds changed, okay, and
1422
01:13:11,087 --> 01:13:14,856
it got viewWillLayoutSubviews, it never got viewWillAppear,
1423
01:13:14,858 --> 01:13:16,924
it already was on screen. That make sense?
1424
01:13:16,926 --> 01:13:20,461
And now interesting. I'm gonna click on one of these, and
1425
01:13:20,463 --> 01:13:25,833
what's gonna happen to Face 1? Out of the heap. Okay,
1426
01:13:25,835 --> 01:13:31,506
watch this. Angry, see Face 1 gone, now we're on Face 2.
1427
01:13:31,508 --> 01:13:34,108
So even in a split view where it seems like,
1428
01:13:34,110 --> 01:13:36,477
you know with the cards, it's really obvious that yeah,
1429
01:13:36,479 --> 01:13:39,247
I'm throwing the card away, I'm making a new card. But
1430
01:13:39,249 --> 01:13:40,615
even here in a split view,
1431
01:13:40,617 --> 01:13:41,649
I'm throwing away the detail,
1432
01:13:41,651 --> 01:13:44,786
I'm making a new one. Plopping it right in on top, okay?
1433
01:13:44,788 --> 01:13:48,756
So all of these segues create a new MVC, even if they're
1434
01:13:48,758 --> 01:13:50,825
a split view navigation controller, whatever.
1435
01:13:50,827 --> 01:13:54,562
The only ones that don't that you've seen is in tab bar. All
1436
01:13:54,564 --> 01:13:57,565
those things in the tab bar. It's too bad I don't really
1437
01:13:57,567 --> 01:13:59,767
have a tab bar, I wanted to show you this VCL thing.
1438
01:13:59,769 --> 01:14:02,770
But those ones get created when you click on the tab for
1439
01:14:02,772 --> 01:14:05,673
the first time and they live forever. Okay,
1440
01:14:05,675 --> 01:14:07,909
they never go away. And when you click on the tab,
1441
01:14:07,911 --> 01:14:10,411
it comes back, it's still exactly the same way.
1442
01:14:10,413 --> 01:14:10,845
It's not a segue,
1443
01:14:10,847 --> 01:14:13,114
in other words the tab bar is not segueing.
1444
01:14:13,116 --> 01:14:15,583
Navigation control and split view are segueing, tab bar,
1445
01:14:15,585 --> 01:14:22,590
notice we never did anything there to do a segue, okay? So,
1446
01:14:22,592 --> 01:14:26,360
yeah, notice here also that even though when I clicked on
1447
01:14:26,362 --> 01:14:29,397
here to show this, look what happened to Emotions 1,
1448
01:14:29,399 --> 01:14:32,266
it got laid out a couple times even though its size did not
1449
01:14:32,268 --> 01:14:35,303
change. Here I'll do it again. We'll go to happy. Okay,
1450
01:14:35,305 --> 01:14:38,806
I didn't change, this did not change size in the least.
1451
01:14:38,808 --> 01:14:40,942
Okay, and yet it got viewDidLayoutSubviews,
1452
01:14:40,944 --> 01:14:44,178
viewDidLayoutSubview. Okay, so you have to be prepared for
1453
01:14:44,180 --> 01:14:47,849
that being called all the time, okay? And so here is
1454
01:14:47,851 --> 01:14:51,619
Face 3, it's getting pulled out of the storyboard. It's
1455
01:14:51,621 --> 01:14:54,889
kind of interesting that it gets a viewWillDisappear here.
1456
01:14:54,891 --> 01:14:55,056
I'm not sure why it gets that right there. Kind of strange
1457
01:14:55,058 --> 01:14:59,961
I've noticed this,
1458
01:14:59,963 --> 01:15:04,098
and then it loads, it gets a WillAppear it lays out,
1459
01:15:04,100 --> 01:15:08,536
it gets DidDisappear, and then DidAppear. So I, this is
1460
01:15:08,538 --> 01:15:11,405
strange, I don't really understand why it goes through
1461
01:15:11,407 --> 01:15:15,209
that shenanigan right there, but the good news is that
1462
01:15:15,211 --> 01:15:18,246
once viewDidAppear happens, it has appeared so
1463
01:15:18,248 --> 01:15:21,516
that's good. It's just kind of strange, so be prepared for
1464
01:15:21,518 --> 01:15:23,751
that. Now you'll have this code of mine,
1465
01:15:23,753 --> 01:15:25,553
this little logging thing. So
1466
01:15:25,555 --> 01:15:28,356
you can always throw this in your own app, okay, change
1467
01:15:28,358 --> 01:15:31,192
the name to the classes. And then you can see if
1468
01:15:31,194 --> 01:15:33,995
somethings not going the way you expect and the lifecycle
1469
01:15:33,997 --> 01:15:36,130
method is not getting called at the time you want,
1470
01:15:36,132 --> 01:15:41,569
you can see when it's happening exactly. Okay, all
1471
01:15:41,571 --> 01:15:45,606
right, that is it for today. Your assignment is posted.
1472
01:15:45,608 --> 01:15:47,174
I already explained what it's gonna be.
1473
01:15:47,176 --> 01:15:51,812
It's due next Wednesday before class. Hopefully you've
1474
01:15:51,814 --> 01:15:55,416
finished up all of the reading for the Swift thing.
1475
01:15:55,418 --> 01:15:58,052
All that Swift reading, you're really gonna want to have that
1476
01:15:58,054 --> 01:16:00,488
in your memory bank as the rest of the course happens.
1477
01:16:00,490 --> 01:16:01,556
So if you're a little behind there,
1478
01:16:01,558 --> 01:16:05,026
make sure you catch up as soon as possible. All right, I'll
1479
01:16:05,028 --> 01:16:08,529
be here if you have questions. >> For
1480
01:16:08,531 --> 01:16:08,562
more, please visit us as stanford.edu.
================================================
FILE: subtitles/7. Closures, Extensions, Protocols, Delegation, and ScrollView.srt
================================================
1
00:00:00,001 --> 00:00:03,168
[MUSIC]
2
00:00:03,170 --> 00:00:07,840
Stanford University. >> All right well welcome to
3
00:00:07,842 --> 00:00:11,810
lecture number seven of CS193P,
4
00:00:11,812 --> 00:00:16,849
Spring of 2016. So we've got a lot of different topics today,
5
00:00:16,851 --> 00:00:19,718
a couple of demos interspersed with it.
6
00:00:19,720 --> 00:00:22,354
Here are all the topics. A lot of these are kind of,
7
00:00:22,356 --> 00:00:26,525
I don't want to say advanced Swift features, but kind of
8
00:00:26,527 --> 00:00:30,596
important Swift features that we haven't talked about so
9
00:00:30,598 --> 00:00:33,565
far. And then towards the end
10
00:00:33,567 --> 00:00:37,836
we'll talk a little about an iOS kind of concept,
11
00:00:37,838 --> 00:00:39,872
that comes over from the objective C world,
12
00:00:39,874 --> 00:00:42,775
that we have to deal with in Swift, called delegation, but
13
00:00:42,777 --> 00:00:45,778
it's super important. It has to do with our whole NVC
14
00:00:45,780 --> 00:00:49,048
model and how we do that blind structure communication and
15
00:00:49,050 --> 00:00:50,582
now I'm gonna finish off with scroll view.
16
00:00:50,584 --> 00:00:53,218
Okay, very important class, because these devices
17
00:00:53,220 --> 00:00:55,554
are really small and you want to look at something big.
18
00:00:55,556 --> 00:00:59,925
So you will need to be able to scroll and zoom in on it,
19
00:00:59,927 --> 00:01:03,629
okay. All right, first topic, Memory management. So I told
20
00:01:03,631 --> 00:01:07,733
you already, that reference classes, or reference types,
21
00:01:07,735 --> 00:01:12,071
which are classes, live in the heap, all right. And that,
22
00:01:12,073 --> 00:01:15,007
that memory is automatically taken care of for you, okay?
23
00:01:15,009 --> 00:01:17,009
That's called automatic reference counting,
24
00:01:17,011 --> 00:01:18,877
because it's a reference counting base scheme. It keeps
25
00:01:18,879 --> 00:01:20,746
track of how many pointers are pointing to something, and
26
00:01:20,748 --> 00:01:24,316
when that count goes to zero, it immediately freezes, right?
27
00:01:24,318 --> 00:01:26,185
Which is different from, like, garbage collection,
28
00:01:26,187 --> 00:01:28,020
where you're basically going through the heap and
29
00:01:28,022 --> 00:01:30,789
marking it, trying to find things that aren't pointed to,
30
00:01:30,791 --> 00:01:34,660
and then sweeping through. Marking or sweeping and
31
00:01:34,662 --> 00:01:35,194
then clearing out. So
32
00:01:35,196 --> 00:01:38,797
you know, that kind of memory frame can be intermittent and
33
00:01:38,799 --> 00:01:41,600
it's not really predictable, whereas, ARC is completely and
34
00:01:41,602 --> 00:01:45,137
utterly predictable. Normally you don't think about ARC,
35
00:01:45,139 --> 00:01:47,739
you don't think about memory in the heap at all except for
36
00:01:47,741 --> 00:01:51,076
there are, there is a small way that you can influence ARC
37
00:01:51,078 --> 00:01:51,210
the way it works.
38
00:01:51,212 --> 00:01:54,012
And that's with these three things right here. Strong,
39
00:01:54,014 --> 00:01:57,850
weak, and unowned, okay? So we're going to talk about each
40
00:01:57,852 --> 00:02:00,619
of these three keywords that you can put in Swift.
41
00:02:00,621 --> 00:02:04,256
And all of them you use when you're declaring a variable,
42
00:02:04,258 --> 00:02:07,993
okay? It's all for declaring variables. All right, so
43
00:02:07,995 --> 00:02:10,996
strong, you don't even see the word, strong in Swift,
44
00:02:10,998 --> 00:02:14,299
because that's the default. Strong is normal
45
00:02:14,301 --> 00:02:18,837
reference counting. Basically a pointer that is strong,
46
00:02:18,839 --> 00:02:21,807
forces whatever's in the heap to stay in the heap,
47
00:02:21,809 --> 00:02:24,843
until that pointer no longer points to it. Okay, so
48
00:02:24,845 --> 00:02:27,646
it strongly holds things in the in the heap, so that's
49
00:02:27,648 --> 00:02:30,582
the default. Okay, if you go round creating pointers,
50
00:02:30,584 --> 00:02:33,051
they are strong pointers, gotta get rid of all
51
00:02:33,053 --> 00:02:36,255
the pointers to something for it to clean up. Okay, but
52
00:02:36,257 --> 00:02:41,460
then there's weak. So if you have a weak pointer, it means,
53
00:02:41,462 --> 00:02:43,595
if no one else is interested in this thing in heap,
54
00:02:43,597 --> 00:02:47,766
then you can get rid of it in the heap and set me to nil.
55
00:02:48,002 --> 00:02:51,069
Okay, so it's kind of like, yeah, I'm pointing to
56
00:02:51,071 --> 00:02:53,739
the thing in the heap, but I'm not that interested in it,
57
00:02:53,741 --> 00:02:56,808
if it goes away then just set me to nil. Now for me to be
58
00:02:56,810 --> 00:02:59,745
set to nill that means I have to be an optional pointer,
59
00:02:59,747 --> 00:03:04,216
okay? So weak only works for optional reference pointers,
60
00:03:04,218 --> 00:03:06,652
okay? So basically optional pointers to classes,
61
00:03:06,654 --> 00:03:11,023
all right? A weak pointer never keeps things in
62
00:03:11,025 --> 00:03:13,892
heap, okay? It's up to strong pointers, pointing to things
63
00:03:13,894 --> 00:03:16,962
to keep them in the heap. So, a good example of this,
64
00:03:16,964 --> 00:03:19,131
that you've already seen, is outlets, right?
65
00:03:19,133 --> 00:03:22,167
Remember your calculator, you have an outlet to the display,
66
00:03:22,169 --> 00:03:24,036
that UI label. That's weak, okay,
67
00:03:24,038 --> 00:03:28,473
it automatically got set to be weak. Why is that weak? Well,
68
00:03:28,475 --> 00:03:30,008
because the view hierarchy, okay?
69
00:03:30,010 --> 00:03:32,511
The super view, for example, of that UI label,
70
00:03:32,513 --> 00:03:35,380
it has a strong pointer to that UI label. So your outlet
71
00:03:35,382 --> 00:03:38,350
doesn't have to keep it in the keep, in the heap, cuz it's
72
00:03:38,352 --> 00:03:41,420
going to be kept in the heap by the view hierarchy. But
73
00:03:41,422 --> 00:03:44,156
if the view hierarchy ever gets rid of that UI label,
74
00:03:44,158 --> 00:03:46,725
in other words, it's no longer part of the view,
75
00:03:46,727 --> 00:03:49,461
then you're probably not interested in it as an outlet
76
00:03:49,463 --> 00:03:53,665
anyway, so just set the outlet to nill. Now you notice when
77
00:03:53,667 --> 00:03:56,435
you make an outlet you can over at the bottom switch over
78
00:03:56,437 --> 00:03:59,738
to strong, meaning that even if the UI label comes out of
79
00:03:59,740 --> 00:04:03,408
the view hierarchy it'll still stay in the heap. Okay, maybe
80
00:04:03,410 --> 00:04:05,978
you wanna do that because maybe you wanna put it back in
81
00:04:05,980 --> 00:04:08,013
the view hierarchy after it got removed from the view
82
00:04:08,015 --> 00:04:10,882
hierarchy, okay. That'd be one of the only reasons I can
83
00:04:10,884 --> 00:04:13,385
think of that you would have a strong pointer through
84
00:04:13,387 --> 00:04:15,721
an outlet to something in the view hierarchy.
85
00:04:15,723 --> 00:04:18,490
Right, cuz maybe we are going to take in and out. Okay,
86
00:04:18,492 --> 00:04:21,493
everyone understand weak? Optional reference pointers
87
00:04:21,495 --> 00:04:24,329
can be weak. Okay, and you just declare it when you say
88
00:04:24,331 --> 00:04:28,800
var, you say weak var whatever optional pointer, okay?
89
00:04:28,802 --> 00:04:33,605
And then the last one here, unowned, unowned kind of means
90
00:04:33,607 --> 00:04:37,242
don't reference count this, which is very dangerous. If
91
00:04:37,244 --> 00:04:41,880
you have an unowned pointer, it means that reference
92
00:04:41,882 --> 00:04:45,517
counting is not going to track it. And so this pointer is
93
00:04:45,519 --> 00:04:47,953
always going to point to that little place in memory.
94
00:04:47,955 --> 00:04:50,589
And you better be sure that the thing you point to
95
00:04:50,591 --> 00:04:54,993
stays there. Okay, until you don't use that, this pointer
96
00:04:54,995 --> 00:04:58,230
anymore, okay? And if you have an unknown pointer and
97
00:04:58,232 --> 00:05:00,132
you later reference it, and the thing that you're
98
00:05:00,134 --> 00:05:02,267
reference got thrown out of the heap, because there were
99
00:05:02,269 --> 00:05:06,738
no more strong pointers to it, then you will crash, okay?
100
00:05:06,740 --> 00:05:09,641
Which could be memory reference error.
101
00:05:09,643 --> 00:05:12,444
Now you might say, why do I ever want this unowned thing?
102
00:05:12,446 --> 00:05:15,013
And I will show you a little example, later in this
103
00:05:15,015 --> 00:05:18,817
lecture, of a way to break a memory cycle between objects.
104
00:05:18,819 --> 00:05:19,584
One object points to another,
105
00:05:19,586 --> 00:05:22,187
which points back to the first one, directly or indirectly,
106
00:05:22,189 --> 00:05:25,590
keeping them both in memory. You can break it with unknown,
107
00:05:25,592 --> 00:05:27,092
but often times you'll break it with weak.
108
00:05:27,094 --> 00:05:30,162
But you'll see that coming up. Okay, so this is the only way
109
00:05:30,164 --> 00:05:32,931
you can influence arc, is with these three things. And you
110
00:05:32,933 --> 00:05:35,634
almost never really use this, it's very rare to use these.
111
00:05:35,636 --> 00:05:39,438
Weak, occasionally, strong, never, 'cuz of the default,
112
00:05:39,440 --> 00:05:44,042
and unowned, next to never. Okay, all right, let's talk
113
00:05:44,044 --> 00:05:47,813
more about closures. We did closures in our first lecture,
114
00:05:47,815 --> 00:05:48,980
our first week of lecture.
115
00:05:48,982 --> 00:05:51,950
Remember we had those closures like $0 times $1.
116
00:05:51,952 --> 00:05:54,986
That's a closure, a closure is just an inline function.
117
00:05:54,988 --> 00:05:58,123
And one thing that is interesting about closures,
118
00:05:58,125 --> 00:06:00,659
they are stored in the heap as well, okay?
119
00:06:00,661 --> 00:06:04,363
So closures are essentially a reference type, okay.
120
00:06:04,365 --> 00:06:05,997
Cuz you remember that closures are just functions,
121
00:06:05,999 --> 00:06:10,936
functions are types, or just normal types in Swift. So,
122
00:06:10,938 --> 00:06:11,470
they're stored in the heap.
123
00:06:11,472 --> 00:06:14,706
Now it has interesting ramifications for the way
124
00:06:14,708 --> 00:06:19,044
things work, because inside of a closure, you can reference
125
00:06:19,046 --> 00:06:23,115
all kinds of variables that were in the scope around it.
126
00:06:23,117 --> 00:06:25,650
Okay, cuz remember it's an inline function, so
127
00:06:25,652 --> 00:06:29,187
you might reference variables that are local variables in
128
00:06:29,189 --> 00:06:31,289
that inline, in the function you're in,
129
00:06:31,291 --> 00:06:34,393
or maybe instance variable, properties of the class,
130
00:06:34,395 --> 00:06:37,396
if you're inside a method you declare the closure.
131
00:06:37,398 --> 00:06:40,465
It's perfectly legal to access all of those things, and not
132
00:06:40,467 --> 00:06:43,435
to only access them, but you can access them read-write.
133
00:06:43,437 --> 00:06:45,637
Okay, well now we know, from the calculator,
134
00:06:45,639 --> 00:06:49,007
that sometimes a closure lives a long time, maybe it gets put
135
00:06:49,009 --> 00:06:52,544
in a dictionary, like we did in the calculator, right. And
136
00:06:52,546 --> 00:06:54,746
so that dictionary can live for a long, long time.
137
00:06:54,748 --> 00:06:57,516
And that closure keeps getting pulled out and called,
138
00:06:57,518 --> 00:07:00,419
called, so what happens if you put a local variable,
139
00:07:00,421 --> 00:07:04,122
you captured a local variable In one of these closures and
140
00:07:04,124 --> 00:07:04,156
you put it in there.
141
00:07:04,158 --> 00:07:06,057
Well that local variable has to be kept around too,
142
00:07:06,059 --> 00:07:10,529
and Swift automatically does this, okay? Anything that gets
143
00:07:10,531 --> 00:07:14,032
captured inside a closure that you use inside a closure
144
00:07:14,034 --> 00:07:17,269
gets moved, or if it already is in the heap, it gets
145
00:07:17,271 --> 00:07:19,838
a strong pointer to it from the closure, otherwise, it
146
00:07:19,840 --> 00:07:22,507
gets moved to the heap. Okay, so everything get in the,
147
00:07:22,509 --> 00:07:24,876
stays in the heap, okay? Everything that closure and
148
00:07:24,878 --> 00:07:26,611
everything it references internally in its
149
00:07:26,613 --> 00:07:31,450
implementation, all of that gets in the heap, okay? So,
150
00:07:31,452 --> 00:07:34,453
what is the problem with that? Well, there's no problem,
151
00:07:34,455 --> 00:07:36,988
it generally just kind of works how you would think,
152
00:07:36,990 --> 00:07:39,424
except for memory cycles. Okay, so
153
00:07:39,426 --> 00:07:42,127
let's go through why a memory cycle can be created and
154
00:07:42,129 --> 00:07:44,996
what's bad about memory cycles. Okay, what's bad about
155
00:07:44,998 --> 00:07:47,466
them is, you have a closure pointing to an object, that
156
00:07:47,468 --> 00:07:50,335
object is pointing back to the closure, they're pointing to
157
00:07:50,337 --> 00:07:53,038
each other, they have strong references to each other.
158
00:07:53,040 --> 00:07:56,208
How can they ever leave the heap? Never. Right.
159
00:07:56,210 --> 00:07:57,476
Because they each are pointing to each other.
160
00:07:57,478 --> 00:08:00,045
They always are gonna maintain a strong pointer to each
161
00:08:00,047 --> 00:08:03,181
other. There's no way, unless you set one of those pointers
162
00:08:03,183 --> 00:08:04,916
to nil, okay, or something else.
163
00:08:04,918 --> 00:08:07,853
There's no way they can stop pointing to each other. Okay?
164
00:08:07,855 --> 00:08:10,088
So let's see how this can happen with the calculator.
165
00:08:10,090 --> 00:08:12,457
Let's say with my CalculatorBrain I added
166
00:08:12,459 --> 00:08:16,595
a new method called addUnary function that took a symbol
167
00:08:16,597 --> 00:08:18,029
and a operation, right.
168
00:08:18,031 --> 00:08:20,232
A function that takes a double or turns to a double.
169
00:08:20,234 --> 00:08:22,467
And basically I'm making it so that the user of my
170
00:08:22,469 --> 00:08:24,970
CalculatorBrain can add their own functions. Right now our
171
00:08:24,972 --> 00:08:27,405
CalculatorBrain just has this built-in table of functions
172
00:08:27,407 --> 00:08:29,741
and operations. Well what if I added this method and
173
00:08:29,743 --> 00:08:32,377
I just let people add them? That would be really cool,
174
00:08:32,379 --> 00:08:36,448
right? And so let's say we have our View Controller. And
175
00:08:36,450 --> 00:08:38,984
we wanted to add a Unary function which is the same
176
00:08:38,986 --> 00:08:42,587
as square root except for that it would turn the display in
177
00:08:42,589 --> 00:08:48,093
the calculator red. Okay? So I call it red square root, okay.
178
00:08:48,095 --> 00:08:48,326
It's just square root but
179
00:08:48,328 --> 00:08:51,463
it's gonna turn the thing red when the result comes back.
180
00:08:51,465 --> 00:08:54,099
Well how would we do that? Okay, well we would just call
181
00:08:54,101 --> 00:08:57,202
this addUnaryOperation. Now one thing I just wanna
182
00:08:57,204 --> 00:08:58,737
remind you a little bit of the syntax here.
183
00:08:58,739 --> 00:09:02,073
You see here's UnaryOperation, it has two arguments.
184
00:09:02,075 --> 00:09:03,542
There's the symbol, okay.
185
00:09:03,544 --> 00:09:07,612
Here's the operation which is this closure. Of course,
186
00:09:07,614 --> 00:09:10,749
because of type inference I don't, well, first of all,
187
00:09:10,751 --> 00:09:16,121
I don't need to do this comma second. Second argument.
188
00:09:16,123 --> 00:09:19,524
Here, I can actually put this closure after the end, right?
189
00:09:19,526 --> 00:09:22,027
So I can take this second argument and
190
00:09:22,029 --> 00:09:22,494
put it after the end.
191
00:09:22,496 --> 00:09:24,996
Remember that's the trailing closure syntax,
192
00:09:24,998 --> 00:09:25,764
everyone remember this?
193
00:09:25,766 --> 00:09:28,833
Okay since the closure is the last argument we can do this.
194
00:09:28,835 --> 00:09:32,070
Also I don't need any of this type stuff in here because it
195
00:09:32,072 --> 00:09:34,906
can infer that. Okay, so I can just take that away and
196
00:09:34,908 --> 00:09:37,242
use $0 down here. Okay, everyone remember that?
197
00:09:37,244 --> 00:09:40,812
All right, so here I am adding this UnaryOperation.
198
00:09:40,814 --> 00:09:43,415
Notice that it sets the display color.
199
00:09:43,417 --> 00:09:46,051
This is in my View Controller, my calculator View Controller,
200
00:09:46,053 --> 00:09:48,753
right? It sets the display color to red when it does
201
00:09:48,755 --> 00:09:51,022
the square root. Okay, this is gonna be UnaryOperation.
202
00:09:51,024 --> 00:09:53,091
Everybody understand what this is doing?
203
00:09:53,093 --> 00:09:54,426
It's really super simple. Okay,
204
00:09:54,428 --> 00:09:56,428
well this code will not compile. Okay,
205
00:09:56,430 --> 00:09:59,364
why will this code not compile? It's interesting.
206
00:09:59,366 --> 00:10:01,199
It's because, and when you look at the warning,
207
00:10:01,201 --> 00:10:02,968
it's gonna say, you have to put self.
208
00:10:02,970 --> 00:10:06,471
In front of this. Which normally you don't have to do,
209
00:10:06,473 --> 00:10:08,974
you can just reference your own properties with display,
210
00:10:08,976 --> 00:10:11,676
why do you have to put self.? Well you have to put self.
211
00:10:11,678 --> 00:10:15,280
There cuz the compiler wants you to realize that this
212
00:10:15,282 --> 00:10:20,852
closure is going to capture this self. And
213
00:10:20,854 --> 00:10:22,921
keep a strong pointer to it forever, for
214
00:10:22,923 --> 00:10:24,856
as long as this closure lives. Okay? And
215
00:10:24,858 --> 00:10:27,759
this closure's gonna be put in a dictionary in another class
216
00:10:27,761 --> 00:10:30,795
so it's gonna live a long time. It's gonna capture self.
217
00:10:30,797 --> 00:10:33,465
So self now has a strong point to it, it can never leave
218
00:10:33,467 --> 00:10:37,736
the heap until until this closure leaves the heap. Okay?
219
00:10:37,738 --> 00:10:40,472
But this closure is never gonna leave the heap because
220
00:10:40,474 --> 00:10:44,342
it's in the CalculatorBrain which the View Controller has
221
00:10:44,344 --> 00:10:47,278
a strong point or two. So, who will leave the,
222
00:10:47,280 --> 00:10:50,715
the heap first? Neither of them, okay? So they're stuck.
223
00:10:50,717 --> 00:10:52,917
So, that's why swift forces you put that there so
224
00:10:52,919 --> 00:10:56,221
you realize that you are implicitly capturing and, and
225
00:10:56,223 --> 00:10:59,457
making a strong point or two self. All right?
226
00:10:59,459 --> 00:11:02,794
Now, how do we deal with this? How do we break this loop, so
227
00:11:02,796 --> 00:11:05,430
that we can have these things leave the heap normally?
228
00:11:05,432 --> 00:11:07,766
Okay? There's a couple ways we can do it.
229
00:11:07,768 --> 00:11:10,702
First, we have to realize that there's a cool syntax in
230
00:11:10,704 --> 00:11:14,839
swift. That allows you to, declare variables to
231
00:11:14,841 --> 00:11:17,942
use inside your closure. Right at the beginning. All you do
232
00:11:17,944 --> 00:11:21,413
is a square bracket tier and you just put the names
233
00:11:21,415 --> 00:11:22,514
of the variables you want and
234
00:11:22,516 --> 00:11:25,483
their initial values. Okay? Put it right here.
235
00:11:25,485 --> 00:11:30,455
For example, I can create a variable called, me. M-E.
236
00:11:30,457 --> 00:11:33,925
And, I'm gonna set it to self. Okay. And so
237
00:11:33,927 --> 00:11:36,861
me is now local variable only inside this closure and
238
00:11:36,863 --> 00:11:41,066
its value is self. Okay? And sure enough look I can change
239
00:11:41,068 --> 00:11:43,835
self right there to be me because me is just a local
240
00:11:43,837 --> 00:11:48,339
variable. But this will have no effect on the the closure
241
00:11:48,341 --> 00:11:51,309
problem because me is still self and it still gonna make
242
00:11:51,311 --> 00:11:54,746
a strong point in there. To self and hold it in memory.
243
00:11:54,748 --> 00:11:58,516
But I can use those unow, those unowned weak and strong
244
00:11:58,518 --> 00:12:02,454
things with this variable, so I'm gonna say unowned me =
245
00:12:02,456 --> 00:12:07,125
self. Now, we've broken that cycle. Okay, because unowned
246
00:12:07,127 --> 00:12:10,261
means don't reference count this, which means that swift
247
00:12:10,263 --> 00:12:14,566
is not going to make a strong pointer to this me in this
248
00:12:14,568 --> 00:12:17,202
closure. So now this closure does not point strongly to
249
00:12:17,204 --> 00:12:19,904
self. Self still points strongly to this closure
250
00:12:19,906 --> 00:12:22,640
through the CalculatorBrain through the dictionary.
251
00:12:22,642 --> 00:12:26,144
To it, okay. But it does not now strongly point back to.
252
00:12:26,146 --> 00:12:28,213
It's not gonna keep the View Controller in memory.
253
00:12:28,215 --> 00:12:32,383
Now the danger here, is if this closure lived longer than
254
00:12:32,385 --> 00:12:34,586
the View Controller lived, which is pretty much
255
00:12:34,588 --> 00:12:36,755
impossible because this is in the dictionary that's
256
00:12:36,757 --> 00:12:38,790
in the brain that the controller owned. But
257
00:12:38,792 --> 00:12:42,026
if some wackiness happened and this lived longer and we tried
258
00:12:42,028 --> 00:12:44,596
to execute this longer after the View Controller is gone.
259
00:12:44,598 --> 00:12:47,899
This will crash. Okay? Because me would be pointing
260
00:12:47,901 --> 00:12:50,368
to something that got free from the heat. But
261
00:12:50,370 --> 00:12:53,371
another way to do it besides unowned is weak. So
262
00:12:53,373 --> 00:12:57,175
we could make self weak, okay? This will work great.
263
00:12:57,177 --> 00:12:58,643
This will also break the cycle.
264
00:12:58,645 --> 00:13:01,679
Because weak things don't keep things in the heap. However,
265
00:13:01,681 --> 00:13:06,417
self inside this closure now becomes what?
266
00:13:06,419 --> 00:13:11,389
An optional UIViewController. Okay, so this self right here,
267
00:13:11,391 --> 00:13:13,525
this local variable self has the same name but
268
00:13:13,527 --> 00:13:16,628
it's a different variable than the outer self. Okay?
269
00:13:16,630 --> 00:13:19,531
And this is weak so this will not work, anymore. This won't
270
00:13:19,533 --> 00:13:23,368
compile because display is not a message you can send to
271
00:13:23,370 --> 00:13:26,371
an optional, you can only send it to our View Controller;
272
00:13:26,373 --> 00:13:28,940
calculator View Controller. So how do you fix this?
273
00:13:28,942 --> 00:13:32,644
Well the simplest way is to use this optional changing and
274
00:13:32,646 --> 00:13:33,912
just put a question mark in here.
275
00:13:33,914 --> 00:13:35,947
Remember if you put a question mark on an optional,
276
00:13:35,949 --> 00:13:39,551
on the left hand side of an equal sign, it means if any of
277
00:13:39,553 --> 00:13:42,187
these things or the question mark are nil, just ignore this
278
00:13:42,189 --> 00:13:46,558
whole line. Okay? Bailout. So this is perfect. Weak self.
279
00:13:46,560 --> 00:13:49,360
If this self ever were nil, this would just bail out and
280
00:13:49,362 --> 00:13:53,398
not do it. Okay? So now this code would work even
281
00:13:53,400 --> 00:13:56,668
if the View Controller got thrown out of the heap. And
282
00:13:56,670 --> 00:13:57,435
since this is weak,
283
00:13:57,437 --> 00:14:00,538
we're using a weak self in here, it won't keep the,
284
00:14:00,540 --> 00:14:02,807
the View Controller in the heap. Now,
285
00:14:02,809 --> 00:14:06,144
when we do this weakSelf, here, we almost always declare
286
00:14:06,146 --> 00:14:10,248
a different name here. Almost always. WeakSelf. Okay? So
287
00:14:10,250 --> 00:14:12,851
weak variable weakSelf = self, and then we'll
288
00:14:12,853 --> 00:14:15,220
put that in there. And that tells people reading our code,
289
00:14:15,222 --> 00:14:16,855
yeah, I understand I'm making this weak. So
290
00:14:16,857 --> 00:14:22,360
don't worry about that, that possible memory cycle. Okay so
291
00:14:22,362 --> 00:14:23,161
that's closure in memory cycle.
292
00:14:23,163 --> 00:14:26,197
I'm gonna do a demo on this, okay. Which is I'm gonna do
293
00:14:26,199 --> 00:14:29,000
that red square root and let's see what this looks like
294
00:14:29,002 --> 00:14:34,072
in the calculator. To do this. All right so
295
00:14:34,074 --> 00:14:37,242
I'm going back to our calculator
296
00:14:39,880 --> 00:14:42,614
[INAUDIBLE] There we go. Okay so here's our calculator as we
297
00:14:42,616 --> 00:14:45,350
left it off. This is not your homework calculator. This is
298
00:14:45,352 --> 00:14:48,386
lecture four or whatever our last time. We just have
299
00:14:48,388 --> 00:14:52,624
this blank calculator right here. I'm going to now that
300
00:14:52,626 --> 00:14:55,760
you're comfortable with things like navigation controllers.
301
00:14:55,762 --> 00:14:58,863
I'm going to actually add another View Controller here.
302
00:14:58,865 --> 00:15:05,236
Okay, and I'm gonna put a button in it. Oops,
303
00:15:05,238 --> 00:15:08,373
make this bigger. Put a button in this. I'm gonna make it
304
00:15:08,375 --> 00:15:12,377
bigger. We'll make it 40 point, I would say. Okay,
305
00:15:12,379 --> 00:15:16,881
I'm gonna change the title to be Calculate.
306
00:15:17,050 --> 00:15:21,920
Okay, and this button is going to cause this calculator here
307
00:15:21,922 --> 00:15:24,255
to appear. So let's go ahead and Reset to
308
00:15:24,257 --> 00:15:26,925
Suggested Constraints. Notice when I did that, look,
309
00:15:26,927 --> 00:15:28,927
I got all kinds of constraints I didn't really want.
310
00:15:28,929 --> 00:15:31,596
You see, because, that's cuz I didn't have this thing be
311
00:15:31,598 --> 00:15:35,099
in its natural size. So let's go over to the size inspector
312
00:15:35,101 --> 00:15:37,302
over here and look at our constraints. And
313
00:15:37,304 --> 00:15:39,103
we see, it's constraining the width to be 204.
314
00:15:39,105 --> 00:15:42,674
I don't want that. I wanna it to be its natural width. So
315
00:15:42,676 --> 00:15:45,376
I'm just gonna select this constraint, constraint and
316
00:15:45,378 --> 00:15:47,245
hit the Delete. Same thing here,
317
00:15:47,247 --> 00:15:50,448
it's constraining it to the top by some magic number 203.
318
00:15:50,450 --> 00:15:54,085
I don't want that because I want it to be aligned center x
319
00:15:54,087 --> 00:15:55,753
and y. So it's okay in here just to go ahead and
320
00:15:55,755 --> 00:15:58,589
delete constraints you don't work want. Now of course now
321
00:15:58,591 --> 00:16:01,893
this is yellow, so we're gonna go down here and
322
00:16:01,895 --> 00:16:05,330
say click on this. All right. And go down here and
323
00:16:05,332 --> 00:16:09,334
say update frames, and that's gonna move the frames to where
324
00:16:09,336 --> 00:16:12,904
they should be, and now everything is cute. Okay,
325
00:16:12,906 --> 00:16:15,573
a little quick review there of constraints.
326
00:16:15,575 --> 00:16:18,409
Also, I don't want the entry point to be my calculator,
327
00:16:18,411 --> 00:16:19,811
I want it to be this thing. Okay,
328
00:16:19,813 --> 00:16:22,447
this is going to be my first thing, and I'm going to put it
329
00:16:22,449 --> 00:16:25,850
inside of a navigation controller. So, I'm going to
330
00:16:25,852 --> 00:16:28,987
put this inside a navigation controller with embed in,
331
00:16:28,989 --> 00:16:34,892
oops, select this again, embed in navigation controller,
332
00:16:34,894 --> 00:16:39,464
and now I have a nice little UI here. That has a navigation
333
00:16:39,466 --> 00:16:41,432
controller in this and I'm going to make it so
334
00:16:41,434 --> 00:16:43,101
that this button, when it's clicked,
335
00:16:43,103 --> 00:16:47,138
segues to show a calculator. Okay, so let's do that.
336
00:16:47,140 --> 00:16:50,441
Control, drag over here, we're going to do a show because
337
00:16:50,443 --> 00:16:52,810
we're inside a navigation controller. This is not for
338
00:16:52,812 --> 00:16:55,713
iPad, this is for iPhone only, so I'm just going to do Show,
339
00:16:55,715 --> 00:16:59,050
and here's our show now. I'm not going to set an identifier
340
00:16:59,052 --> 00:17:01,786
here because I'm not gonna prepare that calculator. I'm
341
00:17:01,788 --> 00:17:04,856
just gonna let it come up in whatever its state is. I'm not
342
00:17:04,858 --> 00:17:08,393
gonna prepare it. So, no need to put an identifier here.
343
00:17:08,395 --> 00:17:13,131
Okay, so this is what our UI looks like. All right, so
344
00:17:13,133 --> 00:17:16,834
let's go ahead and run this, see what this looks like.
345
00:17:28,681 --> 00:17:29,881
Okay, so here's our calculator.
346
00:17:29,883 --> 00:17:32,717
We're inside a navigation controller. Hit Calculate,
347
00:17:32,719 --> 00:17:36,654
it shows it, go back, hit calculate, it shows it.
348
00:17:36,656 --> 00:17:38,089
Now remember in a navigation controller,
349
00:17:38,091 --> 00:17:41,692
every time we segue what happens? We get a new MVC. So,
350
00:17:41,694 --> 00:17:43,995
we're getting a new calculator MVC every single time.
351
00:17:43,997 --> 00:17:47,498
In fact, I'm going to prove that to you by going back here
352
00:17:47,500 --> 00:17:48,666
to my calculator view controller.
353
00:17:48,668 --> 00:17:51,936
And I'm going to have it print every time it creates a new
354
00:17:51,938 --> 00:17:54,839
one and every time it leaves the heap. Okay? So,
355
00:17:54,841 --> 00:17:56,908
let's start with every time that it creates a new one,
356
00:17:56,910 --> 00:18:00,144
we can use viewDidLoad() for that, okay?
357
00:18:00,146 --> 00:18:00,711
So, here's viewDidLoad().
358
00:18:00,713 --> 00:18:03,881
Remember in our ViewController life-cycles we always need to
359
00:18:03,883 --> 00:18:07,185
call super, so I'm calling super.viewDidLoad().
360
00:18:07,187 --> 00:18:10,121
I'm going to create a global variable here,
361
00:18:10,123 --> 00:18:13,191
which is my calculatorCount Start out as zero.
362
00:18:13,193 --> 00:18:16,327
That's how many calculator instances are currently
363
00:18:16,329 --> 00:18:19,030
existing in the world. So when I do a viewDidLoa,
364
00:18:19,032 --> 00:18:22,900
which I know happens only once per MVC, I'm going to take my
365
00:18:22,902 --> 00:18:25,536
calculator account and increment it by one.
366
00:18:25,538 --> 00:18:28,439
And then I'm just going to print out, loaded up
367
00:18:28,441 --> 00:18:31,809
a new calculator and I'll tell you what the account is at
368
00:18:31,811 --> 00:18:37,415
that time, and that's going to be calculator count, okay?
369
00:18:37,417 --> 00:18:40,418
So, every time we create a new MBC where it's gonna get
370
00:18:40,420 --> 00:18:42,553
a message, now how do we find out
371
00:18:42,555 --> 00:18:45,690
when something leaves the heap? Anyone do the homework,
372
00:18:45,692 --> 00:18:49,861
the reading homework and tell me? Special method?
373
00:18:49,863 --> 00:18:54,832
What? Nobody? My gosh, okay.
374
00:18:54,834 --> 00:18:59,337
Deinit. Okay, this special thing deinit gets called just
375
00:18:59,339 --> 00:19:02,540
before you leave the heap, okay? So, in here I'm just
376
00:19:02,542 --> 00:19:05,543
going to say the calculator Count minus equals one because
377
00:19:05,545 --> 00:19:09,213
this is leaving the heap, and I'm going to do a print here,
378
00:19:09,215 --> 00:19:14,519
I'm gonna say calculator left the heap and
379
00:19:14,521 --> 00:19:16,187
now show our count. Okay? So now,
380
00:19:16,189 --> 00:19:19,524
when I run, we're going to see our count of calculators.
381
00:19:19,526 --> 00:19:22,393
And what happens to it as we keep clicking that calculator
382
00:19:22,395 --> 00:19:24,829
button, and keep having new MVCs, all right? So,
383
00:19:24,831 --> 00:19:26,397
here we go, let's calculate. There it is,
384
00:19:26,399 --> 00:19:29,767
loaded up a new calculator, count equals 1, looks good.
385
00:19:29,769 --> 00:19:33,371
We go back, calculator left the heap, count is 0,
386
00:19:33,373 --> 00:19:38,709
go back again, loaded up a new calculator, count is 1.
387
00:19:39,679 --> 00:19:43,181
Go back, count to 0. So, this is proving what I was
388
00:19:43,183 --> 00:19:45,583
saying before. That every time we segue,
389
00:19:45,585 --> 00:19:47,451
it's creating a new one. Every time we go back,
390
00:19:47,453 --> 00:19:51,656
it throws it out. Everybody believe that now? Okay. So
391
00:19:51,658 --> 00:19:55,660
now, let's go ahead and do our red square root. Okay?
392
00:19:55,662 --> 00:19:57,895
Let's do our red square root button. I told you,
393
00:19:57,897 --> 00:19:59,897
I was gonna go to calculate the brain and
394
00:19:59,899 --> 00:20:04,268
add a new public function here called addUnaryOperation. It's
395
00:20:04,270 --> 00:20:08,172
gonna take a symbol which is a string, and it's gonna take in
396
00:20:08,174 --> 00:20:10,942
operation that goes along with it, which takes a double and
397
00:20:10,944 --> 00:20:16,013
returns a double. Okay? And adding a Unary operation
398
00:20:16,015 --> 00:20:18,382
is really easy because all we all have
399
00:20:18,384 --> 00:20:21,686
to do is add an Unary operation to this table right
400
00:20:21,688 --> 00:20:25,489
here, okay? So, I'm just gonna say operation some symbol,
401
00:20:25,491 --> 00:20:28,826
okay? This is the symbol that the person wants this under.
402
00:20:28,828 --> 00:20:31,696
We're gonna set this equal to the operation.
403
00:20:31,698 --> 00:20:32,563
We can't really do that, though,
404
00:20:32,565 --> 00:20:36,300
because we have e nums in here. So we need to wrap
405
00:20:36,302 --> 00:20:41,939
a operation dot unary operation around this thing,
406
00:20:41,941 --> 00:20:45,243
okay, and the associated value is a double takes a double so
407
00:20:45,245 --> 00:20:51,015
that works. And that's it. So, I just added a unary operation
408
00:20:51,017 --> 00:20:55,620
to my operation table. Okay. Now that we have that feature,
409
00:20:55,622 --> 00:20:57,888
then let's go back to our view controller and
410
00:20:57,890 --> 00:20:59,690
use it. We might viewDidLoad.
411
00:20:59,692 --> 00:21:04,895
Right here. I'm going to add a unary operation, oops,
412
00:21:04,897 --> 00:21:10,701
add, and my brain, I'm gonna add a unary operation. And
413
00:21:10,703 --> 00:21:14,071
I'm gonna say I'm gonna have it symbol be the letter Z, but
414
00:21:14,073 --> 00:21:18,542
we could make it a nice red dot and a square sign,
415
00:21:18,544 --> 00:21:20,144
but faster to type z, and
416
00:21:20,146 --> 00:21:23,814
the function is going to be a closure. Okay.
417
00:21:23,816 --> 00:21:26,851
And again, I can use the trailing closure notation
418
00:21:26,853 --> 00:21:30,955
to do that right here. Okay, and as we said in the slides,
419
00:21:30,957 --> 00:21:35,893
I'm just going to set my displays text color
420
00:21:36,029 --> 00:21:39,597
equal to UI color, red color. And
421
00:21:39,599 --> 00:21:43,000
then I'm gonna return the square root of $0.
422
00:21:43,002 --> 00:21:46,504
Okay, and as promised I have an error right here.
423
00:21:46,506 --> 00:21:49,307
It says reference to property 'display' in closure requires
424
00:21:49,309 --> 00:21:53,177
explicit 'self' to make make capture semantics explicit.
425
00:21:53,179 --> 00:21:56,414
Okay? Hopefully that with that warning it makes perfect sense
426
00:21:56,416 --> 00:21:59,483
now. Links wants you to be clear that you are going
427
00:21:59,485 --> 00:22:02,153
to capture self, it's gonna get a strong pointer to it.
428
00:22:02,155 --> 00:22:03,821
So, I'm gonna do the fix right here,
429
00:22:03,823 --> 00:22:07,291
which is insert self, fixes that problem.
430
00:22:07,293 --> 00:22:08,259
And now we have [INAUDIBLE] operation. So,
431
00:22:08,261 --> 00:22:11,929
let's go to our story board and add that somewhere here.
432
00:22:11,931 --> 00:22:17,301
We'll replace, how about we replace our old square root
433
00:22:17,303 --> 00:22:19,670
with our new z square root and
434
00:22:19,672 --> 00:22:25,109
run? All right, so here we are,
435
00:22:25,111 --> 00:22:28,446
okay, I'm gonna hit calculate, loaded up a new calculator.
436
00:22:28,448 --> 00:22:32,350
Let's do 81 red square root, it's working perfectly, okay.
437
00:22:32,352 --> 00:22:35,720
It took the square root and it turned the display red. And so
438
00:22:35,722 --> 00:22:40,891
let's go back. We did't lo--, we didn't leave the heap,
439
00:22:40,893 --> 00:22:42,460
that's weird. Le's go back again. Wait,
440
00:22:42,462 --> 00:22:46,430
now we have two calculators, wha's going on, or three.
441
00:22:46,432 --> 00:22:48,299
No, we're collecting calculators.
442
00:22:48,301 --> 00:22:51,235
Now, these calculators are not very big, they do't have
443
00:22:51,237 --> 00:22:53,270
a lot of storage so probably not gonna kill us. But
444
00:22:53,272 --> 00:22:56,741
what if these had an image? Right or even like a video or
445
00:22:56,743 --> 00:22:58,743
something in there, and w're collecting these things, and
446
00:22:58,745 --> 00:23:02,346
they were building up in the heap never to be freed. Okay,
447
00:23:02,348 --> 00:23:05,449
that would be a problem. Okay, so how can we make it so that
448
00:23:05,451 --> 00:23:08,419
it goes back to where it was where when we left it would
449
00:23:08,421 --> 00:23:12,189
free up? Well all we have to do is go here to this and
450
00:23:12,191 --> 00:23:16,026
fix a problem that this self is being held in memory so
451
00:23:16,028 --> 00:23:19,563
that a view controller can't be freed when it comes off.
452
00:23:19,565 --> 00:23:22,032
Okay? So we can do exactly what we talked about before.
453
00:23:22,034 --> 00:23:26,704
We can create, for example, unowned. Me equals self, how
454
00:23:26,706 --> 00:23:29,573
about that one? Okay? Don't forget the in right here.
455
00:23:29,575 --> 00:23:32,543
Okay? All this stuff has to go before the in, all right?
456
00:23:32,545 --> 00:23:34,745
A closure in. So if we do that, and
457
00:23:34,747 --> 00:23:38,282
now we say, me, okay? So me, right here, is unowned.
458
00:23:38,284 --> 00:23:40,451
So, it's not going to keep anything in the heap.
459
00:23:40,453 --> 00:23:42,520
All right, and we know it's safe here because we're
460
00:23:42,522 --> 00:23:44,989
the view controller. We own the brain. So, there's no way
461
00:23:44,991 --> 00:23:50,094
that we're gonna get thrown out before the brain. So,
462
00:23:50,096 --> 00:23:53,597
let's run it again. So, here's calculate,
463
00:23:53,599 --> 00:23:57,902
81 square root, okay, the red is still working,
464
00:23:57,904 --> 00:24:02,506
back, left the heap Okay, working good. See that?
465
00:24:02,508 --> 00:24:06,677
All right, now another way we could've done it here instead
466
00:24:06,679 --> 00:24:10,781
of unowned is we could've said weakSelf = self, okay.
467
00:24:10,783 --> 00:24:12,716
So, this is a weak variable right here.
468
00:24:12,718 --> 00:24:16,120
It's going to be an optional. In fact, if I look at it, see,
469
00:24:16,122 --> 00:24:17,588
it's an optional view controller,
470
00:24:17,590 --> 00:24:19,390
you see that? Optional view controller, so
471
00:24:19,392 --> 00:24:22,626
right here it's complaining, you can't send display to,
472
00:24:22,628 --> 00:24:25,529
oops, this was weakSelf, it would be complaining. You
473
00:24:25,531 --> 00:24:29,300
can't send display to weakSelf because it's an optional, see?
474
00:24:29,302 --> 00:24:32,336
And it's asking do you want to put exclamation point? But
475
00:24:32,338 --> 00:24:36,373
I'm gonna be more conservative and just put a question mark
476
00:24:36,375 --> 00:24:38,476
right here, just in case this ever did get to be nil,
477
00:24:38,478 --> 00:24:41,645
I just want this line of code not to happen. Okay, so,
478
00:24:41,647 --> 00:24:50,087
let's see if that fixes our problem. All right, Calculate,
479
00:24:50,089 --> 00:24:54,158
Back, Calculate, Back, Calculate, and Square root.
480
00:24:54,160 --> 00:24:58,629
Still turning it red. Okay, now, you are probably going to
481
00:24:58,631 --> 00:25:00,998
be using functions. I would imagine your assignment three
482
00:25:01,000 --> 00:25:05,970
and closures too probably. I'm not going to require that you
483
00:25:05,972 --> 00:25:07,671
not have memory loops. In other words, you are allowed
484
00:25:07,673 --> 00:25:10,908
to have these memory cycles in your assignment three, but
485
00:25:10,910 --> 00:25:12,710
if you want to try and fix it in your assignment three,
486
00:25:12,712 --> 00:25:15,980
you are welcome to. Okay? I never ask you to do anything
487
00:25:15,982 --> 00:25:17,481
in your assignment that I didn't teach you
488
00:25:17,483 --> 00:25:18,916
in class before I gave you the assignment.
489
00:25:18,918 --> 00:25:21,185
So this is after I gave you the assignment so
490
00:25:21,187 --> 00:25:23,988
it's totally optional if you want to fix it. But
491
00:25:23,990 --> 00:25:27,057
otherwise it's likely you probably will have a memory
492
00:25:27,059 --> 00:25:30,394
cycle in your assignment three. It's possible you won't
493
00:25:30,396 --> 00:25:34,999
but likely you would if you don't do this. Okay?
494
00:25:35,001 --> 00:25:37,902
Any questions about that? All right,
495
00:25:37,904 --> 00:25:44,708
back to our slides. Okay, the next thing we're talk
496
00:25:44,710 --> 00:25:48,746
about is a cool little Swift feature called extensions.
497
00:25:48,748 --> 00:25:52,416
Okay, extensions allow you to add methods and
498
00:25:52,418 --> 00:25:56,520
properties to other classes, even if you don't have
499
00:25:56,522 --> 00:25:59,023
the source code to the other classes. So, for
500
00:25:59,025 --> 00:26:01,892
example you can add it to classes in UIkit.
501
00:26:01,894 --> 00:26:05,863
Okay. You could add a method to UIbutton or, in this case,
502
00:26:05,865 --> 00:26:09,700
I've added a method to UIViewController. Okay. So
503
00:26:09,702 --> 00:26:12,636
when I add this method, it's available all throughout my
504
00:26:12,638 --> 00:26:16,473
app. All view controllers will now have this method. Okay,
505
00:26:16,475 --> 00:26:19,343
now what this is example this method I did right here? Well,
506
00:26:19,345 --> 00:26:21,845
remember and when we're doing prepare for segway and
507
00:26:21,847 --> 00:26:25,416
we wanna handle the case where we put the detail of the split
508
00:26:25,418 --> 00:26:28,218
view controller into a navigation controller. And so,
509
00:26:28,220 --> 00:26:31,221
when the segue happens we have to look inside the navigation
510
00:26:31,223 --> 00:26:32,990
controller. Remember that? And we had this
511
00:26:32,992 --> 00:26:36,026
little code right here if let navcon equal
512
00:26:36,028 --> 00:26:38,696
the destination ViewController as NavigationController,
513
00:26:38,698 --> 00:26:40,431
then return the visibleViewController
514
00:26:40,433 --> 00:26:41,165
remember all that business?
515
00:26:41,167 --> 00:26:43,968
Well I'm just gonna add that inside of a method here in
516
00:26:43,970 --> 00:26:46,937
ViewController called contentViewController. So,
517
00:26:46,939 --> 00:26:48,572
if I ask a ViewController what's your
518
00:26:48,574 --> 00:26:51,976
contentViewController? Well if it's a NavigationController
519
00:26:51,978 --> 00:26:54,712
it's gonna give me the visibleViewController. Okay?
520
00:26:54,714 --> 00:26:56,347
If it's any other ViewController it's looking
521
00:26:56,349 --> 00:26:59,283
to be itself cuz itself is the content.
522
00:26:59,285 --> 00:27:02,019
That way, whether it's in the ViewNavigationController or
523
00:27:02,021 --> 00:27:05,055
not, I'm gonna get the thing that I intend, the content
524
00:27:05,057 --> 00:27:08,125
that I'm trying to get at. Everyone understand that?
525
00:27:08,127 --> 00:27:10,494
So that makes you prepareForSegue instead of
526
00:27:10,496 --> 00:27:13,297
looking like this where you got this destination and
527
00:27:13,299 --> 00:27:15,799
all this you can actually make this into a one-liner.
528
00:27:15,801 --> 00:27:17,768
You can get rid of all this and make a one-liner. So,
529
00:27:17,770 --> 00:27:20,137
let's see if we can understand this. Ready? Here we go. We're
530
00:27:20,139 --> 00:27:24,141
going to save the Segue's destination ViewController,
531
00:27:24,143 --> 00:27:29,413
it's contentViewController, as, my ViewController.
532
00:27:29,415 --> 00:27:31,649
If I can do that, then I'm good to go. See, so,
533
00:27:31,651 --> 00:27:36,987
you see how this simplified this code really nicely. Now,
534
00:27:36,989 --> 00:27:39,056
extensions you got to be a little careful with.
535
00:27:39,058 --> 00:27:42,292
They could be abused. Okay. You start adding all
536
00:27:42,294 --> 00:27:44,595
these wacky methods to class, it makes no sense.
537
00:27:44,597 --> 00:27:47,598
You're still object on your programmers if you're gonna
538
00:27:47,600 --> 00:27:49,066
add a method to UIViewController,
539
00:27:49,068 --> 00:27:52,036
it better makes sense as a UIViewController method.
540
00:27:52,038 --> 00:27:54,672
Okay, it shouldn't be some calculator method,
541
00:27:54,674 --> 00:27:56,240
okay this has nothing to do with calculators.
542
00:27:56,242 --> 00:27:59,643
It has everything to do with UIViewControllers, okay.
543
00:27:59,645 --> 00:28:02,279
So, if you use extensions, use them wisely,
544
00:28:02,281 --> 00:28:06,750
use them in an object oriented fashion. Okay? Now,
545
00:28:06,752 --> 00:28:11,088
inside of an extension, notice that you can refer to self.
546
00:28:11,090 --> 00:28:13,691
Self of course means the UIViewController you sent
547
00:28:13,693 --> 00:28:17,761
this to. Just like self would mean if you had a method,
548
00:28:17,763 --> 00:28:20,197
in UIViewController that was in that class.
549
00:28:20,199 --> 00:28:25,202
Okay? Couple of interesting restrictions on
550
00:28:25,204 --> 00:28:28,305
extensions. By the way, you can extend classes,
551
00:28:28,307 --> 00:28:31,408
structs and enums. Okay, all of them are extensible.
552
00:28:31,410 --> 00:28:35,713
You can't re-implement any methods or properties. It's
553
00:28:35,715 --> 00:28:38,649
not re-implement thing, okay? It's an additional thing.
554
00:28:38,651 --> 00:28:41,118
You can add methods, but you can't re-implement.
555
00:28:41,120 --> 00:28:45,022
Okay? Also any properties that you add have to be computed
556
00:28:45,024 --> 00:28:47,157
properties like that content view controller was.
557
00:28:47,159 --> 00:28:50,861
You can't have any storage with your extension. Okay,
558
00:28:50,863 --> 00:28:54,098
extension is purely for adding code, no storage.
559
00:28:54,100 --> 00:28:57,601
All right, this feature I say is easily abused because you
560
00:28:57,603 --> 00:29:00,504
can add all kinds of non objected oriented methods and
561
00:29:00,506 --> 00:29:03,707
stuff like that, but actually this feature can be really
562
00:29:03,709 --> 00:29:06,877
powerful for organizing and structuring your code.
563
00:29:06,879 --> 00:29:10,447
Okay? And it's really beyond the scope for this class for
564
00:29:10,449 --> 00:29:13,417
me to show you how you could use extensions in terms of
565
00:29:13,419 --> 00:29:17,121
building your kind of code infrastructure in your app,
566
00:29:17,123 --> 00:29:18,455
but know that it's possible.
567
00:29:18,457 --> 00:29:21,125
Because if you go out there in the real world and
568
00:29:21,127 --> 00:29:25,229
you start building big apps, extensions are a great way to
569
00:29:25,231 --> 00:29:28,031
kind of divide up big complicated classes,
570
00:29:28,033 --> 00:29:32,269
into sensible sub-pieces that are each extensions.
571
00:29:32,271 --> 00:29:33,370
Also, if you have protocols,
572
00:29:33,372 --> 00:29:35,339
which I'm going to talk about later in this lecture,
573
00:29:35,341 --> 00:29:38,208
extensions are a great way to implement a protocol
574
00:29:38,210 --> 00:29:40,744
in a class, so you'll see what that means in a minute.
575
00:29:40,746 --> 00:29:43,747
Okay, for now you can use extensions in your homework or
576
00:29:43,749 --> 00:29:47,184
whatever, but use it more along the lines of the content
577
00:29:47,186 --> 00:29:48,318
view controller example right?
578
00:29:48,320 --> 00:29:50,621
Small utility functions that make sense,
579
00:29:50,623 --> 00:29:54,057
that kind of thing okay? Maybe for your final project if you
580
00:29:54,059 --> 00:29:57,861
want to read up on how you can use extensions to build your
581
00:29:57,863 --> 00:29:59,129
code base you can try it then,
582
00:29:59,131 --> 00:30:00,631
but on your homework assignments let's stick to the
583
00:30:00,633 --> 00:30:04,468
little stuff, okay? All right, so I mentioned protocols,
584
00:30:04,470 --> 00:30:08,405
let's talk about protocols. Protocols are a way to express
585
00:30:08,407 --> 00:30:12,743
an API more concisely, okay? Protocols are another type,
586
00:30:12,745 --> 00:30:14,545
their the last type I'm going to talk about, okay?
587
00:30:14,547 --> 00:30:16,413
We've talked about classes, enoms, strucks,
588
00:30:16,415 --> 00:30:19,049
now we're gonna talk about, and functions, those are also
589
00:30:19,051 --> 00:30:21,485
a type, now we're going to talk about protocols, okay,
590
00:30:21,487 --> 00:30:26,523
the last type. What a protocol does is it lets you have
591
00:30:26,525 --> 00:30:29,960
an API where instead of having to specify the full class or
592
00:30:29,962 --> 00:30:32,729
struct that you're going to be using to do whatever
593
00:30:32,731 --> 00:30:35,566
you are going to do. You can just specify what it is about
594
00:30:35,568 --> 00:30:39,403
that class or struct that you actually need, okay, and
595
00:30:39,405 --> 00:30:42,139
you do this with a protocol. And a protocol is simply
596
00:30:42,141 --> 00:30:46,810
a collection of method and property declarations. Okay,
597
00:30:46,812 --> 00:30:48,946
so it's just like a little bundle of methods,
598
00:30:48,948 --> 00:30:52,482
and you can have part of your API that says I want something
599
00:30:52,484 --> 00:30:54,785
that implements this bundle of methods. And
600
00:30:54,787 --> 00:30:57,588
it can be any kind of struct, enum, class, whatever as
601
00:30:57,590 --> 00:30:59,756
long as long as it implements those little methods.
602
00:30:59,758 --> 00:31:02,159
Okay, and that's what the protocol is all about.
603
00:31:02,161 --> 00:31:04,595
So, it's a type, anywhere you can use
604
00:31:04,597 --> 00:31:07,064
a type, you can use a protocol. So, any like I might
605
00:31:07,066 --> 00:31:10,200
have an argument to a function that takes a UIViewController,
606
00:31:10,202 --> 00:31:13,170
it could also take a protocol, okay, that has a few methods
607
00:31:13,172 --> 00:31:15,973
in UIViewController that I needed. So then it wouldn't be
608
00:31:15,975 --> 00:31:17,708
so restrictive and have to be a UIViewController,
609
00:31:17,710 --> 00:31:20,944
it could just be something that implements those methods.
610
00:31:20,946 --> 00:31:23,447
Okay, anywhere you can use a TYPE you can use a protocol.
611
00:31:23,449 --> 00:31:27,818
Okay, the implementation of a protocol happens in the class
612
00:31:27,820 --> 00:31:32,556
that implements the protocol. The class or struct or
613
00:31:32,558 --> 00:31:34,391
enum that implements the protocol. Okay,
614
00:31:34,393 --> 00:31:36,560
the protocol itself doesn't have any implementation.
615
00:31:36,562 --> 00:31:40,430
The protocol itself is just a declaration of the properties
616
00:31:40,432 --> 00:31:44,835
and methods. Okay? Now, it is possible however,
617
00:31:44,837 --> 00:31:50,173
to use extensions to implement some or all of a protocol.
618
00:31:50,175 --> 00:31:52,276
So, you just say extension, protocol,
619
00:31:52,278 --> 00:31:54,111
instead of saying extension class name.
620
00:31:54,113 --> 00:31:56,747
Whatever, you say extension protocol open curly brace and
621
00:31:56,749 --> 00:32:00,851
implement some of the methods. Or properties in the protocol.
622
00:32:00,853 --> 00:32:05,022
Okay, now protocols just like extensions can have
623
00:32:05,024 --> 00:32:08,125
no storage, okay. Protocol can have no storage and
624
00:32:08,127 --> 00:32:10,994
extensions can have no storage either. So there's no way to
625
00:32:10,996 --> 00:32:14,965
implement some of the protocol with any storage, no vars,
626
00:32:14,967 --> 00:32:18,802
okay. You can have vars but they have to be computed.
627
00:32:18,804 --> 00:32:22,906
Okay, so no storage in protocols or extensions.
628
00:32:22,908 --> 00:32:25,976
All right, so there's four aspects to using a protocol.
629
00:32:25,978 --> 00:32:27,811
One is the protocol declaration.
630
00:32:27,813 --> 00:32:29,746
That's where you declare what methods and
631
00:32:29,748 --> 00:32:30,981
properties are in this protocol.
632
00:32:30,983 --> 00:32:33,817
Then there's the declaration where a class, a struct or
633
00:32:33,819 --> 00:32:38,221
an enum says, I implement this protocol. So,
634
00:32:38,223 --> 00:32:39,656
it's not good enough if you're a class or
635
00:32:39,658 --> 00:32:42,292
struct enum to just implement the methods or
636
00:32:42,294 --> 00:32:42,426
the property there.
637
00:32:42,428 --> 00:32:44,928
You have to actually declare I implement this,
638
00:32:44,930 --> 00:32:47,531
then you have to go implement all the methods and
639
00:32:47,533 --> 00:32:50,701
properties as well. Okay, and we'll show you how to do that.
640
00:32:50,703 --> 00:32:53,437
Then there's the code that implements the protocol and
641
00:32:53,439 --> 00:32:58,442
that's usually going to be, in the class structure enum that
642
00:32:58,444 --> 00:32:59,576
said it was going to implement it.
643
00:32:59,578 --> 00:33:02,713
But it could also be in an extension, okay?
644
00:33:02,715 --> 00:33:04,681
It can either be an extension to the protocol or
645
00:33:04,683 --> 00:33:06,416
it could be an extension to the class, struct or
646
00:33:06,418 --> 00:33:09,586
enum. Okay? You could have implementation in there.
647
00:33:09,588 --> 00:33:10,921
Again, no storage though.
648
00:33:10,923 --> 00:33:12,856
So if you need storage to implement it,
649
00:33:12,858 --> 00:33:16,460
that's gonna have to be inside the class, struct or enum.
650
00:33:16,462 --> 00:33:18,929
All right. Optional methods in a protocol.
651
00:33:18,931 --> 00:33:21,732
This one's a little bit tricky, okay, in Swift,
652
00:33:21,734 --> 00:33:27,104
protocols all the properties, all the methods are required.
653
00:33:27,106 --> 00:33:30,507
If you say you implement that protocol, you must implement
654
00:33:30,509 --> 00:33:33,677
all of the protocol. You ca't just pick and choose.
655
00:33:33,679 --> 00:33:37,214
However in Objective C, it had something called protocols.
656
00:33:37,216 --> 00:33:40,817
Okay, and in Objective C things could be optional. So
657
00:33:40,819 --> 00:33:43,854
in Objective C, you could put the word optional in front of.
658
00:33:43,856 --> 00:33:45,922
So you could say like optional func, whatever.
659
00:33:45,924 --> 00:33:47,391
And it would mean that in this protocol,
660
00:33:47,393 --> 00:33:49,426
you could still say you implement this protocol and
661
00:33:49,428 --> 00:33:52,729
you don't have to implement this one. It's optional, okay?
662
00:33:52,731 --> 00:33:58,602
That was Objective C only. Now, I was used Objective C.
663
00:33:58,604 --> 00:33:59,836
It was written in Objective C and
664
00:33:59,838 --> 00:34:02,139
uses this optional protocol stuff a lot. Okay, and
665
00:34:02,141 --> 00:34:05,609
I'm gonna talk about how it uses it in a moment here. And
666
00:34:05,611 --> 00:34:08,979
so Swift has to have this mechanism. And so the way
667
00:34:08,981 --> 00:34:13,517
it does this is it lets you declare protocols to be @objc
668
00:34:13,519 --> 00:34:18,722
compliant. You just say @objc space protocol, whatever.
669
00:34:18,724 --> 00:34:22,492
And that means this protocol is an objc compliant one. And
670
00:34:22,494 --> 00:34:27,297
inside that kind of protocol, you can use the word optional
671
00:34:27,299 --> 00:34:31,468
in front of any of the funcs or vars in there. To say this
672
00:34:31,470 --> 00:34:33,870
is optional so that someone who implements that protocol
673
00:34:33,872 --> 00:34:37,407
does not have to implement those optional things, okay?
674
00:34:37,409 --> 00:34:39,509
This is used in iOS for something called delegation
675
00:34:39,511 --> 00:34:43,346
and we'll talk all about that. One thing that's interesting
676
00:34:43,348 --> 00:34:45,849
is that any optional protocol implementing class.
677
00:34:45,851 --> 00:34:48,151
So if you're class and it implements a protocol,
678
00:34:48,153 --> 00:34:50,887
an Objective C protocol. With these optional things must
679
00:34:50,889 --> 00:34:54,091
inherit from NSObject, so this is the second time I'm telling
680
00:34:54,093 --> 00:34:56,793
you. Sometimes a class can't, a Swift class cant
681
00:34:56,795 --> 00:34:59,896
inherit from nothing, you have to inherit from NSObject or
682
00:34:59,898 --> 00:35:03,733
from a class that inherits from NSObject. Okay? So
683
00:35:03,735 --> 00:35:05,802
cuz it's all part of this Objective C runtime and
684
00:35:05,804 --> 00:35:08,738
that class has to be available in the Objective C runtime.
685
00:35:08,740 --> 00:35:12,175
Just like the protocol has to be available in
686
00:35:12,177 --> 00:35:13,643
the Objective C runtime,
687
00:35:13,645 --> 00:35:17,481
okay? So that's the optional thing there.
688
00:35:17,483 --> 00:35:20,083
All right, so here is what the syntax looks like, here I'm
689
00:35:20,085 --> 00:35:24,154
declaring a protocol, it's called some protocol. Okay?
690
00:35:24,156 --> 00:35:27,390
See it has, looks just like doing a class, right? Class,
691
00:35:27,392 --> 00:35:30,594
someclass, enum, someenum, protocol, someproperty.
692
00:35:30,596 --> 00:35:33,196
Exactly the same. And then there's the curly braces and
693
00:35:33,198 --> 00:35:36,933
then there's these methods inside. Notice that you can
694
00:35:36,935 --> 00:35:41,471
have inheritance, in fact multiple inheritance for
695
00:35:41,473 --> 00:35:46,810
protocols. All right? Now, Swift is a single inheritance
696
00:35:46,812 --> 00:35:49,446
model for classes, but for protocols, it's multiple
697
00:35:49,448 --> 00:35:53,083
inheritance. What does it mean for a protocol to inherit from
698
00:35:53,085 --> 00:35:56,286
another protocol? Well, it just means that some property,
699
00:35:56,288 --> 00:35:59,055
okay, some protocol, anyone who implements some protocol
700
00:35:59,057 --> 00:36:02,359
also has to implement all of these protocols as well.
701
00:36:02,361 --> 00:36:04,161
InheritedProtocol1, InheritedProtocol2,
702
00:36:04,163 --> 00:36:06,596
it's gotta implement those as well. So it's required,
703
00:36:06,598 --> 00:36:09,799
the requirement goes all the way through, okay, that's what
704
00:36:09,801 --> 00:36:13,470
multiple inheritance means there. Okay, inside here when
705
00:36:13,472 --> 00:36:17,707
you have a var, okay, remember this has to be, in here you
706
00:36:17,709 --> 00:36:21,645
have to specify whether it's a get only or get and set. You
707
00:36:21,647 --> 00:36:23,947
can do that with this little curly brace syntax here,
708
00:36:23,949 --> 00:36:26,683
it could either have the word get in here or get space set.
709
00:36:26,685 --> 00:36:30,654
There's no such thing as a set only property, okay? Get or
710
00:36:30,656 --> 00:36:35,759
get and set. Any functions that are mutated,
711
00:36:35,761 --> 00:36:36,660
they will mutate this thing.
712
00:36:36,662 --> 00:36:39,763
They're expected to mutate it. Have to be marked mutating and
713
00:36:39,765 --> 00:36:42,732
this is because struct can be allowed to implement these
714
00:36:42,734 --> 00:36:45,669
protocols. And we know that structs that have methods that
715
00:36:45,671 --> 00:36:48,038
mutate themselves have to be declared mutating. So
716
00:36:48,040 --> 00:36:51,374
we have to do the same thing in the protocol here as well.
717
00:36:52,377 --> 00:36:55,979
Now it is possible to restrict your protocol by the way to be
718
00:36:55,981 --> 00:36:58,215
only for classes. In other words, enums and
719
00:36:58,217 --> 00:37:00,784
structs are not allowed to implement it. You do that by
720
00:37:00,786 --> 00:37:03,920
putting colon class comma right after the protocol
721
00:37:03,922 --> 00:37:07,824
before all the inherited ones, you just put class comma.
722
00:37:07,826 --> 00:37:11,127
Okay? That means this protocol is only for classes.
723
00:37:11,129 --> 00:37:12,896
Then you would not need mutating, okay,
724
00:37:12,898 --> 00:37:17,434
cuz we don't use mutating for classes. You can even
725
00:37:17,436 --> 00:37:19,703
specify initializers in your protocol. And
726
00:37:19,705 --> 00:37:23,974
that's basically saying anyone who implements this protocol
727
00:37:23,976 --> 00:37:27,844
has to have an initializer that looks like this, right?
728
00:37:27,846 --> 00:37:32,415
Okay, now, you have this protocol declared with all
729
00:37:32,417 --> 00:37:37,520
the vars and funcs in there. How do you implement it, okay?
730
00:37:37,522 --> 00:37:38,989
Well, a class or a structure or
731
00:37:38,991 --> 00:37:43,760
enum comes along and it says at the end of its, after,
732
00:37:43,762 --> 00:37:45,495
in the class' case after it says what its super
733
00:37:45,497 --> 00:37:49,266
class is. It puts a comma and says here's all the protocols
734
00:37:49,268 --> 00:37:52,369
I implement. And when it says these other protocols
735
00:37:52,371 --> 00:37:54,604
over here, it's promising to implement them. And
736
00:37:54,606 --> 00:37:57,774
the compiler will not let it get away with not implementing
737
00:37:57,776 --> 00:38:02,679
them, it will complain, okay? So simple as that.
738
00:38:02,681 --> 00:38:04,581
If you have an enum or structs,
739
00:38:04,583 --> 00:38:07,517
like here's an enum, there's no superclass obviously, but
740
00:38:07,519 --> 00:38:11,588
otherwise it looks the same. Right? So this enum is saying
741
00:38:11,590 --> 00:38:14,257
I'm gonna implement these two protocols. Same thing with
742
00:38:14,259 --> 00:38:16,593
this struct, it's saying I'm gonna implement these. And
743
00:38:16,595 --> 00:38:18,295
obviously if there are any mutating ones in here,
744
00:38:18,297 --> 00:38:21,865
it's gonna have to be mutating in there. Okay?
745
00:38:21,867 --> 00:38:25,235
There's no limit to the number of protocols that a class or
746
00:38:25,237 --> 00:38:30,073
struct or enum can implement, can do as many as they want.
747
00:38:30,075 --> 00:38:33,376
If you have a init as part of the protocol and
748
00:38:33,378 --> 00:38:36,713
a class is implementing it, it has to make that init
749
00:38:36,715 --> 00:38:40,483
required. Okay, that's because you wouldn't want
750
00:38:40,485 --> 00:38:44,354
this class to have a subclass that doesn't implement this.
751
00:38:44,356 --> 00:38:47,223
Now it would not implement the protocol anymore. And yet
752
00:38:47,225 --> 00:38:51,428
it inherits the fact that it has to implement the protocol.
753
00:38:51,430 --> 00:38:51,861
So, there's init,
754
00:38:51,863 --> 00:38:56,566
that's why you have to have this be required, okay? You
755
00:38:56,568 --> 00:38:59,669
are allowed to add protocol conformance via an extension.
756
00:38:59,671 --> 00:39:02,639
So you could have extension to something, let's say this is
757
00:39:02,641 --> 00:39:06,776
a struct or a class or an enum colon some protocol and
758
00:39:06,778 --> 00:39:10,547
then put the methods in here. So now I'm extending that
759
00:39:10,549 --> 00:39:14,718
class or struct or enum to implement this protocol. And
760
00:39:14,720 --> 00:39:17,554
that just allows you to put this code off in another file,
761
00:39:17,556 --> 00:39:20,123
for example, you don't have to put it in the same file. Or
762
00:39:20,125 --> 00:39:21,925
you might be, this might be a class
763
00:39:21,927 --> 00:39:23,360
you don't have the source code for.
764
00:39:23,362 --> 00:39:24,094
Maybe this is a UI kit thing,
765
00:39:24,096 --> 00:39:26,596
maybe you're extending UI button to implement some
766
00:39:26,598 --> 00:39:29,866
protocol. You could do that with an extension, you see?
767
00:39:29,868 --> 00:39:31,968
And that saves you having to for example,
768
00:39:31,970 --> 00:39:35,138
subclass UI button to add protocol conformance. Just use
769
00:39:35,140 --> 00:39:39,309
an extension. Okay, it's all really very well thought out.
770
00:39:39,311 --> 00:39:41,211
They did a really good job with Swift with protocols and
771
00:39:41,213 --> 00:39:44,381
extensions. Protocols are a really important part of
772
00:39:44,383 --> 00:39:47,350
sophisticated Swift programming. If you're really
773
00:39:47,352 --> 00:39:50,720
gonna build powerful complex things, you really have to be
774
00:39:50,722 --> 00:39:53,890
facile with protocols. Because protocols really
775
00:39:53,892 --> 00:39:56,793
give it the heart of what the API contract is
776
00:39:56,795 --> 00:39:59,929
between things. Okay, it's really talking about,
777
00:39:59,931 --> 00:40:02,699
these are the methods and functions I expect you to
778
00:40:02,701 --> 00:40:05,535
implement if you're gonna work with me, okay.
779
00:40:05,537 --> 00:40:06,403
That's what a protocol all about.
780
00:40:06,405 --> 00:40:09,639
And that's fundamentally good object-oriented programming,
781
00:40:09,641 --> 00:40:11,441
fundamentally good encapsulation, so
782
00:40:11,443 --> 00:40:13,176
you really wanna understand this.
783
00:40:13,178 --> 00:40:14,244
Now, this is a beginning,
784
00:40:14,246 --> 00:40:16,746
starting class in iOS so I don't expect you to
785
00:40:16,748 --> 00:40:19,449
have mastered this by the end. But again, it's something you
786
00:40:19,451 --> 00:40:21,484
gotta know that if you go out in the real world,
787
00:40:21,486 --> 00:40:22,819
and start a program people are gonna
788
00:40:22,821 --> 00:40:25,021
expect if you're gonna be a sophisticated programmer.
789
00:40:25,023 --> 00:40:27,357
It's like you're gonna know how to use protocols, and
790
00:40:27,359 --> 00:40:28,792
not just make everything a class.
791
00:40:28,794 --> 00:40:31,961
Okay all the courses you've had before now probably,
792
00:40:31,963 --> 00:40:34,063
everything's a class. Okay it's a Java thing,
793
00:40:34,065 --> 00:40:36,666
everything's a class. Now in the real world out there
794
00:40:36,668 --> 00:40:38,635
you're probably gonna have a lot of protocols.
795
00:40:38,637 --> 00:40:43,139
Okay, question? >> In what case would you
796
00:40:43,141 --> 00:40:46,543
use a protocol other than extension?
797
00:40:46,545 --> 00:40:46,843
>> Okay so this question is,
798
00:40:46,845 --> 00:40:49,245
in what case would you use a protocol versus just
799
00:40:49,247 --> 00:40:51,981
an extension for example, and that's a good question.
800
00:40:51,983 --> 00:40:55,852
You'd use a protocol anywhere you need to, specify that as
801
00:40:55,854 --> 00:41:00,957
a type in some API. Right, if you need to say the argument
802
00:41:00,959 --> 00:41:03,326
to this method takes something that implements
803
00:41:03,328 --> 00:41:06,596
certain methods then you need that protocol type to say that
804
00:41:06,598 --> 00:41:10,867
in your user data type. An extension is not its own type
805
00:41:10,869 --> 00:41:13,236
it's just adding methods to an existing type.
806
00:41:13,238 --> 00:41:15,672
See the difference? So a protocol is a typing and
807
00:41:15,674 --> 00:41:20,410
it's for strongly typing things. Okay, so,
808
00:41:20,412 --> 00:41:23,146
let's look at an example of protocols here.
809
00:41:23,148 --> 00:41:25,482
I have a protocol called Moveable. Okay,
810
00:41:25,484 --> 00:41:29,452
it has a mutating function called moveTo a certain point,
811
00:41:29,454 --> 00:41:33,156
okay. That's its only method. And I have two classes, two,
812
00:41:33,158 --> 00:41:37,427
not classes, two data types here. A Car. It's a moveable.
813
00:41:37,429 --> 00:41:39,929
See? It implements moveTo. It doesn't have to say mutating
814
00:41:39,931 --> 00:41:43,333
cuz it's a class. And I have a shape like a shape on screen.
815
00:41:43,335 --> 00:41:46,870
It's a triangle, a rectangle. Okay? It's moveable. Okay? And
816
00:41:46,872 --> 00:41:49,806
it's mutating. Okay, and it's got, they've each got other
817
00:41:49,808 --> 00:41:52,942
methods like a car knows how to change the oil of a car.
818
00:41:52,944 --> 00:41:54,677
And a shape knows how to draw the shape.
819
00:41:54,679 --> 00:41:58,515
Okay. But, they share the fact that they're both moveable.
820
00:41:58,517 --> 00:42:01,518
So I can create a couple local variables here, a prius,
821
00:42:01,520 --> 00:42:04,087
which is a car, initialized to a car here.
822
00:42:04,089 --> 00:42:06,556
And let's say I have a square, which is a shape,
823
00:42:06,558 --> 00:42:08,691
probably this would be actually be square,
824
00:42:08,693 --> 00:42:09,526
open parentheses, closes parentheses,
825
00:42:09,528 --> 00:42:12,996
probably have a subclass or a different kind of, well,
826
00:42:12,998 --> 00:42:16,766
let's say it's a shape [LAUGH] okay. So we got a shape here.
827
00:42:16,768 --> 00:42:19,936
Both of these, okay the prius and the square, are each
828
00:42:19,938 --> 00:42:25,341
a local variable of their perspective type. Now I, and
829
00:42:25,343 --> 00:42:27,577
so these, this is the method that's common to them all.
830
00:42:27,579 --> 00:42:32,282
Now I can create a var called thingToMove which is of type
831
00:42:32,284 --> 00:42:35,985
Moveable. That's its type. Okay, and I told you protocols
832
00:42:35,987 --> 00:42:39,589
could be type. And look, I can set this thing to moveTo
833
00:42:39,591 --> 00:42:43,293
a prius. Because a prius is a Moveable. Cuz it's a car.
834
00:42:43,295 --> 00:42:47,797
You see? So this is perfectly legal right here. And
835
00:42:47,799 --> 00:42:50,400
I could even say thingToMove.moveTo, and
836
00:42:50,402 --> 00:42:54,137
it'll call the car's version of moveTo, okay.
837
00:42:54,139 --> 00:43:01,377
Make sense? However, I cannot say thingToMove.changeOil.
838
00:43:01,780 --> 00:43:05,148
Okay? Why not? Prius is a car. Car can change oil.
839
00:43:05,150 --> 00:43:08,251
How come I can't say thingToMove change the oil?
840
00:43:08,820 --> 00:43:12,522
Because thingToMove is not a car,
841
00:43:12,524 --> 00:43:15,191
it's a moveable. Okay? Moveables only know
842
00:43:15,193 --> 00:43:18,962
how to move. Now the fact that underlying there's an actual
843
00:43:18,964 --> 00:43:21,064
thing a car in there that can do change oil, that's,
844
00:43:21,066 --> 00:43:23,433
that hasn't, it's irrelevant, okay. It has nothing to do it
845
00:43:23,435 --> 00:43:25,902
with because this thing is typed to only be a Moveable,
846
00:43:25,904 --> 00:43:32,008
so you can only send moveable things to it. Okay? I could
847
00:43:32,010 --> 00:43:35,044
also say thingToMove, this same variable, equals square.
848
00:43:35,046 --> 00:43:38,014
Also perfectly legal, because a square is a shape,
849
00:43:38,016 --> 00:43:42,819
a shape is a Moveable. Okay? I could even create an array
850
00:43:42,821 --> 00:43:46,456
of things to move that are moveables and
851
00:43:46,458 --> 00:43:48,324
put the prius in the square in there.
852
00:43:48,326 --> 00:43:49,092
Okay, so even though prius and
853
00:43:49,094 --> 00:43:50,159
the square are completely different things,
854
00:43:50,161 --> 00:43:53,796
a car and a shape, okay? They can live in this array because
855
00:43:53,798 --> 00:43:57,600
they're both moveables. This is an array of moveables,
856
00:43:57,602 --> 00:44:01,037
okay? Now I could have a function here for
857
00:44:01,039 --> 00:44:04,040
example called slide which takes a slider or
858
00:44:04,042 --> 00:44:06,142
thing to slide basically. And it's a moveable.
859
00:44:06,144 --> 00:44:09,379
So, with argument type here, this function is moveable.
860
00:44:09,381 --> 00:44:13,149
And inside here, I could take slider move to some position.
861
00:44:13,151 --> 00:44:15,184
And I don't care wether this is a car or shape.
862
00:44:15,186 --> 00:44:18,721
This function slide has no idea wether sliding a car or
863
00:44:18,723 --> 00:44:22,358
sliding a shape. No idea. Cuz all it knows is it's working
864
00:44:22,360 --> 00:44:25,094
on a moveable. So that's how you could use a moveable as
865
00:44:25,096 --> 00:44:27,563
a type to an argument, of a function, okay.
866
00:44:27,565 --> 00:44:30,033
And see, I can say slide(prius), slide(square),
867
00:44:30,035 --> 00:44:32,902
works perfectly, yeah. >> You said car [INAUDIBLE]
868
00:44:32,904 --> 00:44:36,272
into moveable it's another protocol.
869
00:44:36,274 --> 00:44:40,209
Card could be seen as a little object as well
870
00:44:40,211 --> 00:44:42,578
as the other. >> Yeah.
871
00:44:42,580 --> 00:44:45,848
So the question is, I think the question is if car
872
00:44:45,850 --> 00:44:48,117
was part of other protocols? >> Yeah.
873
00:44:48,119 --> 00:44:48,651
>> Then could I set
874
00:44:48,653 --> 00:44:50,953
some variable that's of that other protocol equal to this?
875
00:44:50,955 --> 00:44:53,990
Absolutely I could. Because it is both removable and that
876
00:44:53,992 --> 00:44:56,793
other thing. Independently it is both of those things.
877
00:44:56,795 --> 00:45:00,763
And I can even have a protocol right here that's
878
00:45:00,765 --> 00:45:05,268
two different things. Now this would mean that for something
879
00:45:05,270 --> 00:45:07,537
to be passed to slip and slide through this variable.
880
00:45:07,539 --> 00:45:10,206
It would have to implement both of these protocols. So
881
00:45:10,208 --> 00:45:12,909
it's almost like I'm inventing a protocol in this slot.
882
00:45:12,911 --> 00:45:15,044
With this syntax right here, it's almost like you're
883
00:45:15,046 --> 00:45:19,115
inventing a protocol that inherits both of these.
884
00:45:19,117 --> 00:45:22,185
Okay, so this means that it requires both that's what this
885
00:45:22,187 --> 00:45:24,353
protocol angle bracket means okay, and
886
00:45:24,355 --> 00:45:27,356
this could be any number of protocols in here. Now I can't
887
00:45:27,358 --> 00:45:31,494
say slipAndSlide(prius) here, because prius is not slippery.
888
00:45:31,496 --> 00:45:34,363
It's moveable but it's not slippery so it will not pass
889
00:45:34,365 --> 00:45:38,768
through here, everybody got that? So that's protocol.
890
00:45:38,770 --> 00:45:41,437
Super powerful, you'll start to get a feel for
891
00:45:41,439 --> 00:45:44,240
as we start using more and more. Now the first important
892
00:45:44,242 --> 00:45:46,909
use of protocol we're gonna talk about is Delegation.
893
00:45:46,911 --> 00:45:49,912
All right? So delegation is how we do this blind
894
00:45:49,914 --> 00:45:52,648
structured communication between our view and
895
00:45:52,650 --> 00:45:56,285
our controller right there. And we talked about this very
896
00:45:56,287 --> 00:46:00,022
second lecture, I think about how sometimes our view wants
897
00:46:00,024 --> 00:46:01,791
to send message like, should I do this,
898
00:46:01,793 --> 00:46:05,828
or I will do this like in the scroll view. I did scroll to
899
00:46:05,830 --> 00:46:09,766
this location, I will zoom to this zoom factor. Or
900
00:46:09,768 --> 00:46:12,869
if I'm some data thing like a table, I'm gonna say, well
901
00:46:12,871 --> 00:46:16,005
I've got a count of 400 rows, I want the data at row 7,
902
00:46:16,007 --> 00:46:20,276
I want the data at row 12 through 20. Okay? These kind
903
00:46:20,278 --> 00:46:23,679
of communications right here, you can see now with protocol,
904
00:46:23,681 --> 00:46:26,149
it's really easy. You just create a protocol that has all
905
00:46:26,151 --> 00:46:29,285
these methods in it, and anybody can implement this.
906
00:46:29,287 --> 00:46:31,521
Even though it's usually a controller, it doesn't have to
907
00:46:31,523 --> 00:46:33,923
be a controller. The controller for example could
908
00:46:33,925 --> 00:46:37,326
instantiate some other object that implements those methods.
909
00:46:37,328 --> 00:46:39,796
Okay, it would still be a part of controller camp,
910
00:46:39,798 --> 00:46:41,631
by the way, but it doesn't have to actually
911
00:46:41,633 --> 00:46:44,967
be a UIViewController sub-class anymore.
912
00:46:44,969 --> 00:46:49,539
Okay, so let's see what that looks like. First some
913
00:46:49,541 --> 00:46:52,842
view something in the view okay this generic remember
914
00:46:52,844 --> 00:46:55,678
views are generic minions okay of the controller.
915
00:46:55,680 --> 00:46:59,148
They declare some protocol which is the methods,
916
00:46:59,150 --> 00:47:00,049
like the will should did, or
917
00:47:00,051 --> 00:47:03,219
the data add count, those things that it wants somebody
918
00:47:03,221 --> 00:47:05,121
else like the controller to implement for it,
919
00:47:05,123 --> 00:47:07,356
so it declares a protocol with all those methods and
920
00:47:07,358 --> 00:47:10,493
properties in it. Then the view's API, okay,
921
00:47:10,495 --> 00:47:13,896
somewhere in the view, it has a public property, okay, which
922
00:47:13,898 --> 00:47:20,303
is weak delegate, and the type is this protocol it invented.
923
00:47:20,305 --> 00:47:22,972
Okay so this is gonna be a var. Okay, that's going to
924
00:47:22,974 --> 00:47:25,808
have to be an object that implements that protocol and
925
00:47:25,810 --> 00:47:27,944
it's weak because the view is kind of
926
00:47:27,946 --> 00:47:31,948
saying well if no one can do this will did should or can't
927
00:47:31,950 --> 00:47:34,750
do the count add data well I'll just sit here empty or I
928
00:47:34,752 --> 00:47:37,887
won't do what I do or I won't notify anyone of the wills and
929
00:47:37,889 --> 00:47:41,290
dids. Okay, I won't ask anyone the shoulds. So it can be nil
930
00:47:41,292 --> 00:47:44,260
so that's why the delegate is usually weak okay or
931
00:47:44,262 --> 00:47:47,396
might even be unowned but it's certainly not strong.
932
00:47:47,398 --> 00:47:50,900
Cuz you don't want the view to keep the controller in memory
933
00:47:50,902 --> 00:47:53,436
right. The controller can keeps the view in memory but
934
00:47:53,438 --> 00:47:55,438
you don't want the view keeping the controller.
935
00:47:55,440 --> 00:47:58,441
All right, then the view uses this delegate property
936
00:47:58,443 --> 00:48:01,444
any time it wants to send that will, should, did,
937
00:48:01,446 --> 00:48:03,946
count, data, add. It just sends it to this
938
00:48:03,948 --> 00:48:06,883
delegate, okay? Because it knows the delegate implements
939
00:48:06,885 --> 00:48:09,952
those protocol methods. Then the controller
940
00:48:09,954 --> 00:48:12,788
has to declare that it implements this protocol, so
941
00:48:12,790 --> 00:48:15,524
it just puts it on its you know, class something,
942
00:48:15,526 --> 00:48:18,661
UIV controller, comma, that protocol.
943
00:48:18,663 --> 00:48:22,899
Then the controller sets itself as this delegate, so it
944
00:48:22,901 --> 00:48:26,802
sets itself as this property. So now when the view talks
945
00:48:26,804 --> 00:48:29,739
to that delegate it's talking back to the controller.
946
00:48:29,741 --> 00:48:34,243
So that's how it plays out to use delegation here.
947
00:48:34,245 --> 00:48:36,946
Now of course the controller has to implement the protocol,
948
00:48:36,948 --> 00:48:38,247
has to implement the methods, but
949
00:48:38,249 --> 00:48:40,783
remember this is all objective C worlds.
950
00:48:40,785 --> 00:48:43,619
Right?The delegation is from the objective C world, so
951
00:48:43,621 --> 00:48:44,954
all these things you're gonna see in iOS,
952
00:48:44,956 --> 00:48:47,924
these delegate protocols like scroll, views delegate,
953
00:48:47,926 --> 00:48:52,094
table views, delegate. Almost all the methods are optional,
954
00:48:52,096 --> 00:48:53,796
okay. There's a few in there that aren't, and
955
00:48:53,798 --> 00:48:56,265
that's because you might not care that the scroll
956
00:48:56,267 --> 00:48:58,968
view just finished scrolling to this location. So you don't
957
00:48:58,970 --> 00:49:01,871
wanna that method scroll view did scroll to point.
958
00:49:01,873 --> 00:49:02,071
You don't want it, so
959
00:49:02,073 --> 00:49:04,740
you don't wanna even have to implement it. Okay? So
960
00:49:04,742 --> 00:49:06,976
these protocols are all gonna be Objective-C protocols,
961
00:49:06,978 --> 00:49:09,578
@objc, and they're gonna have a lot of word optional in
962
00:49:09,580 --> 00:49:12,815
front of a lot of the funcs, okay. All right, so
963
00:49:12,817 --> 00:49:15,985
now the view's hooked up to the controller. The view still
964
00:49:15,987 --> 00:49:18,587
has no idea what kind of object is doing, is
965
00:49:18,589 --> 00:49:20,957
implementing the will, should, and did. It could be anything.
966
00:49:20,959 --> 00:49:22,391
Any class, it could, doesn't even have to be a class!
967
00:49:22,393 --> 00:49:24,760
It could be a struct or something. Although actually,
968
00:49:24,762 --> 00:49:27,229
probably does have to be a class, cuz OBJC thing,
969
00:49:27,231 --> 00:49:29,465
those have to be classes, and it's an object thing. So
970
00:49:29,467 --> 00:49:32,301
it is a class but it doesn't know that it's a UI view
971
00:49:32,303 --> 00:49:34,437
controller. Ok, it doesn't have to be a view-a UI view
972
00:49:34,439 --> 00:49:37,106
controller. So it's blind. But it's structured, because
973
00:49:37,108 --> 00:49:39,942
the protocol says exactly the method that had to be sent.
974
00:49:39,944 --> 00:49:45,948
All right? So, this, I keep talking about this happening,
975
00:49:45,950 --> 00:49:47,850
because of objective C in the history of iOS.
976
00:49:47,852 --> 00:49:49,986
What would you do in Swift? Because Swift doesn't have
977
00:49:49,988 --> 00:49:51,654
these optional protocols. Well, in Swift,
978
00:49:51,656 --> 00:49:54,523
either you could do the same thing, ok? And just
979
00:49:54,525 --> 00:49:57,460
break up your protocol into the pieces that make sense, so
980
00:49:57,462 --> 00:49:59,228
you might have three or four different protocols and
981
00:49:59,230 --> 00:50:01,163
you'll only implement the ones that you want.
982
00:50:01,165 --> 00:50:03,299
Like you have the scroll view notification protocols,
983
00:50:03,301 --> 00:50:05,901
and then you have the scroll view should protocols, and
984
00:50:05,903 --> 00:50:08,337
then you have the data app protocols, or whatever.
985
00:50:08,339 --> 00:50:12,842
Another way to do things like this is with closures, okay?
986
00:50:12,844 --> 00:50:15,644
You don't even need all this overhead of having
987
00:50:15,646 --> 00:50:18,948
these protocols do that you can just have a closure and
988
00:50:18,950 --> 00:50:20,683
the closure knows what
989
00:50:20,685 --> 00:50:22,284
the arguments to the closure are and
990
00:50:22,286 --> 00:50:22,885
what it's supposed to return.
991
00:50:22,887 --> 00:50:25,821
And that's essentially like having the protocol defined.
992
00:50:25,823 --> 00:50:28,724
So in this case what you could do you can imagine saying to
993
00:50:28,726 --> 00:50:33,062
the scroll view. When you're finished scrolling,
994
00:50:33,264 --> 00:50:37,333
execute this closure. Right, give it a closure. So
995
00:50:37,335 --> 00:50:39,235
that's another way to do it is with closures. And
996
00:50:39,237 --> 00:50:43,072
you're gonna see that iOS does some things with closures and
997
00:50:43,074 --> 00:50:45,508
some things with delegates. And they're not
998
00:50:45,510 --> 00:50:48,444
exact substitutes for each other. Protocols are sometimes
999
00:50:48,446 --> 00:50:51,213
nice because they make it really clear what this object
1000
00:50:51,215 --> 00:50:54,417
is capable of delegating. So that's nice.
1001
00:50:54,419 --> 00:50:56,385
Closures are really good for things like error
1002
00:50:56,387 --> 00:50:59,455
call backs and multi, when a multi-threaded world when
1003
00:50:59,457 --> 00:51:01,690
things are gonna take a long time and they're done later
1004
00:51:01,692 --> 00:51:04,226
and it wants to tell you it's done, that kinda thing. So
1005
00:51:04,228 --> 00:51:07,930
we'll see both as the rest of the quarter goes on. All
1006
00:51:07,932 --> 00:51:10,633
right, so here's an example of scroll view, right? So
1007
00:51:10,635 --> 00:51:14,837
scroll view, the, is a UI view subclass. It has a var called
1008
00:51:14,839 --> 00:51:18,908
delegate, it's weak. Its type is UIScrollView delegate
1009
00:51:18,910 --> 00:51:21,811
optional because you don't have to set a delegate here.
1010
00:51:21,813 --> 00:51:23,913
Okay. The delegate protocol looks like this.
1011
00:51:23,915 --> 00:51:27,650
It's in OBJC protocol. Okay. And it has all these
1012
00:51:27,652 --> 00:51:29,919
optional functions. It has over a dozen of them,
1013
00:51:29,921 --> 00:51:32,421
things like scrollViewDidScroll. View for
1014
00:51:32,423 --> 00:51:35,491
assuming it's scroll view which gets a view to zoom on
1015
00:51:35,493 --> 00:51:38,360
on inside the scroll view etc. Okay? So
1016
00:51:38,362 --> 00:51:41,397
a Controller that wants to work with the ScrollView
1017
00:51:41,399 --> 00:51:45,367
will say MyViewController is a subclass of UIViewController
1018
00:51:45,369 --> 00:51:49,538
and it implements this protocol. Okay?
1019
00:51:49,540 --> 00:51:50,272
Now most of this is optional so
1020
00:51:50,274 --> 00:51:54,310
didn't have to do much to do that. Okay? And then
1021
00:51:54,312 --> 00:51:57,446
this UIViewController is gonna say, scrollView.delegate.
1022
00:51:57,448 --> 00:51:59,548
This is some outlet that points to the ScrollView.
1023
00:51:59,550 --> 00:52:02,184
That delegate equals self and that's gonna be okay because
1024
00:52:02,186 --> 00:52:05,321
it says that it implements this protocol, okay.
1025
00:52:05,323 --> 00:52:08,390
Now the scroll view is gonna use this to talk to this view
1026
00:52:08,392 --> 00:52:11,994
controller. Okay, and then view controller implements
1027
00:52:11,996 --> 00:52:16,499
whichever of these it wants and, off to the races. Okay,
1028
00:52:16,501 --> 00:52:23,205
make sense? All right, let's go through and
1029
00:52:23,207 --> 00:52:25,107
talk about ScrollView in more detail, because this is
1030
00:52:25,109 --> 00:52:28,244
an important class, all right. So here's an interesting,
1031
00:52:28,246 --> 00:52:30,346
this is like an iPhone one or something.
1032
00:52:30,348 --> 00:52:32,982
Notice this very small little iPhone. But it has a cool
1033
00:52:32,984 --> 00:52:35,384
little animation here to show you how ScrollViews work.
1034
00:52:35,386 --> 00:52:37,386
Look at this ScrollView. See it can ScrollView
1035
00:52:37,388 --> 00:52:40,556
horizontally, and inside there can be vertical things.
1036
00:52:40,558 --> 00:52:44,527
So this is a ScrollView that's inside ScrollView. See that?
1037
00:52:44,529 --> 00:52:47,696
So ScrollView's really smart about that. Look at this one.
1038
00:52:47,698 --> 00:52:49,965
Okay, you've got two things, top thing ScrollViews and
1039
00:52:49,967 --> 00:52:51,700
the bottom thing is a horizontal ScrollView
1040
00:52:51,702 --> 00:52:55,271
with vertical ones inside. So you can see an old iPhone
1041
00:52:55,273 --> 00:52:58,774
one here or whatever it is, not a lot of screen space, but
1042
00:52:58,776 --> 00:53:02,344
it's very efficiently being used by the ScrollViews, okay.
1043
00:53:02,346 --> 00:53:04,446
So the ScrollView is super powerful. So
1044
00:53:04,448 --> 00:53:07,783
let's talk a little bit about how ScrollView works and
1045
00:53:07,785 --> 00:53:10,386
we do it mostly by adding subviews to it.
1046
00:53:10,388 --> 00:53:11,987
Let's first remind ourselves what it's like to
1047
00:53:11,989 --> 00:53:15,958
add a subview to a normal UIView. It's really simple.
1048
00:53:15,960 --> 00:53:18,060
I've got the UI view right here on my phone.
1049
00:53:18,062 --> 00:53:22,598
I just set the frame, where I want this UI view to be added,
1050
00:53:22,600 --> 00:53:25,334
and then I just add it. View, add sub-view.
1051
00:53:25,336 --> 00:53:27,203
This is probably in the view controllers so
1052
00:53:27,205 --> 00:53:28,971
this will be the top level view. And
1053
00:53:28,973 --> 00:53:31,674
I'm just adding the sub-view, this specifies where it is.
1054
00:53:31,676 --> 00:53:34,276
All right, everyone is cool with that? Very simple.
1055
00:53:34,278 --> 00:53:38,881
So a scroll view is almost exactly the same. Okay. But
1056
00:53:38,883 --> 00:53:43,152
the big step that's different is first we declare a content
1057
00:53:43,154 --> 00:53:47,623
size. This is a big area, k, that the scroll view is going
1058
00:53:47,625 --> 00:53:52,795
to be scrolling around and in. Okay. And we specify by size.
1059
00:53:52,797 --> 00:53:54,530
Okay, here I've made one 3,000 wide by 2,000 high, so
1060
00:53:54,532 --> 00:53:58,067
we've got a lot of room to scroll around in there.
1061
00:53:58,069 --> 00:54:01,303
But once I've specified the content size which I've set
1062
00:54:01,305 --> 00:54:02,538
this bar right here.
1063
00:54:02,540 --> 00:54:05,574
Now I just add sub views in the exact same way. Okay,
1064
00:54:05,576 --> 00:54:07,977
logo.frame here I've moved it over to 2700,
1065
00:54:07,979 --> 00:54:10,446
so it's way over on the right. Scroll view,
1066
00:54:10,448 --> 00:54:12,915
add sub view logo, okay? Just like any other view.
1067
00:54:12,917 --> 00:54:15,217
I've added it as a subview. Could add another one.
1068
00:54:15,219 --> 00:54:18,053
Let's add an aerial photo of Stanford here, okay?
1069
00:54:18,055 --> 00:54:22,558
Added it as a subview. So they all get added in a coordinate
1070
00:54:22,560 --> 00:54:27,596
system relative to this big content area right here, okay?
1071
00:54:27,598 --> 00:54:32,234
Now the scroll view just moves around and lets you
1072
00:54:32,236 --> 00:54:36,872
look around inside that content area. See. That's all
1073
00:54:36,874 --> 00:54:40,609
it's doing, is looking around. Now you can move the subviews
1074
00:54:40,611 --> 00:54:41,910
to different places in the same way you can move
1075
00:54:41,912 --> 00:54:44,913
them in a regular view. Right? So I can change the frame of
1076
00:54:44,915 --> 00:54:45,981
the aerial view to be in the corner.
1077
00:54:45,983 --> 00:54:48,717
I can change the frame of the logo, maybe, to overlap.
1078
00:54:48,719 --> 00:54:51,053
I can also come along later and change the content size.
1079
00:54:51,055 --> 00:54:53,689
Maybe I want this content size to be small enough to just fit
1080
00:54:53,691 --> 00:54:58,193
my views. Okay and again then when I'm kind of using
1081
00:54:58,195 --> 00:55:01,630
my finger, in the scroll view I'm just moving around in
1082
00:55:01,632 --> 00:55:05,000
its content area. So the most important thing to understand
1083
00:55:05,002 --> 00:55:08,470
about this is if you don't set your content size
1084
00:55:08,472 --> 00:55:08,671
you get nothing.
1085
00:55:08,673 --> 00:55:10,306
You can't scroll because you're scrolling over that
1086
00:55:10,308 --> 00:55:14,643
content size. This is the most commonly mistaken thing about
1087
00:55:14,645 --> 00:55:15,477
scroll view people don't understand.
1088
00:55:15,479 --> 00:55:18,047
They don't set their content size and so things
1089
00:55:18,049 --> 00:55:22,518
can't scroll. Gotta have a content size, okay? Couple of
1090
00:55:22,520 --> 00:55:25,354
other interesting things. If you want to find out where
1091
00:55:25,356 --> 00:55:30,225
you're currently showing in the content area, there's this
1092
00:55:30,227 --> 00:55:33,962
var inside of scrollView called contentOffset. And
1093
00:55:33,964 --> 00:55:36,732
it's just a CG point. Gonna tell you the X and
1094
00:55:36,734 --> 00:55:40,102
Y of the upper left corner of where the scroll view is
1095
00:55:40,104 --> 00:55:43,272
showing right as it moves around that changes.
1096
00:55:43,274 --> 00:55:45,274
Also if you wanted to know the whole,
1097
00:55:45,276 --> 00:55:49,111
what that whole rectangle is in the, coordinate system in
1098
00:55:49,113 --> 00:55:52,648
one of these views, like this thing or the, or the.
1099
00:55:52,650 --> 00:55:55,217
The logo or the, or the aerial view. You can get
1100
00:55:55,219 --> 00:55:59,154
the scrollView's bounds, okay. ScrollView.bound. And
1101
00:55:59,156 --> 00:56:04,426
use this UIView method convertRect fromView, okay?
1102
00:56:04,428 --> 00:56:07,463
So you're gonna convert this bounds from the scrollView's
1103
00:56:07,465 --> 00:56:10,532
coordinate system, to this aerial view. This is probably
1104
00:56:10,534 --> 00:56:13,836
the UIImageView, okay. To it, coordinate system. Now you
1105
00:56:13,838 --> 00:56:17,406
can find the whole rectangle in the a, in the aerial guy
1106
00:56:17,408 --> 00:56:22,244
coordinate system, to show what's, what's visible, okay?
1107
00:56:23,614 --> 00:56:26,348
Okay, how do you create a scrollView,
1108
00:56:26,350 --> 00:56:27,583
it's just like in the other UIView,
1109
00:56:27,585 --> 00:56:30,786
usually you're gonna drag it out in, your story board,
1110
00:56:30,788 --> 00:56:36,091
okay? You can also do Embed In Scroll View in the storyboard.
1111
00:56:36,093 --> 00:56:38,193
We don't usually do that very much, but you can.
1112
00:56:38,195 --> 00:56:40,496
That's if you just had one view inside there,
1113
00:56:40,498 --> 00:56:44,233
it would embed it in there and also set its content size
1114
00:56:44,235 --> 00:56:46,735
to the size of the thing you embedded, but usually we drag
1115
00:56:46,737 --> 00:56:51,540
it out. You can also create it in code, right, it's just,
1116
00:56:51,542 --> 00:56:55,110
it's just a view, so you can use UIView (frame:),
1117
00:56:55,112 --> 00:56:59,581
initializer there. Then you, add your too big UIView,
1118
00:56:59,583 --> 00:57:02,184
of the view that's too big that you wanna scroll over,
1119
00:57:02,186 --> 00:57:05,854
just by creating it and calling addSubView, okay?
1120
00:57:05,856 --> 00:57:10,192
Talked about on the previous slides there,
1121
00:57:10,194 --> 00:57:12,661
okay? Don't forget to set that contentSize,,
1122
00:57:12,663 --> 00:57:15,330
I can't emphasize that enough. If you don't, if you don't set
1123
00:57:15,332 --> 00:57:17,366
the contentSize, it's just not gonna work, okay?
1124
00:57:17,368 --> 00:57:21,904
All right, so, you can scroll with your finger, but you can
1125
00:57:21,906 --> 00:57:25,307
also scroll programmatically. And the classic way to do is
1126
00:57:25,309 --> 00:57:28,210
by saying scrollRectToVisible and you just specify
1127
00:57:28,212 --> 00:57:31,513
a rectangle in the contents size coordinate system, and
1128
00:57:31,515 --> 00:57:34,583
it'll scroll to try and show as much of that rectangle
1129
00:57:34,585 --> 00:57:38,187
as possible. It'll scoll, scroll as little as possible,
1130
00:57:38,189 --> 00:57:41,423
to show as much as possible of the rectangle, okay?
1131
00:57:41,425 --> 00:57:44,026
Here's also a ton of other methods in scrollView, I can't
1132
00:57:44,028 --> 00:57:47,296
talk about them all, you can go look them up. But you can
1133
00:57:47,298 --> 00:57:49,565
control which direction scrolling is allowed,
1134
00:57:49,567 --> 00:57:51,333
just vertically or just horizontally, or
1135
00:57:51,335 --> 00:57:53,735
if it starts being vertical, does it lock vertical?
1136
00:57:53,737 --> 00:57:56,772
That's how the scroll view inside scrollView works.
1137
00:57:56,774 --> 00:57:59,641
Things like that, you can flash your scroll bars,
1138
00:57:59,643 --> 00:58:00,909
all kinds of stuff there, okay.
1139
00:58:00,911 --> 00:58:03,745
Now what about zooming? So we talked about kinda panning
1140
00:58:03,747 --> 00:58:08,016
around, what if I wanna zoom in, like pinch to zoom in,
1141
00:58:08,018 --> 00:58:11,820
how do I do that? Okay, well, all UIViews, okay,
1142
00:58:11,822 --> 00:58:14,122
have a property called transform, it's
1143
00:58:14,124 --> 00:58:17,459
an affine transform, which means translate, scale and
1144
00:58:17,461 --> 00:58:21,396
rotate for, or is incorporated into to this transform. And so
1145
00:58:21,398 --> 00:58:25,267
the scrollView, as you zoom it's just modifying the scale
1146
00:58:25,269 --> 00:58:29,471
of this transform property in the view. So basically, if you
1147
00:58:29,473 --> 00:58:32,140
zoomed in really a lot your view would get very grainy,
1148
00:58:32,142 --> 00:58:35,978
because all it's doing is basically scaling the bits up.
1149
00:58:35,980 --> 00:58:37,813
Now, once it's finished zooming in,
1150
00:58:37,815 --> 00:58:39,448
you could redraw your view to not be so
1151
00:58:39,450 --> 00:58:43,485
grainy, okay? If you have, the ability to do that. But,
1152
00:58:43,487 --> 00:58:46,989
generally when it's zooming, it is zooming the bits, okay?
1153
00:58:46,991 --> 00:58:52,261
Using this transform property. Zooming will not work unless
1154
00:58:52,263 --> 00:58:54,963
you set these properties. Because by default,
1155
00:58:54,965 --> 00:58:58,534
the minimumZoomScale and the maximumZoomScale is 1.
1156
00:58:58,536 --> 00:59:03,138
Meaning no zoom, okay? So this is how much,
1157
00:59:03,140 --> 00:59:07,509
you can zoom size, zoom down, minimum. So 0.5
1158
00:59:07,511 --> 00:59:11,046
would mean it can zoom down to half its normal size. And
1159
00:59:11,048 --> 00:59:14,316
this is zooming out, eh, you can zoom to twice its normal
1160
00:59:14,318 --> 00:59:17,853
size if you set it to 2.0. Okay, so you have to set
1161
00:59:17,855 --> 00:59:20,455
these, don't forget that or your zooming won't work.
1162
00:59:20,457 --> 00:59:22,925
You also won't work if you don't implement this
1163
00:59:22,927 --> 00:59:24,359
delegate method, that's why I talked to you and
1164
00:59:24,361 --> 00:59:27,062
told you about delegates. You have to implement this method,
1165
00:59:27,064 --> 00:59:29,431
viewForZoomingInScrollView, okay. By the way,
1166
00:59:29,433 --> 00:59:33,535
notice these delegate methods, the first argument is always
1167
00:59:33,537 --> 00:59:35,370
the thing that's sending you the delegate method,
1168
00:59:35,372 --> 00:59:37,940
it's basically the sender. Okay, just so
1169
00:59:37,942 --> 00:59:39,575
you know which, if you had two scrollViews,
1170
00:59:39,577 --> 00:59:42,578
you'd know which one is asking you for the view for
1171
00:59:42,580 --> 00:59:43,145
zooming in scrollView.
1172
00:59:43,147 --> 00:59:45,781
Now this is going to return the UIView that's gonna
1173
00:59:45,783 --> 00:59:50,886
have its transform modified when you pinch, okay? So
1174
00:59:50,888 --> 00:59:51,386
you have to implement this.
1175
00:59:51,388 --> 00:59:54,856
If you don't implement this you get no zooming,
1176
00:59:54,858 --> 00:59:58,026
all right? You can also zoom programmatically, not just
1177
00:59:58,028 --> 01:00:00,128
with a pinch obviously, you can set it to scale, or
1178
01:00:00,130 --> 01:00:02,931
you can zoom to a rectangle that looks like this. So here
1179
01:00:02,933 --> 01:00:07,135
I have a zoom scale of 1.2 I'm kinda zoomed out a little bit,
1180
01:00:07,137 --> 01:00:08,637
and I could zoom back to normal.
1181
01:00:08,639 --> 01:00:11,506
Okay, this is 1.0 so this is the normal size of this image.
1182
01:00:11,508 --> 01:00:15,944
Or I could zoom back out to 1.2, so that's 20% larger,
1183
01:00:15,946 --> 01:00:18,847
right? I can also do the zoomToRect, let's say I have
1184
01:00:18,849 --> 01:00:21,516
this little rectangle here and I said zoom to this rect,
1185
01:00:21,518 --> 01:00:24,119
it would show as much of the rectangle as possible. Or
1186
01:00:24,121 --> 01:00:27,489
if I had the rect out there, and I said zoom to rect,
1187
01:00:27,491 --> 01:00:30,826
it would zoom it down to fit that rectangle, okay.
1188
01:00:30,828 --> 01:00:34,730
So that's how you can zoom in from your code, okay?
1189
01:00:34,732 --> 01:00:36,732
There's lots and lots of other delegate methods,
1190
01:00:36,734 --> 01:00:38,800
you can look at the documentation to figure out,
1191
01:00:38,802 --> 01:00:40,135
a lot of them are the did and
1192
01:00:40,137 --> 01:00:42,004
will, like here's DidEndZooming.
1193
01:00:42,006 --> 01:00:46,208
This is where you might redraw your thing with more,
1194
01:00:46,210 --> 01:00:49,544
with a finer grain, if you got scaled up really big and
1195
01:00:49,546 --> 01:00:52,280
you got grainy, things like that. So you can take a look
1196
01:00:52,282 --> 01:00:55,717
at all those. I have a demo here at the end, but
1197
01:00:55,719 --> 01:00:58,153
first I want to talk about whats coming up here.
1198
01:00:58,155 --> 01:00:59,788
On Wednesday your Assignment 3 is due,
1199
01:00:59,790 --> 01:01:01,189
of course before lecture you know that,
1200
01:01:01,191 --> 01:01:03,525
and we're gonna start talking about Multithreading,
1201
01:01:03,527 --> 01:01:06,561
all right? Friday this section is on UI Testing,
1202
01:01:06,563 --> 01:01:07,162
which is really cool and
1203
01:01:07,164 --> 01:01:09,665
iOS, you can actually basically record mouse clicks
1204
01:01:09,667 --> 01:01:12,601
and then play them back, and make sure that you're getting
1205
01:01:12,603 --> 01:01:16,104
the results you expect. Next week, we're gonna talk about
1206
01:01:16,106 --> 01:01:17,439
Table View, and Object-Oriented Database.
1207
01:01:17,441 --> 01:01:21,076
I'm give a little break this weekend, okay? I'm not gonna
1208
01:01:21,078 --> 01:01:23,912
assign, Assignment 4 on Wednesday, I'll be assigning
1209
01:01:23,914 --> 01:01:26,882
it next Monday. It'll be due the Monday after. So
1210
01:01:26,884 --> 01:01:29,217
you have no assignment over this coming weekend.
1211
01:01:29,219 --> 01:01:32,821
There's no more reading assignments at all. Okay,
1212
01:01:32,823 --> 01:01:36,558
right now, I'm gonna do a little scrollView demo, okay,
1213
01:01:36,560 --> 01:01:41,663
called Cassini. Any questions before I jump into that?
1214
01:01:41,665 --> 01:01:47,803
Okay Cassini, okay, I'm gonna create a brand new thing here.
1215
01:01:47,805 --> 01:01:53,575
So let's quit that, let's go, oops, over here.
1216
01:01:54,578 --> 01:01:57,679
Create a new project. Okay, it's gonna be an iO, I'm gonna
1217
01:01:57,681 --> 01:02:00,082
start going fast through these new project creations here,
1218
01:02:00,084 --> 01:02:01,583
Single View Applications like we always do.
1219
01:02:01,585 --> 01:02:04,786
I'm gonna call it Cassini. Okay all the things we
1220
01:02:04,788 --> 01:02:09,391
normally do there. Put it in the same place we always do.
1221
01:02:11,128 --> 01:02:14,963
Here is our Cassini. I'm going to do the same thing where I'm
1222
01:02:14,965 --> 01:02:17,432
gonna move these things out of the way, or
1223
01:02:17,434 --> 01:02:22,771
at least most of them. Supporting,
1224
01:02:22,773 --> 01:02:24,906
oops, Supporting Files.
1225
01:02:24,908 --> 01:02:27,576
Okay, they, that, there, I left info plist,
1226
01:02:27,578 --> 01:02:31,379
cuz we're actually gonna look at that today. All right,
1227
01:02:31,381 --> 01:02:35,450
here's my storyboard, for Cossini right here,
1228
01:02:35,452 --> 01:02:39,020
this storyboard has a generic ViewController.
1229
01:02:39,022 --> 01:02:41,389
You know, we always get this generic ViewController.
1230
01:02:41,391 --> 01:02:43,692
Now in the calculator, we renamed this,
1231
01:02:43,694 --> 01:02:45,794
hopefully you did that for your assignment. But
1232
01:02:45,796 --> 01:02:48,130
I'm not gonna rename it, I'm gonna do another thing here,
1233
01:02:48,132 --> 01:02:51,066
cuz I don't want this to be a generic ViewController.
1234
01:02:51,068 --> 01:02:53,235
I want it to be a subclass of ViewController, but
1235
01:02:53,237 --> 01:02:55,370
I'm just going to delete this instead.
1236
01:02:55,372 --> 01:02:56,772
So I'm gonna take this ViewController and
1237
01:02:56,774 --> 01:02:57,405
I'm gonna right-click and
1238
01:02:57,407 --> 01:03:00,041
hit Delete, just get rid of that whole class and
1239
01:03:00,043 --> 01:03:02,277
move it to the trash. Now my storyboard,
1240
01:03:02,279 --> 01:03:04,813
if I look at the Identity inspector over here,
1241
01:03:04,815 --> 01:03:08,016
it still thinks this is a ViewController, okay.
1242
01:03:08,018 --> 01:03:10,652
So I need to create the actual class,
1243
01:03:10,654 --> 01:03:11,820
not this generic ViewController, so
1244
01:03:11,822 --> 01:03:15,190
I'm gonna do that. Gonna create, a iOS Source,
1245
01:03:15,192 --> 01:03:18,460
Cocoa Touch Class, it's gonna be a ViewController, I'm gonna
1246
01:03:18,462 --> 01:03:21,730
call it ImageViewController. I'm gonna have my UI here,
1247
01:03:21,732 --> 01:03:25,100
what it's gonna do is just display an image, okay.
1248
01:03:25,102 --> 01:03:27,769
So it's just, that's just very simple,
1249
01:03:27,771 --> 01:03:29,538
UI is gonna display the image,
1250
01:03:29,540 --> 01:03:32,440
in the whole screen of the phone or the iPad,
1251
01:03:32,442 --> 01:03:35,310
whatever. Okay, so it's gonna an ImageViewController,
1252
01:03:35,312 --> 01:03:38,547
it's gonna display that. We'll put it in the same place we
1253
01:03:38,549 --> 01:03:39,614
put all these other things over here.
1254
01:03:39,616 --> 01:03:43,318
All right, here it is. Okay, you can see we have
1255
01:03:43,320 --> 01:03:45,420
a couple of ViewController life cycles here, here.
1256
01:03:45,422 --> 01:03:46,888
I'm not going to be a bad citizen, so
1257
01:03:46,890 --> 01:03:50,759
hopefully I won't have any memory. Then I'm gonna leak,
1258
01:03:50,761 --> 01:03:53,995
I'm also not going to navigate from this MVC,
1259
01:03:53,997 --> 01:03:57,165
okay. On Wednesday, we'll be continuing this demo and
1260
01:03:57,167 --> 01:03:59,467
we will be navigating to this, but not from it,
1261
01:03:59,469 --> 01:04:01,236
so I don't need, the prepareForSegue,
1262
01:04:01,238 --> 01:04:05,440
that's only for things that are navigating from, okay?
1263
01:04:05,442 --> 01:04:07,342
I do need my viewDidLoad() right here, so
1264
01:04:07,344 --> 01:04:10,679
I'm gonna leave that in here. When I create a new MVC
1265
01:04:10,681 --> 01:04:13,815
controller here, you gotta remember to go back here to
1266
01:04:13,817 --> 01:04:17,419
your identity inspector and set the class. If you don't,
1267
01:04:17,421 --> 01:04:19,654
obviously none of your outlets are gonna work right,
1268
01:04:19,656 --> 01:04:21,690
you'll probably crash at launch, all kinds of things,
1269
01:04:21,692 --> 01:04:24,693
okay? So I'm just using the Identity Inspector right here
1270
01:04:24,695 --> 01:04:26,761
to set that to ImageViewController.
1271
01:04:26,763 --> 01:04:29,064
Another thing I like to do when I create an MVC right
1272
01:04:29,066 --> 01:04:32,467
off the bat is think about what is my model, okay?
1273
01:04:32,469 --> 01:04:35,503
So this is an image view controller, what does it do?
1274
01:04:35,505 --> 01:04:39,241
It shows an image, so I'm gonna have its model,
1275
01:04:39,243 --> 01:04:39,674
which is gonna be public,
1276
01:04:39,676 --> 01:04:43,311
I'm gonna allow the people to set it, be the URL
1277
01:04:43,313 --> 01:04:46,748
of the image to display. Okay, so that's my model,
1278
01:04:46,750 --> 01:04:50,919
it's a URL. It could be a file on the local filesystem, could
1279
01:04:50,921 --> 01:04:55,056
be something over the network, okay, that's what it is.
1280
01:04:55,058 --> 01:04:59,895
Now to display this imageURL's image, I need an image view.
1281
01:04:59,897 --> 01:05:03,365
Now we have created all of our classes,
1282
01:05:03,367 --> 01:05:06,401
all our views by going over here and finding things,
1283
01:05:06,403 --> 01:05:07,702
like there's image view down here somewhere,
1284
01:05:07,704 --> 01:05:09,871
here it is right here, and we've been dragging them out.
1285
01:05:09,873 --> 01:05:12,574
And I could drag this out and all this stuff, but I want to
1286
01:05:12,576 --> 01:05:15,110
start showing a little bit how to create views in code.
1287
01:05:15,112 --> 01:05:18,680
So we're going to create this image view in our controller's
1288
01:05:18,682 --> 01:05:21,116
code, and in fact I'm just going to create a private
1289
01:05:21,118 --> 01:05:23,985
var for it, which I'm going to call my image view, okay?
1290
01:05:23,987 --> 01:05:28,156
And I'm just going to set it equal to UIImageView.
1291
01:05:28,158 --> 01:05:29,824
So I'm creating one right here.
1292
01:05:29,826 --> 01:05:32,861
I'm using the init that takes no arguments, so that's going
1293
01:05:32,863 --> 01:05:36,932
to create an image view that the frame is 0000, okay? Tiny,
1294
01:05:36,934 --> 01:05:39,968
tiny little ImageView. So I'm going to have to be careful to
1295
01:05:39,970 --> 01:05:45,473
reset it's frame whenever I put an image in there. Okay,
1296
01:05:45,475 --> 01:05:46,174
so now I have this ImageView,
1297
01:05:46,176 --> 01:05:50,812
that's cool. I need my viewDidLoad right down here,
1298
01:05:50,814 --> 01:05:56,084
okay. After I'm loaded I'm going to,
1299
01:05:56,086 --> 01:06:01,156
just take this, ImageView that I created and
1300
01:06:01,158 --> 01:06:05,093
add it as a subview of my top level view.
1301
01:06:05,095 --> 01:06:09,331
So I'm gonna say view.addSubview my ImageView.
1302
01:06:09,333 --> 01:06:12,867
Okay, so remember this view right here, this var, okay,
1303
01:06:12,869 --> 01:06:14,302
comes along with your UIViewController.
1304
01:06:14,304 --> 01:06:17,839
It's the view that's at the very top, okay?
1305
01:06:17,841 --> 01:06:21,676
So I'm just going to add this image view, in there.
1306
01:06:21,678 --> 01:06:25,647
Now, its frame is 0000 so I'm putting it up in the corner
1307
01:06:25,649 --> 01:06:28,883
there, okay? So right now it's not gonna show anything, but I
1308
01:06:28,885 --> 01:06:31,953
haven't put any image in there either, so nothing's gonna
1309
01:06:31,955 --> 01:06:34,956
happen. So, I'm gonna kill two birds with one stone and
1310
01:06:34,958 --> 01:06:37,459
deal with that and create a new private var, which I'm
1311
01:06:37,461 --> 01:06:42,464
gonna call image. It's gonna be a UI Image, optional, okay?
1312
01:06:42,466 --> 01:06:44,866
And this is gonna be a computed var, okay? And
1313
01:06:44,868 --> 01:06:48,636
in the set for this, this is when I'm gonna set my image,
1314
01:06:48,638 --> 01:06:52,073
okay? So, this var, anytime I want to set the image that I'm
1315
01:06:52,075 --> 01:06:55,043
showing, I'm gonna set this var. And every, and I
1316
01:06:55,045 --> 01:06:57,579
could also use it for getting as you'll see in a second, but
1317
01:06:57,581 --> 01:06:58,313
I'm just gonna set it. So
1318
01:06:58,315 --> 01:07:00,048
what do I need to do when I set an image, okay.
1319
01:07:00,050 --> 01:07:03,218
Well there's two things I need to do. One is I need to set
1320
01:07:03,220 --> 01:07:05,420
the imageView's image to that image,
1321
01:07:05,422 --> 01:07:09,057
which is the newValue right? So now I've taking
1322
01:07:09,059 --> 01:07:12,927
my image view right here and I'm giving it the image. But
1323
01:07:12,929 --> 01:07:17,866
I know I need to reset my frame, okay, to fit this new
1324
01:07:17,868 --> 01:07:22,604
image. So I'm also gonna say imageView.sizeToFit. Okay, so
1325
01:07:22,606 --> 01:07:27,075
this is a method on imageView that will size the imageView
1326
01:07:27,077 --> 01:07:31,513
to fit whatever image is in it, okay? Now the get is
1327
01:07:31,515 --> 01:07:35,183
really easy. I'm just going to return the image view's image.
1328
01:07:35,385 --> 01:07:37,185
So this is an interesting var right here,
1329
01:07:37,187 --> 01:07:43,358
it's a compute a var, that it's a computed var and
1330
01:07:43,360 --> 01:07:45,894
it's actually storing its data somewhere else.
1331
01:07:45,896 --> 01:07:50,565
It's storing it in the image view, but it's intervening
1332
01:07:50,567 --> 01:07:54,969
when that value gets set to do this little size to fit, okay,
1333
01:07:54,971 --> 01:07:57,872
everyone understand why I have this image var? So
1334
01:07:57,874 --> 01:08:00,241
now the rest of my code looks really cool. Anytime I want to
1335
01:08:00,243 --> 01:08:02,177
set an image I just say image equals whatever and
1336
01:08:02,179 --> 01:08:05,947
it's going to set it in image view and size to fit it.
1337
01:08:06,116 --> 01:08:10,385
Okay, now what else do we need to do here?
1338
01:08:10,387 --> 01:08:12,287
So we've got this nice image thing,
1339
01:08:12,289 --> 01:08:15,890
one thing we wanna do is whenever our model gets set,
1340
01:08:15,892 --> 01:08:18,593
we want to go load that image up. So,
1341
01:08:18,595 --> 01:08:22,397
I'm gonna do a didSet here in my model. And
1342
01:08:22,399 --> 01:08:24,999
I'm first going to set whatever image I have to nil,
1343
01:08:25,001 --> 01:08:26,267
so if I have an image that I'm showing,
1344
01:08:26,269 --> 01:08:28,837
I'm gonna clear that out. And now I'm going to go fetch
1345
01:08:28,839 --> 01:08:31,539
the image that the person is asking me for. And I'm going
1346
01:08:31,541 --> 01:08:34,209
to do that in another little method here, fetchImage.
1347
01:08:34,211 --> 01:08:38,913
Okay, its going to be all private func fetchImage.
1348
01:08:38,915 --> 01:08:42,417
And all this thing is gonna do is gonna go out and look for
1349
01:08:42,419 --> 01:08:44,385
this URL either on the Internet or
1350
01:08:44,387 --> 01:08:48,323
in a local file, okay. So how's it gonna do that?
1351
01:08:48,325 --> 01:08:53,027
Well let's see if that URL is not nil first of all, okay so
1352
01:08:53,029 --> 01:08:57,265
if I can let, oops, let the URL equal that image URL,
1353
01:08:57,267 --> 01:09:00,969
okay. Now I know I have a real URL and I can go get it. So
1354
01:09:00,971 --> 01:09:05,273
how do I get the data from the URL to correspond to an image?
1355
01:09:05,275 --> 01:09:08,576
Well I actually use the class NSData. Remember I mentioned
1356
01:09:08,578 --> 01:09:11,045
that class NSData that gets a bag of bits?
1357
01:09:11,047 --> 01:09:13,314
It knows how to go out to a URL on the internet and
1358
01:09:13,316 --> 01:09:17,552
get the bag of bits. So I'm gonna say if let imageData
1359
01:09:17,554 --> 01:09:23,024
equal the NSData's contents of URL it's called, this URL,
1360
01:09:23,026 --> 01:09:27,996
now I have the imageData. I just need to create a UIImage
1361
01:09:27,998 --> 01:09:30,198
out of that and I'm good to go. So let's do that.
1362
01:09:30,200 --> 01:09:35,670
I'm gonna set my image equal to the UIImage that is made
1363
01:09:35,672 --> 01:09:42,610
with that imageData. Okay, so this is an intializer for
1364
01:09:42,612 --> 01:09:46,581
UIImage which takes the raw JPEG data basically. So
1365
01:09:46,583 --> 01:09:49,884
here I got the raw JPEG or png or whatever it is data.
1366
01:09:49,886 --> 01:09:52,320
And here I'm just creating a UIImage out of it.
1367
01:09:52,322 --> 01:09:53,721
And then I say image equals,
1368
01:09:53,723 --> 01:09:56,124
which is gonna cause this setter to happen.
1369
01:09:56,126 --> 01:10:00,094
Which is gonna set the image in my imageView and resize my
1370
01:10:00,096 --> 01:10:04,699
imageView to fit, all right? [COUGH] And then, we're good
1371
01:10:04,701 --> 01:10:09,337
to go. Make sense? This code's pretty simple code,
1372
01:10:09,339 --> 01:10:12,373
but I've intentionally broken it down into pieces like this
1373
01:10:12,375 --> 01:10:15,276
to show you how you can use some of these Swift mechanisms
1374
01:10:15,278 --> 01:10:19,447
to compartmentalize your code and make things simpler. Like
1375
01:10:19,449 --> 01:10:22,283
this is very simple to set the image, because it's taken some
1376
01:10:22,285 --> 01:10:28,223
of the image related handling stuff out of band there.
1377
01:10:28,225 --> 01:10:29,891
Now we need an image to show, okay, so
1378
01:10:29,893 --> 01:10:32,627
the first thing I am gonna show is a little demo image,
1379
01:10:32,629 --> 01:10:35,296
so I actually have a little class right here with some
1380
01:10:35,298 --> 01:10:38,866
demo URLs. Let's go take a look at this. It is actually
1381
01:10:38,868 --> 01:10:42,070
just a struct with only static stuff in it, okay? So
1382
01:10:42,072 --> 01:10:44,606
I've got a little Stanford URL here, to show some image of
1383
01:10:44,608 --> 01:10:47,141
Stanford, and I've also got some NASA images, which we're
1384
01:10:47,143 --> 01:10:51,212
going to show when we get to this demo on Wednesday, okay?
1385
01:10:51,214 --> 01:10:56,251
So I'm just going to go here to my viewDidLoad, all right,
1386
01:10:56,253 --> 01:11:01,456
and I'm going to say that my image URL, that's my model,
1387
01:11:01,458 --> 01:11:04,626
all right. I set my model here equal to,
1388
01:11:04,628 --> 01:11:09,097
I want to say it's equal to the demoURL.stanford, okay,
1389
01:11:09,099 --> 01:11:13,167
this is the demoURL.stanford that I have over here,
1390
01:11:13,169 --> 01:11:16,938
demoURL.stanford. Unfortunately this is
1391
01:11:16,940 --> 01:11:21,142
a string, okay, URLs are actually classes,
1392
01:11:21,144 --> 01:11:26,314
okay, in iOS, so I have to use an NSURL constructor,
1393
01:11:26,316 --> 01:11:31,653
which takes a string and turns it into a URL.
1394
01:11:33,223 --> 01:11:39,494
Got it? Okay, so let's go ahead and run this.
1395
01:11:49,172 --> 01:11:51,873
Okay, didn't work. It's blank and I have a little
1396
01:11:51,875 --> 01:11:54,275
error down here. And this is a very interesting error.
1397
01:11:54,277 --> 01:11:57,145
Okay, you're gonna wanna pay attention here cuz it's gonna
1398
01:11:57,147 --> 01:11:59,280
happen when you do your. Homework next week.
1399
01:11:59,282 --> 01:12:03,451
It says App Transport Security has blocked a resource load
1400
01:12:03,453 --> 01:12:08,389
since it is insecure. By def, by default, only https,
1401
01:12:08,391 --> 01:12:12,927
secure http calls, are allowed to go through. If you
1402
01:12:12,929 --> 01:12:16,731
want insecure calls like this, okay, that's because my URL
1403
01:12:16,733 --> 01:12:21,436
happens to be this kind. If you want that to go through,
1404
01:12:21,438 --> 01:12:24,439
you have to put an entry in your info.p list.
1405
01:12:24,441 --> 01:12:27,408
Okay, so this is what it like, looks like to put something
1406
01:12:27,410 --> 01:12:30,478
in your info.p list, you just click on info.p list,
1407
01:12:30,480 --> 01:12:31,779
so that's showing here, okay?
1408
01:12:31,781 --> 01:12:35,483
And then you right click and say add row, okay?
1409
01:12:35,485 --> 01:12:38,119
When you add a row, you can see there's [LAUGH] dozens and
1410
01:12:38,121 --> 01:12:40,855
dozens of different things you can control in your p list
1411
01:12:40,857 --> 01:12:43,224
here. We actually want the third one down here,
1412
01:12:43,226 --> 01:12:46,594
which is App, App Transport Security Settings.
1413
01:12:46,596 --> 01:12:49,330
See that right there? So App Transport Security Settings,
1414
01:12:49,332 --> 01:12:52,100
Settings is a dictionary. So we click this to go down
1415
01:12:52,102 --> 01:12:54,902
arrow, which means we wanna edit what's in the dictionary.
1416
01:12:54,904 --> 01:12:59,707
And we just hit plus, and it's gonna add a key value here.
1417
01:12:59,709 --> 01:13:01,843
So there's two different keys can be in that one.
1418
01:13:01,845 --> 01:13:05,012
We want Allow Arbitrary Loads. That allows us to load
1419
01:13:05,014 --> 01:13:11,052
arbitrary URLs, okay. And we wanna set it from NO to YES.
1420
01:13:11,054 --> 01:13:13,254
Okay, so that means we're gonna allow arbitrary loads.
1421
01:13:13,256 --> 01:13:16,324
So you're definitely gonna need to do that, for
1422
01:13:16,326 --> 01:13:21,162
your homework. So now we run, we'll go out there.
1423
01:13:21,164 --> 01:13:22,930
There it is. Okay, just a picture of Stanford.
1424
01:13:22,932 --> 01:13:26,334
Now the thing is, it's kinda good, but we can't see,
1425
01:13:26,336 --> 01:13:29,203
we can only see the left half of the quad here, and
1426
01:13:29,205 --> 01:13:30,605
I can't scroll because of course,
1427
01:13:30,607 --> 01:13:33,608
we're not in a scroll view, all right? So how we,
1428
01:13:33,610 --> 01:13:36,310
how do we put this image view into a scroll view so
1429
01:13:36,312 --> 01:13:39,347
that we can drag it around and look at the whole thing?
1430
01:13:39,349 --> 01:13:42,617
Okay, now for this one, for adding a scroll view, I could
1431
01:13:42,619 --> 01:13:46,487
do that in code as well, just like I did the image view. But
1432
01:13:46,489 --> 01:13:49,424
to show you that it's not exclusive which you do,
1433
01:13:49,426 --> 01:13:52,126
I'm gonna put the scroll view in the storyboard,
1434
01:13:52,128 --> 01:13:54,228
and I'll put the image view in the code and
1435
01:13:54,230 --> 01:13:56,998
we can mix them, okay? So how do I do that?
1436
01:13:57,000 --> 01:13:59,100
I'm gonna go to my storyboard right here, and
1437
01:13:59,102 --> 01:14:02,170
I'm gonna go grab a scroll view outta here,
1438
01:14:02,172 --> 01:14:05,440
I'm gonna search for it, actually. Scroll, here it is,
1439
01:14:05,442 --> 01:14:07,608
Scroll View. So, you're gonna drag it out and
1440
01:14:07,610 --> 01:14:15,616
use the blue lines to put it in the edges here. Like that.
1441
01:14:15,618 --> 01:14:18,686
Okay, I'm gonna reset to suggested constraints.
1442
01:14:18,688 --> 01:14:20,688
I'm gonna go over here to my Identity Inspector,
1443
01:14:20,690 --> 01:14:23,157
which I always like to do when I do that reset, and
1444
01:14:23,159 --> 01:14:25,893
make sure you have trailing leading top, bottom.
1445
01:14:25,895 --> 01:14:27,728
Perfect, that's exactly what I want.
1446
01:14:27,730 --> 01:14:29,564
I obviously need an outlet to this scroll
1447
01:14:29,566 --> 01:14:30,631
view if I'm gonna talk to it and
1448
01:14:30,633 --> 01:14:33,935
be its delegate, and things like that. So I'm going to get
1449
01:14:33,937 --> 01:14:36,604
both of these on screen at the same time, my controller and
1450
01:14:36,606 --> 01:14:41,042
my scroll view. Let's go ahead and control drag here,
1451
01:14:41,044 --> 01:14:45,112
this right here. I'm gonna call this outlet scroll view.
1452
01:14:45,114 --> 01:14:48,483
Notice here's the weak, strong by the way, okay? If my scroll
1453
01:14:48,485 --> 01:14:51,285
view leaves my view hierarchy, it can set me to nil, I am
1454
01:14:51,287 --> 01:14:55,756
fine. Connect, okay, we've got our scroll view here. Now,
1455
01:14:55,758 --> 01:15:00,895
all we need to do is set the image view as a subview
1456
01:15:00,897 --> 01:15:05,032
of our scroll view instead of as a sub, subview of our top
1457
01:15:05,034 --> 01:15:08,102
level view. So, really, all I need to do is change this
1458
01:15:08,104 --> 01:15:12,640
to scrollView, okay, and it's gonna add as a subview. Now,
1459
01:15:12,642 --> 01:15:16,410
let's see, is this gonna work? Probably not cuz I wouldn't
1460
01:15:16,412 --> 01:15:19,981
have asked that question. Why is this not gonna work,
1461
01:15:19,983 --> 01:15:21,616
do you think? It looks like it worked,
1462
01:15:21,618 --> 01:15:25,920
right? Let's scroll, it's not, why isn't this scrolling?
1463
01:15:25,922 --> 01:15:28,956
What's going on? I put a scrollView in there. Is it,
1464
01:15:28,958 --> 01:15:30,424
what's wrong with this thing? Okay,
1465
01:15:30,426 --> 01:15:33,361
anyone have any idea why this is not scrolling?
1466
01:15:33,363 --> 01:15:34,262
>> Content size.
1467
01:15:34,264 --> 01:15:35,096
>> Bingo.
1468
01:15:35,098 --> 01:15:36,163
If I had a can, if I was Mehran,
1469
01:15:36,165 --> 01:15:39,433
I'd be throwing candy out to you. Yeah, absolutely,
1470
01:15:39,435 --> 01:15:42,737
no content size, so it's currently scrolling fine over
1471
01:15:42,739 --> 01:15:47,008
a zero by zero area, okay. And the image is just bleeding out
1472
01:15:47,010 --> 01:15:49,777
from the edges of it, okay? So we need to set the content
1473
01:15:49,779 --> 01:15:52,346
size. Where do we set the content size? Well, one place
1474
01:15:52,348 --> 01:15:55,316
we definitely need to set it is anytime we have an image
1475
01:15:55,318 --> 01:15:58,152
because we want the content size to fit the whole image.
1476
01:15:58,154 --> 01:16:02,590
So here, I would just say scrollView.contentSize,
1477
01:16:02,592 --> 01:16:09,163
contentSize = what, ever our image view's frame size is.
1478
01:16:09,165 --> 01:16:11,632
One thing I'm gonna do here is kinda tricky,
1479
01:16:11,634 --> 01:16:15,336
is I'm gonna put a question mark right there. Why might I
1480
01:16:15,338 --> 01:16:20,875
put a question mark there? Because this scrollView
1481
01:16:20,877 --> 01:16:25,146
is an outlet. If this image setting happened, for example,
1482
01:16:25,148 --> 01:16:28,783
when someone was preparing me, this would crash.
1483
01:16:28,785 --> 01:16:31,686
Cuz my scrollView wouldn't be set in prepare. Now no one,
1484
01:16:31,688 --> 01:16:33,921
no one is yet segueing to me. But on Wednesday,
1485
01:16:33,923 --> 01:16:36,257
they're going to be. [LAUGH] So I have to be careful here.
1486
01:16:36,259 --> 01:16:39,193
So here, if I haven't set up my scrollView and
1487
01:16:39,195 --> 01:16:42,697
I get an image, I'm just gonna do nothing. Well,
1488
01:16:42,699 --> 01:16:45,466
then I better do something when the scrollView gets set,
1489
01:16:45,468 --> 01:16:47,668
okay? When the scrollView comes along later, later and
1490
01:16:47,670 --> 01:16:49,937
gets set, I better do this content size, so
1491
01:16:49,939 --> 01:16:52,373
I better do it in both places here. And here,
1492
01:16:52,375 --> 01:16:56,978
we don't need that, okay? See why I'm doing in both places,
1493
01:16:56,980 --> 01:16:58,613
cuz I'm not sure which is gonna happen first.
1494
01:16:58,615 --> 01:17:01,215
Is my scroll view gonna get set first, or my outlet,
1495
01:17:01,217 --> 01:17:03,985
you know, or my, model and thus my image gonna be set
1496
01:17:03,987 --> 01:17:09,924
first? Okay? Okay, so now let's run, see if this works.
1497
01:17:14,230 --> 01:17:15,930
All right, here it is. And sure enough,
1498
01:17:15,932 --> 01:17:19,066
scrolled beautifully, okay, and even if we rotate and
1499
01:17:19,068 --> 01:17:23,304
scroll. All right, so that's great, it's working
1500
01:17:23,306 --> 01:17:27,174
wonderfully. On Wednesday, we'll talk about zooming cuz
1501
01:17:27,176 --> 01:17:30,244
we wanna be able to zoom in on this. Then we're also going to
1502
01:17:30,246 --> 01:17:32,813
talk a little bit about what happens if these images
1503
01:17:32,815 --> 01:17:35,416
are huge and take a long time to download. That's hard for
1504
01:17:35,418 --> 01:17:38,285
me to demo on this network cuz this network is so fast.
1505
01:17:38,287 --> 01:17:41,188
But I have some huge NASA images that I think will go
1506
01:17:41,190 --> 01:17:42,423
slow enough that you're gonna see my
1507
01:17:42,425 --> 01:17:45,159
UI is really sluggish as I wait for these,
1508
01:17:45,161 --> 01:17:47,662
things to download. And that's why we use multithreading,
1509
01:17:47,664 --> 01:17:50,097
Wednesday's topic, to figure out how to make it all really,
1510
01:17:50,099 --> 01:17:53,300
really, really snappy. Okay, that's it,
1511
01:17:53,302 --> 01:17:55,102
I'll see you then. >> For
1512
01:17:55,104 --> 01:17:55,135
more, please visit us at stanford.edu.
================================================
FILE: subtitles/8. Multithreading and Text Field.srt
================================================
1
00:00:00,001 --> 00:00:03,802
[MUSIC]
2
00:00:03,804 --> 00:00:08,040
Standford University. >> All right,
斯坦福大学
3
00:00:08,042 --> 00:00:13,746
welcome then to lecture number eight. I think it's eight,
欢迎参加第八讲。好像是第八讲。
4
00:00:13,748 --> 00:00:18,083
yes, of CS193P Spring of 2016. So today, we start off
是的,2016 春季 CS193P 课程。今天,我们将
5
00:00:18,085 --> 00:00:21,020
by continuing that demo that we were doing last time.
继续完成上一次的 demo
6
00:00:21,022 --> 00:00:24,423
Remember we had the demo, Cassini demo with the, scroll
今天用到之前 scroll view 写的 Cassini
7
00:00:24,425 --> 00:00:27,693
view in there. And so I'm gonna make it more powerful,
让它功能更加强大.
8
00:00:27,695 --> 00:00:30,262
we're gonna get multiple MVCs going in there.
我们将会用到多个 MVC 的结构.
9
00:00:30,264 --> 00:00:34,533
We'll start doing some zooming in the scroll view.
会用到 scroll view 的缩放属性.
10
00:00:34,535 --> 00:00:37,703
Depending on how the time goes I might actually show you some
如果有时间,我会通过实际案例来
11
00:00:37,705 --> 00:00:40,406
things that couple of you have asked about on Piazza.
解答有些同学对 Piazza 项目的疑问.
12
00:00:40,408 --> 00:00:44,009
Like, how to when I launch my calculator app can I have it
例如,在启动我的计算器 app 的时候,
13
00:00:44,011 --> 00:00:46,311
not launch and show a blank graph, right,
因为没有启动图而显示空白的情况.
14
00:00:46,313 --> 00:00:49,314
how can I have it launch and show the calculator for
怎样才能在在第一次启动计算器时,
15
00:00:49,316 --> 00:00:52,651
the very first time, okay, those kinds of things.
显示启动页等,诸如此类的问题.
16
00:00:52,653 --> 00:00:53,652
So we'll see how the time goes and
最后,我们会根据时间安排
17
00:00:53,654 --> 00:00:56,121
decide on the fly whether to do that stuff.
来决定是否要解答这些.
18
00:00:56,123 --> 00:00:57,623
Then we're gonna dive back into the slides.
让我们回到幻灯片上.
19
00:00:57,625 --> 00:00:59,358
I'm gonna talk about multi-threading, okay?
我之后还会讲到多线程.
20
00:00:59,360 --> 00:01:04,029
Very, very important thing in iOS is how to multithread
在 iOS 上,多线程的处理是非常非常重要的内容.
21
00:01:04,031 --> 00:01:06,799
your execution. And then I'll go back to the demo, and
然后我们会回到 Cassini demo,
22
00:01:06,801 --> 00:01:10,436
we'll multithread this Cassini thing we're working on, okay?
为它加上多线程支持.
23
00:01:10,438 --> 00:01:12,104
And then time permitting at the end I'll go back to
在时间允许的情况下, 最后还会回到幻灯片
24
00:01:12,106 --> 00:01:14,673
the slides and I'm gonna talk about text fields. And
讲一下 text field. 然后
25
00:01:14,675 --> 00:01:19,511
then, next week we have table views, databases, etc. And
下周会讲 table view,database 等内容.
26
00:01:19,513 --> 00:01:21,947
again, remember, no assignment going out today.
今天不会布置作业.
27
00:01:21,949 --> 00:01:24,283
The assignment is gonna be a table view based thing, so
作业是基于 table view 的,
28
00:01:24,285 --> 00:01:27,986
it will go out after Monday's lecture, all right? So,
所以我们会在下周一课程之后再布置, 没问题吧.
29
00:01:27,988 --> 00:01:30,589
let's dive back into this Cassini demo. Okay,
那么, 让我们回到 Cassini demo.
30
00:01:30,591 --> 00:01:36,762
we'll start off just exactly where we left off before. So
接着上一次的内容继续完善.
31
00:01:36,764 --> 00:01:41,100
we'll go to Xcode here. So I'm just gonna open Cassini,
启动 Xcode, 打开 Cassini
32
00:01:41,102 --> 00:01:42,868
here it is. I'm just gonna run it real quick just
我先来带大家快速过一遍
33
00:01:42,870 --> 00:01:47,139
to remind you where we were for those with short memories.
回忆下之前的内容.
34
00:01:47,141 --> 00:01:53,812
Even though it's only two days ago. All right,
虽然是两天前才讲的.
35
00:01:53,814 --> 00:01:56,915
here it is. So we've got, our image view, right?
好的, 我们有一个 image view.
36
00:01:56,917 --> 00:02:00,285
We have an ImageView and it's inside a UIScrollView, and so
这个 ImageView 嵌套在 UIScrollView 中,
37
00:02:00,287 --> 00:02:03,922
we're able to scroll around, okay, just by panning with our
所以我们可以进行滚动, 用手指滑动就行
38
00:02:03,924 --> 00:02:06,291
finger. My mouse is like a finger there, so
这光标就是手指
39
00:02:06,293 --> 00:02:10,262
I'm just panning around, and that's all we can do, okay?
目前就只能随意滑动
40
00:02:10,264 --> 00:02:11,997
But we've created this nice UI, or
但是我们这个漂亮的界面
41
00:02:11,999 --> 00:02:15,734
this nice ImageViewController, which is an MVC that lets you
或者说, 漂亮的 ImageViewController 所用的 MVC 模式,
42
00:02:15,736 --> 00:02:19,371
take any image and put it on screen inside the scroll view,
能任意将图片加入到 scroll view 中, 并显示出来
43
00:02:19,373 --> 00:02:23,108
that's kind of a nice reusable, reusable MVC, right?
有很好的重用性, 重用 MVC, 对吧
44
00:02:23,110 --> 00:02:26,378
So now we're going to make this a multi-MVC app, okay,
接下来, 我们会做一个多 MVC 的 app
45
00:02:26,380 --> 00:02:28,213
and I'm gonna reuse this ImageViewController
重用的 ImageViewController
46
00:02:28,215 --> 00:02:31,450
as one of the MVCs. So let's go right to our storyboard,
也是其中之一. 让我们打开 storyboard.
47
00:02:31,452 --> 00:02:32,117
see what this is gonna look like.
来看看里面都有什么内容
48
00:02:32,119 --> 00:02:34,419
This is where the Cassini part of this whole thing is gonna
Cassini 所展示的内容都从这里得到.
49
00:02:34,421 --> 00:02:37,589
come in. All right, I'm gonna create a new View Controller,
现在, 我准备创建一个新的 View Controller
50
00:02:37,591 --> 00:02:39,224
just gonna drag it out right here. Again,
将它拖到这里就可以了
51
00:02:39,226 --> 00:02:41,693
I'm gonna go fairly quickly, cuz I've done this all before,
之前我们已经做过了, 所以这一步操作有点快
52
00:02:41,695 --> 00:02:44,630
so this is almost like a review, for you here.
更像是在帮大家复习
53
00:02:44,632 --> 00:02:45,397
So, I have this controller.
好, controller 创建好了
54
00:02:45,399 --> 00:02:48,700
It needs to have a custom subclass of UIViewController
因为需要自定义 UIViewController
55
00:02:48,702 --> 00:02:51,904
so I'm going to create such a thing. Go here to New File,
所以要做接下来的这些操作. 创建一个新文件
56
00:02:51,906 --> 00:02:55,274
it's iOS source, it's a Cocoa Touch Class. Cuz it's
选择 iOS source, Cocoa Touch Class, 因为
57
00:02:55,276 --> 00:02:58,210
a subclass of an iOS class, which is a UIViewController in
要子类化的类属于 iOS, 这里是 UIViewController
58
00:02:58,212 --> 00:03:01,647
this case. I'm going to call this CassiniViewController,
类名就叫 CassiniViewController 吧
59
00:03:01,649 --> 00:03:04,149
cuz it's gonna let us look at some Cassini images.
这个类会展示更多 Cassini 的图片
60
00:03:04,151 --> 00:03:08,587
That's what our app is gonna do. But just in the same place
也是这个 app 的需求. 存放路径和之前一样
61
00:03:08,589 --> 00:03:11,523
as always here is our CassiniViewController,
CassiniViewController 创建好了
62
00:03:11,525 --> 00:03:15,327
I'm gonna remove the view controller lifecycle methods
暂时先删除模板给出的 view controller
63
00:03:15,329 --> 00:03:18,630
that it gives me right here just for now anyway.
生命周期方法
64
00:03:18,632 --> 00:03:22,067
And I'm actually gonna uncomment this prepareForSegue
然后打开 prepareForSegue 的注释
65
00:03:22,069 --> 00:03:24,002
because my CassiniViewController is
因为 CassiniViewController 会
66
00:03:24,004 --> 00:03:27,005
definitely going to be doing, some segueing.
用到这个方法, 一些 segue
67
00:03:27,007 --> 00:03:29,341
So let's now go back to our storyboard,
现在让我们回到 storyboard
68
00:03:29,343 --> 00:03:32,744
now that we have this ImageViewController class, and
我们已经有了 ImageViewController
69
00:03:32,746 --> 00:03:36,315
I'm gonna change the class of this controller right here
再通过 identity inspector, 将这个
70
00:03:36,317 --> 00:03:40,452
with the identity inspector, to be a CassiniViewController,
controller 改为 CassiniViewController
71
00:03:40,454 --> 00:03:45,724
okay. Everybody got that? I'm going to put this whole
好的, 都能跟上吧? 接下来, 我会将整个
72
00:03:45,726 --> 00:03:49,494
MVC structure into that split view with navigation that's
MVC 结构装到 split view 中
73
00:03:49,496 --> 00:03:52,331
over in there, the thing that will work in both iPad and
这样就能同时用 iPad 和
74
00:03:52,333 --> 00:03:55,300
iPhone, exact same thing we did for,
iPhone 上, 这种处理方式我们在
75
00:03:55,302 --> 00:03:57,869
the emotions view controller. So, let's do that.
emotions view controller 上也做过. 下面来实现吧.
76
00:03:57,871 --> 00:04:00,939
I'm going to start by dragging out the split view controller,
接下来需要拖一个 split view controller
77
00:04:00,941 --> 00:04:02,007
I'm gonna zoom way out here.
先缩小画板
78
00:04:02,009 --> 00:04:04,810
Let's go get a Split View Controller, here it is. I'll
然后找到 Split View Controller
79
00:04:04,812 --> 00:04:09,781
drag it out. I'll get rid of these extra things that came
拖拽出来. 删掉除了
80
00:04:09,783 --> 00:04:12,551
along here with the Split View Controller because I already
Split View Controller 的其他内容, 因为我们已经
81
00:04:12,553 --> 00:04:15,153
have my master and detail right here. The Cassini's
有自己的 master 和 detail 了. Cassini 是
82
00:04:15,155 --> 00:04:17,956
gonna be my master and this ImageViewController's gonna be
master, ImageViewController 是 detail.
83
00:04:17,958 --> 00:04:21,260
my detail right there. So I'm just gonna Control drag, hook
按住 Control 并拖拽到 master
84
00:04:21,262 --> 00:04:25,197
up my master. And a Control drag, hook up my detail.
然后按住 Control 并拖拽到 detail
85
00:04:25,199 --> 00:04:28,400
I'm also gonna take my little entry arrow and make it so
接下来, 将入口箭头移动到
86
00:04:28,402 --> 00:04:31,136
that it enters on the Split View Controller. Right,
Split View Controller
87
00:04:31,138 --> 00:04:32,738
instead of entering on my Image View Controller,
Image View Controller 不再是入口
88
00:04:32,740 --> 00:04:37,376
obviously. And, I want this to work on both iPhone and iPad,
我想要在 iPhone 和 iPad 上使用
89
00:04:37,378 --> 00:04:41,013
so I'm gonna take my master and put it embedded inside
所以, 我准备将 master 嵌入到
90
00:04:41,015 --> 00:04:45,917
of a navigation controller, okay? And maybe I can even go
navigation controller 中, 然后我能在这
91
00:04:45,919 --> 00:04:50,589
in here and put a nice title here, right here. Cassini,
给它加上 title. Cassini.
92
00:04:50,591 --> 00:04:54,626
okay? All right now, what is this UI going to do?
跟上了吗? 最终, 这个界面到底有什么作用?
93
00:04:54,628 --> 00:04:57,029
What's it going to look like? How's it gonna function?
长什么样呢? 怎样触发一些功能?
94
00:04:57,031 --> 00:04:59,598
Well, I'm just gonna have three buttons here.
好的, 我会在这里放 3 个按钮
95
00:04:59,600 --> 00:05:01,500
Similar to how I had in emotions view controller.
和之前的 emotions view controller 很像
96
00:05:01,502 --> 00:05:04,102
In a demo I can only do, I can't do a nice complicated
在 demo 中也只能这样了, 不能像计算器
97
00:05:04,104 --> 00:05:07,172
MVC like a calculator and then another complicated one like
一样有复杂的 MVC 结构, 也不能像
98
00:05:07,174 --> 00:05:09,808
a graph view. I have to do a simple one. So I'm gonna do
graph view 那样复杂. 只能做个简单的界面了. 所以我也只好
99
00:05:09,810 --> 00:05:11,877
a simple one here. Where I'm gonna put three buttons and
这样了. 之所以我要放三个按钮,
100
00:05:11,879 --> 00:05:15,180
those three buttons are just going to cause three different
是因为要展示三张
101
00:05:15,182 --> 00:05:20,285
images from NASA, okay, about Cassini which is a little
NASA 的图片. 至于 Cassini, 它是被
102
00:05:20,287 --> 00:05:22,988
thing sent out into space to take pictures. I'm gonna put
发送到太空来拍摄图片的. 我会把
103
00:05:22,990 --> 00:05:25,223
those three buttons here and then were just gonna segue,
三个按钮放到这, 然后我们处理 segue,
104
00:05:25,225 --> 00:05:27,826
as we know how to do, down to here,
大家应该知道怎么做. 将 ImageViewController
105
00:05:27,828 --> 00:05:29,194
which is gonna be our ImageViewController,
放到下面来
106
00:05:29,196 --> 00:05:30,662
all right. So the Image View Controller's just gonna show
Image View Controller 会用来显示图片
107
00:05:30,664 --> 00:05:33,065
the image, and we already know that the Image View Controller
我们已经知道展示图片的 Image View Controller 是一个
108
00:05:33,067 --> 00:05:35,901
is a nice, little, reasonable MVC that shows an image.
优雅的, 小巧的, 合理的 MVC 结构.
109
00:05:35,903 --> 00:05:37,836
So it's perfect, exactly what I want. So
如此完美, 正是我们想要的
110
00:05:37,838 --> 00:05:39,871
I'm gonna go down here and get the buttons. So it's this
向下滚动, 找到按钮.
111
00:05:39,873 --> 00:05:44,343
button right here. Let's go ahead and make it bigger,
将按钮放在这. 继续, 把它放大点,
112
00:05:44,345 --> 00:05:48,580
I'll say 40 point would be a pretty good size here.
设置为 40 点看起来比较好
113
00:05:48,582 --> 00:05:52,417
And, make three of them, copy, paste so
然后创建三个这样按钮, 复制, 粘贴
114
00:05:52,419 --> 00:05:56,154
I get that. Okay, one of them is gonna be called Earth,
好了, 一个叫做 Earth
115
00:05:56,156 --> 00:05:59,291
another one is gonna be called Cassini.
一个叫做 Cassini.
116
00:05:59,293 --> 00:06:02,194
And another one is gonna be called Saturn, okay?
剩下的一个叫 Saturn, 好了吗?
117
00:06:02,196 --> 00:06:04,262
And then I'm just gonna take these three things.
然后, 将这三个按钮
118
00:06:04,264 --> 00:06:06,031
Of course it put them in a stack view.
放到 stack view 中
119
00:06:06,033 --> 00:06:11,903
Okay, we'll go ahead and have the stack view be fill.
ok, 将 stack view 对齐方式设置为 fill.
120
00:06:11,905 --> 00:06:13,338
And we'll also do again fill equally.
然后将分布设置为 fill equally.
121
00:06:13,340 --> 00:06:15,474
It doesn't really matter because they're all identical,
这些都无关紧要, 它们都是相同的
122
00:06:15,476 --> 00:06:17,909
they're all buttons. We'll put a little spacing in there,
都是按钮. 然后给它们加一些间隙
123
00:06:17,911 --> 00:06:21,179
maybe. 20 points something like that. Then I'm gonna drag
20 点之类的. 接下来将它拖到
124
00:06:21,181 --> 00:06:24,082
this into the middle use my blue lines right there so that
视图中间, 有蓝色对齐线这里
125
00:06:24,084 --> 00:06:27,152
I can go here and say Reset to suggested constraints.
点击这里, 选择 Reset to suggested constraints (重置为建议约束).
126
00:06:27,154 --> 00:06:29,588
And then I'm gonna go over to my size inspector and
接着看到 size inspector
127
00:06:29,590 --> 00:06:32,224
double check that it did the kind of constraints I want.
再次确认约束是否正确添加
128
00:06:32,226 --> 00:06:35,227
Which that looks good to me it's gonna align these things
看来这些约束能正确将
129
00:06:35,229 --> 00:06:39,030
in the center, okay? So, here's our UI right here.
视图居中, 好了吗? 这就是我们的 UI 了.
130
00:06:39,032 --> 00:06:43,802
Now, all we need to do is have these three things segue down
现在, 就只需要将这三个按钮的事件和下面
131
00:06:43,804 --> 00:06:47,472
to here to fill these images. Now, I'll show you where these
这个关联, 来显示图片了. 之后, 我会展示这些
132
00:06:47,474 --> 00:06:49,841
images come from in a minute here, but let's just create
图片从哪来, 现在先来创建 segue.
133
00:06:49,843 --> 00:06:52,110
the segue. Now, I'm gonna do something a little different
这次我们处理 segue 的方式和之前不一样.
134
00:06:52,112 --> 00:06:55,847
with this segue. In our last one I, each segue had
之前, 每个 segue 都有
135
00:06:55,849 --> 00:06:57,816
its own identifier that was different, and
自己的 identifier, 每一个都不同
136
00:06:57,818 --> 00:07:00,619
it was the identifier that let us know which emotion to
这些 identifier 使我们知道应该
137
00:07:00,621 --> 00:07:03,488
show in the emotions one. Here I'm gonna have them all have
显示哪个表情. 这一次, 它们只有
138
00:07:03,490 --> 00:07:06,591
the same identifier, and I'm gonna use the title of
一个相同的 identifier, 我将使用按钮的 title
139
00:07:06,593 --> 00:07:09,628
the button to decide which image, okay? So it's kind of
来决定显示的图片. 所以,
140
00:07:09,630 --> 00:07:11,897
a cross between what we did in the calculator and
这和我们之前做计算器,
141
00:07:11,899 --> 00:07:14,666
what we did in emotions, all right? So, let's go ahead and
做表情的处理不一样.
142
00:07:14,668 --> 00:07:17,969
create these things. So, I'm gonna, Control drag, okay,
下面我们来创建 segue 吧. 按钮 Contorl 并拖拽,
143
00:07:17,971 --> 00:07:21,540
from Saturn down to here. This is in a split view, right,
从 Saturn 到这里. 他们都在 split view 中.
144
00:07:21,542 --> 00:07:22,374
so I'm gonna do Show Detail, right?
选择 Show Detail.
145
00:07:22,376 --> 00:07:25,143
That's the kind of segue I want, not show. The same thing
这才是我想要的 segue, 而不是 Show.
146
00:07:25,145 --> 00:07:29,514
with Cassini right here, Show Detail. And then up here to
Cassini 也是同样, ShowDetail. 接着是
147
00:07:29,516 --> 00:07:34,486
Earth, Show Detail. Whoops, not Present Modally, undo,
Earch, Show Detail. 噢, 不是 Present Modally, 撤销,
148
00:07:34,488 --> 00:07:38,990
let's try that again. Show Detail. Okay, so
重新来一次, Show Detail.
149
00:07:38,992 --> 00:07:41,092
we've got these three things here. Let's go ahead and
这三个就关联好了. 然后来检查下.
150
00:07:41,094 --> 00:07:43,662
inspect them. I'm gonna put an identifier on them but
我准备给它们设置 identifier, 但
151
00:07:43,664 --> 00:07:45,163
they're all gonna have the same identifier.
它们的 identifier 都相同.
152
00:07:45,165 --> 00:07:48,266
And what that means is when prepareForSegue gets called,
那么, 这在调用 prepareForSegue 时意味着什么呢
153
00:07:48,268 --> 00:07:51,169
it's gonna be called, and the same thing is gonna happen for
调用时, 他们所触发的事件都是相同的.
154
00:07:51,171 --> 00:07:53,505
each one. Again I'm gonna look at the sender in
不过, 可以通过
155
00:07:53,507 --> 00:07:54,706
prepareForSegue to know which button,
prepareForSegue 的 sender 来区别按钮,
156
00:07:54,708 --> 00:07:57,175
but we're gonna use the same code. So I'm gonna call this
但是它们的代码是共用的. 所以, 就命名为
157
00:07:57,177 --> 00:08:00,345
Show Image, cuz that's what it does, okay, when you click On
Show Image 吧, 见名知意.
158
00:08:00,347 --> 00:08:03,381
here to segue, it segues to this image view controller and
点击 Segue, 跳转到 image view controller
159
00:08:03,383 --> 00:08:06,585
shows an image. Okay, so let's make sure all three of
显示图片. 确保三个按钮都
160
00:08:06,587 --> 00:08:10,755
them are doing that. Okay, so everybody cool with this?
设置好. 好了, 大家对这一步有疑问吗?
161
00:08:10,757 --> 00:08:14,092
This is all just review, I haven't done anything new, yet
以上是对之前的回顾,还没添加新的知识点
162
00:08:14,094 --> 00:08:17,829
here. K, now of course we know if we want these
如果想要这些 segue 生效
163
00:08:17,831 --> 00:08:21,366
little segues here to work, we need to prepare them, so
还需要准备 segue
164
00:08:21,368 --> 00:08:25,003
let's go back to our Cassini View Controller right here and
接下来回到 Cassini View Controller,
165
00:08:25,005 --> 00:08:27,973
do our prepare for segue. I'm gonna show you something
准备 segue. 接下来我会演示如何
166
00:08:27,975 --> 00:08:31,109
that's kind of nice to do from a clean coding mechanism,
在用纯代码来实现
167
00:08:31,111 --> 00:08:35,747
which is I like to create a private struct here to
我比较喜欢创建一个私有 struct
168
00:08:35,749 --> 00:08:38,750
store my constants that are strings in the storyboard.
来存放 storyboard 中 string 类型的常量
169
00:08:38,752 --> 00:08:42,754
So I usually call this thing storyboard okay? And then I
所以命名为 storyboard
170
00:08:42,756 --> 00:08:47,225
just put these static lets inside. So the ShowImageSegue
然后将这些静态常量放到 struct 中. ShowImageSegue
171
00:08:47,227 --> 00:08:51,296
= "Show Image". So that this string, and basically any
= "Show Image". 就是这样. 在
172
00:08:51,298 --> 00:08:55,400
string that I put anywhere in my story board, like in these,
storyboard 中的其他 string 也都和这个类似,
173
00:08:55,402 --> 00:08:58,236
when I inspect these things down here like this string.
比如看到的这个下面这个 string
174
00:08:58,238 --> 00:09:02,440
Okay? That's all collected here into this nice struct.
将这些都添加到 struct 中
175
00:09:02,442 --> 00:09:03,208
Okay? So
到此,
176
00:09:03,210 --> 00:09:05,644
we already know that this is what we do to do constants,
我们已经知道怎样处理这些常量了
177
00:09:05,646 --> 00:09:08,113
okay? I'm just calling this group of constants storyboard.
我暂且把这个集合称为 storyboard 常量集吧
178
00:09:08,115 --> 00:09:10,882
They're my storyboard constants, all right?
这就是我的 storyboard 常量集了
179
00:09:10,884 --> 00:09:14,252
All right, so let's do prepare for segue right here.
然后在这里准备 segue
180
00:09:14,254 --> 00:09:15,820
What do we need to do in prepare for segue?
我们需要在 prepareForsSegue 中做哪些处理?
181
00:09:15,822 --> 00:09:19,324
Well first of all, we probably want to make sure that this is
首先,我们要确保 segue 是
182
00:09:19,326 --> 00:09:23,194
the show image segue, so I'm going to say if the segue's
show image segue,所以先进行判断
183
00:09:23,196 --> 00:09:28,366
identifier = the storyboard.ShowImageSegue.
segue 的 identifier 是否等于 Storyboard.ShowImageSegue
184
00:09:28,368 --> 00:09:31,469
Okay? Then we know this is what we're preparing for.
这一步是保证我们处理的是正确的 segue
185
00:09:31,471 --> 00:09:34,539
And remember,
记住,在一个复杂的 app 中,可能会有很多不同的
186
00:09:34,541 --> 00:09:37,309
of segues, like we have in emotions actually, with
segue,就像我们有不同的情绪一样,它们有
187
00:09:37,311 --> 00:09:39,911
different identifiers going off. So that's why we almost
不同的标识. 所以我们
188
00:09:39,913 --> 00:09:43,114
always check the identifier first just to make sure
通常使用 identifier 来确保
189
00:09:43,116 --> 00:09:44,583
that we are doing the right thing, and
处理方式是对的
190
00:09:44,585 --> 00:09:48,153
usually we'll try and spell identifier properly. Okay.
当然,还需要正确的拼写 identifier
191
00:09:48,155 --> 00:09:52,157
So now, we know this is the right segue. We need to get
现在,我们知道这里就是想要的 segue 了. 还需要
192
00:09:52,159 --> 00:09:55,293
the MVC that we're segueing to so we can prepare it.
有一个 MVC 结构来准备 segue
193
00:09:55,295 --> 00:09:57,662
So we know that's supposed to be an Image View Controller so
假设需要的是 Image View Controller
194
00:09:57,664 --> 00:10:00,999
I'm going to say if I can let IVC, short for
所以定义一个叫做 ivc 的常量
195
00:10:01,001 --> 00:10:01,900
Image View Controller,
ImageViewController 的简写
196
00:10:01,902 --> 00:10:07,772
equal the segue's destination view controller as
等于 segue 的 destinationViewController
197
00:10:07,774 --> 00:10:11,843
an image view controller. Okay.
转换为 ImageViewController
198
00:10:11,845 --> 00:10:15,513
So if I, I don't even need the parentheses there, sorry. So
这里不需要大括号,不好意思
199
00:10:15,515 --> 00:10:19,217
if I can get that destination view controller as an image
如果能正确将 destinationViewController 转为
200
00:10:19,219 --> 00:10:24,122
view controller then now I'm ready to prepare this thing.
ImageViewController,那么就可以开始做一些准备了
201
00:10:24,124 --> 00:10:26,024
So, how do I need to prepare it? Well,
怎样去准备呢?
202
00:10:26,026 --> 00:10:29,828
it depends on which button Is doing this show image, okay.
这要取决于哪个按钮触发显示图片事件
203
00:10:29,830 --> 00:10:32,564
So, I'm actually gonna look back at the button. Now,
所以,回头看看这些按钮
204
00:10:32,566 --> 00:10:34,966
here's the button. Remember it prepares for segue.
这个参数就是按钮,并且是谁触发的 segue,
205
00:10:34,968 --> 00:10:38,970
The sender is the button or the line in a table or
那这个 sender 就是谁,可能是按钮或者 table 的某一行
206
00:10:38,972 --> 00:10:40,772
something like that that caused it to happen.
或者其他触发事件的对象
207
00:10:40,774 --> 00:10:43,108
So, I need to convert this into a button. So
所以,我需要将它强转为 button
208
00:10:43,110 --> 00:10:46,411
I could actually do this like this. I could say, if I can
我可以这样做,如果 sender
209
00:10:46,413 --> 00:10:52,017
let the sending button equal the sender as a UI button,
是 UIButton,并解包给 sendingButton 不为空
210
00:10:52,019 --> 00:10:55,887
then I can go forward here and say let's let the image name,
那么就可以定义一个 imageName
211
00:10:55,889 --> 00:10:57,822
that's the name of the image we wanna show,
代表我们想要显示的图片名称
212
00:10:57,824 --> 00:11:02,827
equal the sending button's current title. Oops,
并将 sendingButton 的当前图片,哦,不
213
00:11:02,829 --> 00:11:05,363
not current image, current title. All right, so
当前标题.
214
00:11:05,365 --> 00:11:06,531
this'll be one way to do this,
这是一种处理方式
215
00:11:06,533 --> 00:11:11,603
okay, but kind of a cool way here is, this is this, right?
但是还有一种更好的方法,看这个和这个
216
00:11:11,605 --> 00:11:14,205
So what if I took this, cut and
如果我将这个
217
00:11:14,207 --> 00:11:17,375
put it in here like that. Okay, so
剪切到这里
218
00:11:17,377 --> 00:11:21,479
I don't need this except for this is If, let. So, if I put
那就不再需要这个 if let 了,但如果
219
00:11:21,481 --> 00:11:24,616
this in here, this is going to be an optional. I can't send
将它放到这里,那这就会是个可选值,不能从可选值
220
00:11:24,618 --> 00:11:28,053
current title to an optional. So, how can I deal with that?
中直接访问 currentTitle 属性. 该怎么办呢?
221
00:11:28,055 --> 00:11:33,124
Let's use optional chaining. Okay? So it's perfectly legal
让我们试试可选链. 在表达式返回可选值的时候
222
00:11:33,126 --> 00:11:36,494
to use the optional chaining stuff on the end of some,
使用可选链来操作
223
00:11:36,496 --> 00:11:39,898
some other expression that returns a possible optional.
是完全合法的
224
00:11:39,900 --> 00:11:42,400
And remember, if this is nil right here,
如果这里是 nil
225
00:11:42,402 --> 00:11:43,868
then this whole thing's just gonna be ignored.
那么之后的都会被忽略
226
00:11:43,870 --> 00:11:46,571
That's what the question mark means, it means if it's nil,
这个问号就是表示可能为 nil
227
00:11:46,573 --> 00:11:48,206
nil then just return nil and ignore it.
遇到 nil 会返回 nil,然后忽略掉
228
00:11:48,208 --> 00:11:52,677
That means imageName is gonna be returned to nil. Okay.
也就是说 imageName 可能为 nil
229
00:11:52,679 --> 00:11:55,947
See how I did that? This cleans up our code a little
接下来做什么呢?先把代码
230
00:11:55,949 --> 00:11:58,616
bit, to do that. Less if then, if thens and
整理一下. 减少分支语句
231
00:11:58,618 --> 00:12:02,554
all that, business around there. Okay? Now I'm going
接下来,要根据
232
00:12:02,556 --> 00:12:06,291
to go get the image that goes with this button's title.
按钮的 title 来获取图片
233
00:12:06,293 --> 00:12:07,659
Now let me show you how we're gonna do that.
让我来演示一下
234
00:12:07,661 --> 00:12:10,462
This DemoURL class, remember the one that had that little
这个 DemoURL 类,有一个 Stanford 的 URL
235
00:12:10,464 --> 00:12:14,165
Stanford URL in there? Well it also had these NASA URLs,
还有 NASA 的 URL
236
00:12:14,167 --> 00:12:16,568
one for Cassini, one for Earth, and one for Saturn.
里面包含 Cassini,Earth 和 Saturn
237
00:12:16,570 --> 00:12:17,802
Those are the three buttons we have. And
对应着我们的三个按钮
238
00:12:17,804 --> 00:12:20,939
I have a little function right here that just gets the name
这还有个简单的方法,接收图片名称
239
00:12:20,941 --> 00:12:24,676
of the image and looks it up in that dictionary right here,
然后在字典中查找
240
00:12:24,678 --> 00:12:27,979
okay, and returns it. So this a little function called
并返回,所以这个方法叫
241
00:12:27,981 --> 00:12:30,815
NASA Image Named. So let's use this NASA Image Named.
NASAImageNamed. 来调用下这个方法
242
00:12:30,817 --> 00:12:35,520
I'm just gonna say right here that the IVC's image URL
在这里写 ivc 的 imageURL
243
00:12:35,522 --> 00:12:41,326
equals the NASAImageNamed this imageName. Okay?
等于 NASAImageNamed 的这个 imageNames
244
00:12:41,328 --> 00:12:46,731
This thing I got right here. >> [COUGH]
就这样
245
00:12:46,733 --> 00:12:47,398
>> All right.
能跟上吗
246
00:12:47,400 --> 00:12:50,502
And then, oops, sorry, this has gotta be DemoURL.,
接下来,哦,不好意思,要加上 DemoURL.
247
00:12:50,504 --> 00:12:52,670
because it's over here in this DemoURL.
因为这个方法在 DemoURL 中
248
00:12:52,672 --> 00:12:55,306
Notice this is a static function, right?
注意这是个静态方法
249
00:12:55,308 --> 00:12:56,341
NASAImageNamed is static function.
NASAImageNamed 是个静态方法
250
00:12:56,343 --> 00:12:59,611
So, we send it to the class DemoURL, that's why I had to
所以应该用 DemoURL 类来调用,所以
251
00:12:59,613 --> 00:13:05,116
put DemoURL here. Okay, just kind of a utility function for
应该加上 DemoURL. 这只是 DemoURL 中
252
00:13:05,118 --> 00:13:08,820
that little DemoURL class. And while we're at it,
的一个工具方法而已. 接下来,
253
00:13:08,822 --> 00:13:12,323
you know what, let's set the title of that MVC to be that
将 MVC 结构的 title 设置为
254
00:13:12,325 --> 00:13:15,527
image name as well. So that way we'll Earth or Cassini or
图片名字吧. 这样 MVC 的 title 会是 Earth 或 Cassini
255
00:13:15,529 --> 00:13:21,766
whatever as the title of that MVC. Okay so this make sense?
或者其他的名字. 这就是这段代码的作用
256
00:13:22,068 --> 00:13:25,203
Everybody okay now? Let's go ahead and run this and
都能跟上吗?运行程序
257
00:13:25,205 --> 00:13:30,141
see if this is working. Here we go.
看看效果
258
00:13:33,380 --> 00:13:37,081
Okay, there we go. So here's Cassini right here, and
运行起来了,这是 Cassini,
259
00:13:37,083 --> 00:13:41,686
if I click Earth, okay, whoa. So it worked. I clicked Earth
如果我点击 Earth,有点击效果. 点击了 Earth
260
00:13:41,688 --> 00:13:44,923
but nothing's happening in my app. Wait, no there,
但是 app 却没做出响应. 等下,不对啊
261
00:13:44,925 --> 00:13:47,358
it's showing Stanford. Why is is showing Stanford here?
这展示的是斯坦福,为什么会这样?
262
00:13:47,360 --> 00:13:50,361
Well, that's because our nice reusable image view controller
那是因为我们重用的 ImageViewController
263
00:13:50,363 --> 00:13:54,732
we have over here, it's view did load smashes Stanford on
的 viewDidLoad 方法中,将斯坦福盖在了
264
00:13:54,734 --> 00:13:57,435
top of everything. So, that we only put that in there as
所有视图的顶部. 这只是之前的 demo
265
00:13:57,437 --> 00:14:01,105
a demo. Right, so I'm taking that out, sorry about that.
把它提出来,不好意思
266
00:14:01,107 --> 00:14:02,707
Makes everyone see what happened there. So
现在来看看都发生了什么
267
00:14:02,709 --> 00:14:05,143
now let's go back here. It won't be slamming a Stanford
重新运行一次,这下我们的重用 ImageViewController
268
00:14:05,145 --> 00:14:08,880
in there in our reusable image view controller. Okay? So
应该不会被斯坦福盖住了
269
00:14:08,882 --> 00:14:11,182
now we'll go back to Cassini. We'll pick Earth this time.
回到 Cassini 界面,选择 Earth
270
00:14:11,184 --> 00:14:15,119
Again it seems like it's kinda stuck. I can't click on any of
又卡住了. 所有按钮都
271
00:14:15,121 --> 00:14:17,989
these buttons. I can't, actually I can't do anything.
无法点击. 点不了,什么都做不了
272
00:14:17,991 --> 00:14:21,025
There it is. Okay. So that must have been a really big
进来了,真是一张大图
273
00:14:21,027 --> 00:14:24,462
image. It took a very long time to load and here it is.
需要这么长的时间加载,终于显示出来了
274
00:14:24,464 --> 00:14:27,131
It doesn't really look like Earth, but
看起来不怎么像是地球,
275
00:14:27,133 --> 00:14:29,634
pictures of people on Earth, hm. That's interesting.,
倒像是地球上的人,有意思.
276
00:14:29,636 --> 00:14:32,503
Let's try one of these others. Let's try Cassini here. Okay,
再试试别的,试试 Cassini
277
00:14:32,505 --> 00:14:35,106
what happening? This is, basically, just blank.
怎么回事,怎么只有一个白板
278
00:14:35,108 --> 00:14:39,310
What's going on here? How about Saturn? Again,
怎么回事?再试试 Saturn 呢?又来了
279
00:14:39,312 --> 00:14:41,312
taking a really long time, you can see why we're gonna talk
又要等很久,这就是为什么之后我们要讲
280
00:14:41,314 --> 00:14:45,583
about multi-threading a little later. Okay, and here it is,
多线程. 好,进来了
281
00:14:45,585 --> 00:14:47,385
okay that's not good either. All right, so
看起来也不怎么像. 好吧,
282
00:14:47,387 --> 00:14:50,722
we gotta start here. We know the Cassini's not working.
刚才发现 Cassini 没能正常显示
283
00:14:50,724 --> 00:14:53,258
It comes up blank. So why might Cassini not be working.
看到一片空白,为什么会这样
284
00:14:53,260 --> 00:14:56,027
Let's go back here and check for example to make sure that
回到这里,检查下 segue 有没有什么问题
285
00:14:56,029 --> 00:14:59,030
our segue is good. Okay, the Earth's segue. Here's
这是 Earth 的 segue,这是
286
00:14:59,032 --> 00:15:02,667
the Cassini segue. Let';s go look at its identifier.
Cassini 的 segue. 来检查下它的 identifier
287
00:15:02,669 --> 00:15:06,905
Look at that. This Cassini identifier, no identifier.
看到没,Cassini 的 identifier 是空的
288
00:15:06,907 --> 00:15:08,940
So let's put show image in here. Okay?
设置为 Show Image
289
00:15:08,942 --> 00:15:11,743
So when you have a segue and it doesn't seem to be working,
所以,如果你有一个segue,但是没能正常响应
290
00:15:11,745 --> 00:15:12,877
like it segues to the new MVC but
比如想推出一个新的 MVC,但是
291
00:15:12,879 --> 00:15:15,346
the prepared doesn't happen, often you want to go back and
prepare 中的代码没有运行,通常需要回头
292
00:15:15,348 --> 00:15:17,715
look at your identifier, make sure it's set properly or
检查下 identifier,确保它正确的
293
00:15:17,717 --> 00:15:21,419
set in there. Okay, so now, if we go back here.
赋了值. 现在重新运行
294
00:15:25,458 --> 00:15:27,558
All right, we have Cassini here. Let's click on it.
找到 Cassini 并点击
295
00:15:27,560 --> 00:15:30,728
Again, it's loading. That one must be a smaller image.
再次等待加载,这图片应该不大
296
00:15:30,730 --> 00:15:32,797
It loaded pretty quick. And here it is.
加载起来很快,显示出来了
297
00:15:32,799 --> 00:15:36,234
It looks like it's just a bunch of outer space. Okay, so
看起来就只是外太空而已
298
00:15:36,236 --> 00:15:36,935
we've got a problem here,
现在有一个问题
299
00:15:36,937 --> 00:15:39,637
which is that these images that I loaded are huge. That's
当我们加载一些大图的时候
300
00:15:39,639 --> 00:15:44,008
created two problems for us. One, our app get's blocked.
有两个问题摆在了面前. 一个是 app 被卡死了
301
00:15:44,010 --> 00:15:46,878
See I can't click on anything else. I can't even rotate or
任何东西都没办法点击. 连旋转都不行
302
00:15:46,880 --> 00:15:49,547
anything here. My app is just completely stuck.
app 完完全全卡住
303
00:15:49,549 --> 00:15:51,916
You never want your app in that circumstance.
你肯定不想你的 app 出现这样的情况
304
00:15:51,918 --> 00:15:54,652
Never should your app be stuck like that. Users will just,
你们的 app 绝不能出现这样的情况. 用户发现,
305
00:15:54,654 --> 00:15:56,988
basically, double click on the home button and
就会双击 home 键
306
00:15:56,990 --> 00:15:58,456
kill your app and never run it again.
然后杀死进程,再也不会用了
307
00:15:58,458 --> 00:16:00,925
They'll think your app is totally hosed. And it is,
他们会觉得你的 app 太烂了,这就是
308
00:16:00,927 --> 00:16:04,228
if it behaves that way, okay, so we really need to fix that.
界面卡死的后果,所以我们需要修复下
309
00:16:04,230 --> 00:16:06,197
The other thing is, this image is so
另外一个问题就是,图片太大
310
00:16:06,199 --> 00:16:09,500
huge that we can't really see it. So we'd like to be able to
无法直观的展示. 所以我们要添加
311
00:16:09,502 --> 00:16:11,936
zoom in on it, right. So let's go ahead and
缩放功能. 先来处理
312
00:16:11,938 --> 00:16:17,508
do the zooming, okay? How do we do the zooming in our,
缩放吧. 怎么在 ImageViewController 中
313
00:16:18,178 --> 00:16:19,844
back here in our image view controller, right?
做缩放呢?
314
00:16:19,846 --> 00:16:22,146
So our image view controller has this scroll view,
ImageViewController 有一个 scrollView
315
00:16:22,148 --> 00:16:24,983
right? That we dragged into the storyboard. It's got this
我们拖到 storyboard 中的. 并将 imageView 添加
316
00:16:24,985 --> 00:16:28,453
image view as the subview of the scroll view. Okay, and
到了 scrollView 上
317
00:16:28,455 --> 00:16:31,556
we set our content size, so it's working nicely. And
我们还设置了 contentSize,看起来非常棒
318
00:16:31,558 --> 00:16:32,857
we know from the slides last time that
从之前的幻灯片我们知道了
319
00:16:32,859 --> 00:16:37,495
to make zooming work. Okay, we need to set some other things.
缩放的原理. 那这里还需要处理一些东西
320
00:16:37,497 --> 00:16:39,364
What are some of the things we need to do? Well,
需要怎么做呢?
321
00:16:39,366 --> 00:16:42,600
the most important thing is we need to set ourselves
最重要的一点,就是要将我们自己设置为
322
00:16:42,602 --> 00:16:44,135
as the scrollView's delegate. Okay, so
scrollView 的 delegate.
323
00:16:44,137 --> 00:16:48,373
this is the first time you're seeing delegation demoed here.
这是我们在 demo 中第一次用到 delegate
324
00:16:48,375 --> 00:16:48,773
And the way we do that,
我的做法是
325
00:16:48,775 --> 00:16:52,844
I'd probably do it here in my scrollView didSet here, right?
在 scrollView 的 didSet 方法中来设置
326
00:16:52,846 --> 00:16:55,680
The didSet for my scrollView. Probably just say that Mr.
这就是 scrollView 的 didSet 方法. 这样说,
327
00:16:55,682 --> 00:17:00,084
scrollView, please set your delegate to be self. In other
亲爱的 scrollView,让我成为你的代理吧!换句话说,
328
00:17:00,086 --> 00:17:04,856
words, send me any questions you have about how to operate.
就是把你的问题抛给我来处理吧
329
00:17:04,858 --> 00:17:07,125
Now when I put that in there I have a error,
照我这样设置出现了一个错误
330
00:17:07,127 --> 00:17:11,796
anyone guess what this error is? Okay,
有谁知道这是什么错误吗?
331
00:17:11,798 --> 00:17:14,699
it's going to say that these types, this and
错误是说,他们的类型,这个和
332
00:17:14,701 --> 00:17:18,870
this do not match. Okay, and why is that? The type of this
这个不匹配. 为什么呢?这个的类型是...
333
00:17:18,872 --> 00:17:22,006
is, well the type of this, not showing, but the type of this
好吧,看不到,但是这个的类型是
334
00:17:22,008 --> 00:17:27,211
is UI scrollView delegate. Which is a protocol, okay.
UIScrollViewDelegate. 是个协议
335
00:17:27,213 --> 00:17:31,883
The type of this is UI or image view controller.
这个类型是 UI...哦,是ImageViewController
336
00:17:31,885 --> 00:17:34,652
That's its type. Those don't match. Okay, so
这就是它们的类型,并不匹配. 难怪
337
00:17:34,654 --> 00:17:38,523
that's why this says cannot assign value of type image
报错说不能将 ImageViewController
338
00:17:38,525 --> 00:17:42,260
view controller to UI scroll view delegate, optional.
赋值给可选的 UIScrollViewDelegate
339
00:17:42,262 --> 00:17:46,230
Okay, so we need to say that this image view controller is
所以,我们要先让 ImageViewController 遵循
340
00:17:46,232 --> 00:17:51,736
a UI scroll view delegate. And we do that by just putting
UIScrollViewDelegate. 需要做的是,将 UIScrollViewDelegate 声明在
341
00:17:51,738 --> 00:17:54,672
that right after our super class there, a little comma,
父类的右边,逗号分隔
342
00:17:54,674 --> 00:17:57,642
UIScrollViewDelegate, and that says we are scroll view
这表明我们就是 scrollView
343
00:17:57,644 --> 00:18:00,178
delegate, and all of a sudden this works.
的代理,这样就没问题了
344
00:18:00,180 --> 00:18:03,047
Now UIScrollViewDelegate has no methods or
UIScrollViewDelegate 没有方法或
345
00:18:03,049 --> 00:18:05,950
vars in there that are not optional. In other words
变量是不可选的,换句话说,
346
00:18:05,952 --> 00:18:08,853
they're all optional, so we've successfully implemented so
它们都是可选的,所以,遵循以后
347
00:18:08,855 --> 00:18:10,688
the compiler's not complaining. Okay,
编译器并没有报错
348
00:18:10,690 --> 00:18:14,725
cuz none of them are required. But, we still wanna implement
因为没有必须实现的方法. 不过我们还是需要实现
349
00:18:14,727 --> 00:18:17,995
one, that zooming one, which is called
其中一个方法,缩放的那个,叫做
350
00:18:17,997 --> 00:18:19,363
viewForZoomingInScrollView. Now, one
viewForZoomingInScrollView. 在你
351
00:18:19,365 --> 00:18:21,632
thing that's cool is once you say that you're a ScrollView
成为 ScrollView 的代理之后,
352
00:18:21,634 --> 00:18:24,735
delegate, look what happens when I start typing view
在最新的 Xcode 键入字母时
353
00:18:24,737 --> 00:18:26,971
in Xcode. It knows I'm a ScrollView delegate, so
它就知道你已经是 ScrollView 的代理了,所以
354
00:18:26,973 --> 00:18:30,308
the very first thing it offers me is viewForScroll,
提供的首个联想方法就是 viewForScroll
355
00:18:30,310 --> 00:18:33,177
ScrollingInScrollView. For
ZoomingInScrollView(白胡子口误了)
356
00:18:33,179 --> 00:18:36,981
zooming in scrollView.、 Right? So that's kind of cool. And
用做 scrollView 的缩放. 这真的非常棒
357
00:18:36,983 --> 00:18:40,017
what view, which of the scrollView subviews do we want
那个视图?我们想让 scrollView 的哪个子视图
358
00:18:40,019 --> 00:18:44,622
to zoom in on? Well, of course, the image view. This
做缩放?当然是 imageView. 这是
359
00:18:44,624 --> 00:18:48,159
is our image view right here that we store our image in.
我们的 imageView,里面存储了相应图片
360
00:18:48,161 --> 00:18:49,494
That's of course the thing we want
很明显我们是想
361
00:18:49,496 --> 00:18:54,599
to have its transform modified when we pinch in there. Okay?
在手指捏合的时候,对图片做对应的缩放
362
00:18:54,601 --> 00:18:56,400
Now you might think okay, that's good.
你可能会想,那已经完成了,
363
00:18:56,402 --> 00:19:00,505
Got it all ready to go. Let's go start zooming, and
可以运行了. 让我来看看缩放效果吧
364
00:19:00,507 --> 00:19:03,341
we'll go over here. Let's get Cassini, kinda small.
到这个界面,打开 Cassini,一张小图
365
00:19:03,343 --> 00:19:06,277
Let's start zooming. Now how do I zoom? It's with Pinch, so
试试缩放. 怎么进行缩放呢?需要捏合手势,所以
366
00:19:06,279 --> 00:19:08,913
I'm gonna hold down the Option key right here and
按住 Option 键,然后
367
00:19:08,915 --> 00:19:11,048
start pinching. And it's not working.
进行捏合. 但并没有效果
368
00:19:11,050 --> 00:19:14,085
It's not doing anything. Okay, I can still scroll but
完全没有效果. 依然可以滚动,但是
369
00:19:14,087 --> 00:19:18,990
I can't zoom in. Anyone know why? Say
不能缩放. 有人知道为什么吗?大点声.
370
00:19:18,992 --> 00:19:25,396
it louder. I think I might have heard it back there.
我刚才好像听到那有人回答.
371
00:19:25,398 --> 00:19:28,065
Yeah, this right now, our scrollView by default,
是的,没错,现在 scrollView 的默认值是
372
00:19:28,067 --> 00:19:31,836
it's maximum zoom and its minimum zoom is 1.0.
放大倍数和缩小倍数都是 1.0
373
00:19:31,838 --> 00:19:33,738
So it is zooming to the maximum,
所以我们放大
374
00:19:33,740 --> 00:19:36,974
minimum extent we allow which is none, 1.0. So
和缩小的效果根本看不出来,都是 1.0,所以
375
00:19:36,976 --> 00:19:41,779
we need to set the maximum and minimum scrolling factor or
我们需要给 scrollView 的范围因子,或叫做给
376
00:19:41,781 --> 00:19:45,583
zooming factor on this scrollView. So let's do that.
缩放因子赋值. 回到代码.
377
00:19:45,585 --> 00:19:48,386
Let's do that also right here when we're setting our
在这里进行设置,在
378
00:19:48,388 --> 00:19:50,254
scrollView from our storyboard. So
scrollView 在 storyboard 中被赋值时调用的方法
379
00:19:50,256 --> 00:19:52,924
we just say that the scrollView's minimum zoom
键入 scrollView 的 minimumZoomScale
380
00:19:52,926 --> 00:19:57,962
scale. I'm gonna have this minimum zoom scale.
来设置最小缩放因子
381
00:19:57,964 --> 00:20:02,433
What did I think that worked well here? 0.03.
设成多少效果比较好呢?0.03 吧
382
00:20:02,435 --> 00:20:04,735
So we're gonna zoom- allowed to zoom way out.
我们可以进行缩放
383
00:20:04,737 --> 00:20:07,205
So it will make the image really small, okay?
图片会缩小到很小
384
00:20:07,207 --> 00:20:10,975
Three one-hundredths of it's size.
是原图的百分之三
385
00:20:10,977 --> 00:20:13,844
3% of its size. And then the maximum,
也就是 3% 的大小,接下来设置最大因子
386
00:20:13,846 --> 00:20:16,480
it is perfectly fine for us to say the maximum is 1.0.
这个值设为 1.0 就好了
387
00:20:16,482 --> 00:20:20,318
Maybe we never want the thing to be zoomed out any bigger
可能我们永远不会将该图片放大
388
00:20:20,320 --> 00:20:23,020
than it already is, especially there are huge images already.
毕竟它已经是一张很大的图片了
389
00:20:23,022 --> 00:20:26,190
So we can set the maximum to zoom scale. We don't even need
其实我们完全可以不用设置放大因子,这条
390
00:20:26,192 --> 00:20:29,060
to put this. Cuz by default, it's 1.0. But we'll put it
语句可以不用谢,因为默认值就是 1.0. 这里就不删了,
391
00:20:29,062 --> 00:20:32,430
in here just to kinda be very clear about what we're doing.
看着更清晰一点
392
00:20:32,432 --> 00:20:39,737
All right, so now lets try it. All right,
再来看看效果
393
00:20:39,739 --> 00:20:44,542
so lets go get Cassini right here. All right, here it is.
点击 Cassini,然后
394
00:20:44,544 --> 00:20:48,212
Lets zoom in on it, right. I'm pinching. Yeah, look at that,
进行缩放,捏合一下,看到了吧
395
00:20:48,214 --> 00:20:50,314
it's zooming. And sure enough, look,
缩小了. 效果很好
396
00:20:50,316 --> 00:20:54,352
there's Cassini. Taking a little trip by Saturn, okay,
这是 Cassini,在围着 Saturn 兜风
397
00:20:54,354 --> 00:20:57,021
let's zip it around here so we can see a little better. Okay,
旋转一下屏幕,就能看到更多内容
398
00:20:57,023 --> 00:21:00,324
how about some of these other images, go look at the Earth,
来看看其他图片呢,看看 Earch
399
00:21:00,493 --> 00:21:03,861
didn't look like much like Earth. Now if we zoom,
之前看着并不像 Earth. 这次缩小试试
400
00:21:03,863 --> 00:21:07,131
we'll see something here. All right, so here it is,
说不定能看到更多东西. 好了,加载出来了
401
00:21:07,133 --> 00:21:11,802
scrolling around, I'm gonna pinch, pinch some more.
滚动看看,然后捏合,再缩小点
402
00:21:11,804 --> 00:21:17,675
Look at this Earth, okay? Same thing with Saturn.
啊哈,这就是 Earch. 在看看 Saturn 呢
403
00:21:18,111 --> 00:21:20,244
Okay, everyone see what we do with the zooming?
每个人都知道怎么实现缩放了吗?
404
00:21:20,246 --> 00:21:21,279
Zooming quite simple
实现起来真的很简单
405
00:21:21,281 --> 00:21:22,847
in terms of the code you have to write, but
就那么几句代码
406
00:21:22,849 --> 00:21:24,882
you have to understand that delegation piece for
这全都要靠 delegate
407
00:21:24,884 --> 00:21:28,152
it to really, to make it work. And you'll see Saturn,
那才是真谛. 来看看 Saturn
408
00:21:28,154 --> 00:21:34,325
there's Saturn. Okay? Now let's try this on iPad,
这是 Saturn. 没问题,运行在 iPad 上试试,
409
00:21:34,327 --> 00:21:37,528
okay? Let's see what it looks like here.
看看显示效果
410
00:21:47,373 --> 00:21:48,906
All right, so here is our master and
这是 master
411
00:21:48,908 --> 00:21:51,542
our detail right here. So if I click on Cassini,
这是 detail,点击 Cassini,
412
00:21:51,544 --> 00:21:54,912
hopefully Cassini will eventually really slow,
加载起来确实很慢
413
00:21:54,914 --> 00:21:57,315
appear right here. Okay? And we can zoom in here, okay?
显示出来了,缩放试试呢
414
00:21:57,317 --> 00:22:02,119
Now, what would be really cool here is if we could have
如果在这里能有个标题
415
00:22:02,121 --> 00:22:05,856
a title up here that said this was Cassini,
告诉我们这是 Cassini,
416
00:22:05,858 --> 00:22:09,560
or Earth, or Saturn, right? So remember
还是是 Earth,是 Saturn,就更好了
417
00:22:09,562 --> 00:22:12,863
the tricky way we did that, is we went to our storyboard, and
其实有一个很取巧的方式,打开 storyboard
418
00:22:12,865 --> 00:22:17,201
we just put this one down here. Also in the scrollView.
将下面的这个包含 scrollView 的控制器
419
00:22:17,203 --> 00:22:19,704
So we said embed in navigation control.
加上 navigation
420
00:22:19,706 --> 00:22:21,872
Or not in a scrollView, in a navigation controller. So
或者嵌入到 navigation controller 中
421
00:22:21,874 --> 00:22:25,910
I embed it in there, so that now I have a title. Right? And
嵌入之后,就可以设置标题了
422
00:22:25,912 --> 00:22:26,644
we know that if we can go and
如果现在运行
423
00:22:26,646 --> 00:22:29,547
run this, it's not going to work, right? Because these
不会有任何效果,因为
424
00:22:29,549 --> 00:22:33,984
segues are segueing now to a navigation controller. And
现在这个 segue 是 navigation controller 了.
425
00:22:33,986 --> 00:22:36,687
so our prepare is not gonna work,
所以 prepare 方法不会有效
426
00:22:36,689 --> 00:22:40,124
cuz this line of code is going to fail. Right,
这句代码代码判断失败了
427
00:22:40,126 --> 00:22:43,394
because the destination view controller is now a navigation
现在这个 destinationViewController 是 navigationController
428
00:22:43,396 --> 00:22:43,594
controller. Of course,
显然,
429
00:22:43,596 --> 00:22:46,063
a navigation controller is not an image view controller.
navigationController 不是 imageViewController
430
00:22:46,065 --> 00:22:48,165
This is the exact same problem we had before. So
这种问题我们之前遇到过
431
00:22:48,167 --> 00:22:51,869
I'm gonna show you now how to use that extensions mechanism
下面我将演示如何使用 extension 来
432
00:22:51,871 --> 00:22:54,805
to fix this. So I'm gonna create an extension. This is
解决这个问题. 先创建一个 extension.
433
00:22:54,807 --> 00:22:59,143
exactly what I did in the slides of UIViewController.
这是给 UIViewController 的 extension.
434
00:23:00,012 --> 00:23:03,481
Okay, and in this extension, like to scroll up a little so
好的,在 extension 中,好像往下滚动一点
435
00:23:03,483 --> 00:23:07,351
you can see it better. Okay, inside this extension,
更方便你们看. 好的,在这个 extension 中,
436
00:23:07,353 --> 00:23:11,689
I'm gonna add the var contentViewController.
我准备添加一个 contentViewController 变量.
437
00:23:11,691 --> 00:23:14,525
Which is a UIViewController, okay?
类型是 UIViewController
438
00:23:14,527 --> 00:23:17,862
I'm adding this var to the class UIViewController.
这是给 UIViewController 添加的变量
439
00:23:17,864 --> 00:23:21,232
So, all UIViewControllers will now respond to this property
所以所有的 UIViewController 都有一个叫做
440
00:23:21,234 --> 00:23:24,568
contentViewController. Okay? It's gonna be get only.
contentViewController 的属性了. 将它声明为只读属性.
441
00:23:24,570 --> 00:23:27,171
It's gonna get it. And what I'm gonna put in here is
直接就能获取值. 这里的代码和
442
00:23:27,173 --> 00:23:29,907
basically that same code I had in prepareforSegue,
motion view controller 中的 prepareforSegue 方法
443
00:23:29,909 --> 00:23:32,309
in a motionview controller. I'm gonna say,
代码相同,这样写,
444
00:23:32,311 --> 00:23:37,915
if I can let navcon = self as a UINavigationController,
if let navcon = self as? UINavigationController,
445
00:23:37,917 --> 00:23:43,120
so in otherwords, if I am a navigation controller,
意思是,如果 self 是 UINaviagtionController,
446
00:23:43,122 --> 00:23:47,858
then return navcon.visibleViewController.
就返回 navcon.visibleViewController.
447
00:23:47,860 --> 00:23:50,594
Okay? The only problem with this is this is
这里唯一的问题就是
448
00:23:50,596 --> 00:23:53,631
an optional view controller, because the navigation
这个 view controller 是可选值,因为 navigation
449
00:23:53,633 --> 00:23:56,700
controller might not have anything showing. Okay, so
controller 可能不会展示任何信息,所以
450
00:23:56,702 --> 00:24:00,237
I need to return something because contentViewController
这里必须要返回一个确切的值,因为 contentViewController
451
00:24:00,239 --> 00:24:02,940
does not return an optional So I'm gonna use this
不是一个可选类型. 所以我准备用 ?? 将默认值
452
00:24:02,942 --> 00:24:06,210
defaulting to say if I have a NavigationController and
设置为 self,如果有 NavigationController
453
00:24:06,212 --> 00:24:08,779
it has no visible ViewController, just return
但是 visibleViewController 为空的话,就返回
454
00:24:08,781 --> 00:24:12,082
self, which is to say the NavigationController itself,
self,也就是 NavigationController
455
00:24:12,084 --> 00:24:14,819
okay? Otherwise, we can just return self. So if it's not
其他情况,就返回 self. 即如果不是
456
00:24:14,821 --> 00:24:17,655
a NavigationController, then the content is myself.
NavigationController,就返回 self 作为 content
457
00:24:17,657 --> 00:24:22,293
Everyone understand that code right there? Okay? Good.
大家对这段代码有疑问吗?没有?好的.
458
00:24:22,295 --> 00:24:25,296
So, now that we have this, we can just use that method right
有了这个以后,我们在这里直接调方法就可以了.
459
00:24:25,298 --> 00:24:29,400
here. Okay? Instead of asking if the destination can view
这里还要把之前判断的 destination view
460
00:24:29,402 --> 00:24:31,535
controller is an image view controller, I'm gonna ask if
controller 是 imageViewController 改为判断
461
00:24:31,537 --> 00:24:36,240
the destination controller's content view controller, and
destination controller 的 content view controller 是否是
462
00:24:36,242 --> 00:24:38,742
I can escape complete, cuz now that's a method on U, I,
imageViewController. 现在这个方法,哦,这个
463
00:24:38,744 --> 00:24:41,579
this is a UI view controller. This is a method on UI view
就是 UIViewController 了,这个方法就是 UIViewController 的
464
00:24:41,581 --> 00:24:44,381
controller. I'm just gonna ask if the content view controller
只需要判断 content view controller 是否
465
00:24:44,383 --> 00:24:49,954
is an image view controller. Okay? So now we should be able
是 image view controller 就行了. 现在在 iPad 上跑起来
466
00:24:49,956 --> 00:24:54,825
to run an iPad and get some nice titles. All right.
应该就可以看到标题了.
467
00:24:54,827 --> 00:24:58,162
Cassini again. We've got the navigation controller up here.
依然打开 Cassini,能看到这里有个 navigation controller
468
00:24:58,164 --> 00:25:00,297
There's Cassini. Sure enough, there's the title, right.
这是 Cassini,没问题,这是标题
469
00:25:00,299 --> 00:25:04,568
We set the title right here. Ivc.title = imageName, okay?
我们在这里加上了标题,ivc.title = imageName
470
00:25:04,570 --> 00:25:11,275
And of course, we can zoom around, okay?
当然,我们还可以进行缩放
471
00:25:11,277 --> 00:25:13,310
All right, any questions about that before I go back to
讲到这里,大家有什么问题吗?
472
00:25:13,312 --> 00:25:18,449
the slides? All right. Let's hit the slides here.
好的,再回到讲义.
473
00:25:23,689 --> 00:25:26,524
Okay, so now we're gonna talk about multithreading, so
接下来,我会讲讲多线程
474
00:25:26,526 --> 00:25:28,592
we can fix that problem where our Cassini app,
这样就能修复 Cassini app 中,当我们加载大图时
475
00:25:28,594 --> 00:25:31,962
when we click on the big image, it just froze. Okay.
导致的界面卡死问题了.
476
00:25:31,964 --> 00:25:36,033
When you build an app, never have it freeze like that ever.
当你做一款 app 时,千万不要出现界面卡死的情况
477
00:25:36,035 --> 00:25:38,435
In fact, if it freezes like that, probably you won't get
事实上,出现界面卡死的情况,
478
00:25:38,437 --> 00:25:41,605
through the app store approval process. Okay. So the only
还会影响你上架 app store. 解决界面卡死的
479
00:25:41,607 --> 00:25:44,808
way to stop it from freezing is to use multithreading.
唯一方式,就是使用多线程.
480
00:25:44,810 --> 00:25:48,646
Okay. So, multithreading just means we're going to
多线程意味着我们会在
481
00:25:48,648 --> 00:25:50,781
have different threads of execution,
不同的线程中执行任务
482
00:25:50,783 --> 00:25:54,118
okay? Different places in, in our iPhone or
在,在 iPhone 或 iPad 的不同的地方
483
00:25:54,120 --> 00:25:56,887
our iPad where code is running, okay?
执行代码
484
00:25:56,889 --> 00:26:00,090
And these, these appear to run simultaneously, but
这些代码,几乎是同时运行的,
485
00:26:00,092 --> 00:26:03,227
it'll even work on a single core processor, okay,
即使在单核处理器上,也没问题
486
00:26:03,229 --> 00:26:07,231
not a multiprocessor. It works, then, by timesharing.
虽然不是多核处理,但是运用了分时操作手段
487
00:26:07,233 --> 00:26:08,399
Okay. And so you got one thing running,
所以,一个事件在执行,
488
00:26:08,401 --> 00:26:09,867
another thing running and it's kinda going back and
然后再切换到另一个事件,执行后又切回刚才的事件
489
00:26:09,869 --> 00:26:13,304
forth. Letting them each run a little bit and maybe if one of
继续执行. 每个事件都能执行一段时间,如果某个事件
490
00:26:13,306 --> 00:26:15,573
them's more important than the other, it gets run a lot and
在其中比较重要,那么就会停留得就一些,
491
00:26:15,575 --> 00:26:18,576
the other one doesn't get to run so much. Okay? But
次要的事件就不会占用那么长事件.
492
00:26:18,578 --> 00:26:21,545
that's basically what multithreading is all about.
这就是多线程最基础的部分.
493
00:26:21,547 --> 00:26:22,079
So how many people have,
在做的有同学已经,
494
00:26:22,081 --> 00:26:25,583
have done any multithreading programming of any kind? Okay,
已经做过多线程的项目了?好吧,
495
00:26:25,585 --> 00:26:28,586
so some of you have, okay. About a little less than half
一部分同学,大概不到一半的人吧。
496
00:26:28,588 --> 00:26:31,722
of you. Okay, good. So in iOS multithreading is about
谈到 iOS 上的多线程,其实都是
497
00:26:31,724 --> 00:26:35,859
queues, okay? Queue meaning like, if you go to the movies
队列. 队列就像是...如果你在 England 去
498
00:26:35,861 --> 00:26:37,895
maybe in England [LAUGH] they would say, well,
看电影,那么他们会说,
499
00:26:37,897 --> 00:26:40,664
get in the queue, meaning the line to get into the movie,
排队去,意思是,排队依次进电影院.
500
00:26:40,666 --> 00:26:43,901
okay? So same thing here, and it's the same thing as queue
这里的队列和计算机科学中队列
501
00:26:43,903 --> 00:26:46,971
in the computer science sense, right? A queue is a thing,
的含义是一样的. 队列其实就是一个东西,
502
00:26:46,973 --> 00:26:48,973
a bunch of things lined up to do something.
一个有先后顺序的东西.
503
00:26:48,975 --> 00:26:54,912
All right. And these queues contained an iOS functions.
这些队列包含了 iOS 的方法.
504
00:26:54,914 --> 00:26:58,482
Okay? Most of the time these functions are closures. Okay?
绝大多数方法都是闭包. 好吧?
505
00:26:58,484 --> 00:27:02,052
That you've put in there. Okay? Blocks of code that you
然后加入到队列中. 就是闭包中的代码,
506
00:27:02,054 --> 00:27:06,824
put in this queue. Okay? And then, the system simply runs
加入到队列中. 接着,系统会单独运行
507
00:27:06,826 --> 00:27:09,693
along this queue, pulls the next thing off the queue and
这个队列,将队列中的任务取出,
508
00:27:09,695 --> 00:27:13,430
starts it running in a separate thread, okay?
然后在独立的线程中运行.
509
00:27:13,432 --> 00:27:16,100
And you can have multiple of these queues and the system is
你也可以有很多个队列,然后系统便会
510
00:27:16,102 --> 00:27:18,602
pulling them off each one and running them un, in their
分别取出,然后...在各自的线程中
511
00:27:18,604 --> 00:27:21,705
own threads, simple as that, you just got multithreading.
执行,就这么简单的用上了多线程.
512
00:27:21,707 --> 00:27:24,642
Okay. Now there's a little bit of trickiness
那么现在有个问题,
513
00:27:24,644 --> 00:27:25,909
here of what are these queues and
队列是什么,
514
00:27:25,911 --> 00:27:27,444
how do you put things on the queues, but
怎么将任务加入到队列中,
515
00:27:27,446 --> 00:27:30,180
that's the fundamental way these work. Now queues can
这属于队列运行原理. 队列可以是
516
00:27:30,182 --> 00:27:34,118
either be serial, which means the function that's on the top
串行的,意思是只有当上一个任务执行完后,
517
00:27:34,120 --> 00:27:37,154
of the queue gets pulled off, it runs to completion, and
新的队首元素才会出队
518
00:27:37,156 --> 00:27:40,824
then the next one gets pulled off. That's serial queues. Or
接着执行. 这就是串行队列.
519
00:27:40,826 --> 00:27:43,494
they can be concurrent, where the system pulls the top
队列也可以是并行的,意思是系统出队
520
00:27:43,496 --> 00:27:46,130
one off the queue, starts it running in a thread. If it's
一个队首元素,就立即在一个线程中执行. 如果是这样,
521
00:27:46,132 --> 00:27:48,599
got more thread resources, it takes the next one off,
就会占用更多的资源,下一个任务出队以后
522
00:27:48,601 --> 00:27:51,402
starts it running in another thread while the first one's
就会在另一个线程中进行执行,此时,第一个出队的任务
523
00:27:51,404 --> 00:27:51,502
still running, and
仍在执行,
524
00:27:51,504 --> 00:27:55,873
it might keep pulling a whole bunch of them off. Okay, so
这种队列甚至可以直接将整个队列元素出队执行.
525
00:27:55,875 --> 00:27:57,775
that's a concurrent queue.
这就是并行队列.
526
00:27:57,777 --> 00:28:00,544
All right, so let's talk about the queues, how we get them,
接下来,再来看看队列的其他知识. 我们怎么获得队列,
527
00:28:00,546 --> 00:28:03,781
what, what we call them, etc. The most important queue,
怎样调用它们,等等. 对重要的队列,
528
00:28:03,783 --> 00:28:06,717
it's a serial queue called the main queue, okay?
就是一个称为主队列的串行队列.
529
00:28:06,719 --> 00:28:10,554
The main queue is where all UI activity has to happen. And
主队列是刷新 UI 控件的队列.
530
00:28:10,556 --> 00:28:14,658
this is super important for you to understand. That if you
大家一定要着重理解这个知识点. 如果你在
531
00:28:14,660 --> 00:28:19,063
want to do anything where you call something a UI kit, okay,
做一些和 UIKit 相关的动作,比如
532
00:28:19,065 --> 00:28:21,331
UI button, UI anything, all right,
UIButton,或其他 UI 元素,
533
00:28:21,333 --> 00:28:24,334
with a couple of exceptions which I'll talk about later.
包括之后我们还会学到的其他 UI 控件.
534
00:28:24,336 --> 00:28:27,104
You need to be making those calls on the main queue.
它们都必须要在主队列中进行操作.
535
00:28:27,106 --> 00:28:31,241
In other words, you can't put those calls in a closure that
换句话说,它们不能再其他队列的闭包
536
00:28:31,243 --> 00:28:34,378
you put on some other queue. Okay, it has to be on the main
中执行. 必须在主队列中执行》
537
00:28:34,380 --> 00:28:39,149
queue, all right? Now conversely, all non-UI
记住了吗?相反,所有和 UI 控件
538
00:28:39,151 --> 00:28:42,519
activity that's gonna take any amount of time or resources,
无关的操作,可能会耗时,或耗资源的操作,
539
00:28:42,521 --> 00:28:45,589
probably wants to be off the main queue. So it never blocks
就不要放到主队列中. 千万不要阻塞
540
00:28:45,591 --> 00:28:48,092
the main queue. We want the main queue reserved for
主队列. 我们应该尽可能的将主队列的资源
541
00:28:48,094 --> 00:28:51,562
doing our UI stuff as much as possible. Okay, now this is
用来调度 UI 元素. 当然,希望 UI 控件能及时响应
542
00:28:51,564 --> 00:28:55,799
not only cuz we want our main queue UI to be responsive,
只是其中一个原因,
543
00:28:55,801 --> 00:28:56,767
okay, that's the main reason, but
也是最主要的原因,另外
544
00:28:56,769 --> 00:29:00,671
also it serializes the activity on the main queue so
一个原因,是因为主队列是串行队列,所以
545
00:29:00,673 --> 00:29:03,907
that our UI is presented in an orderly fashion. If,
UI 元素能按照我们给的顺序来执行. 如果,
546
00:29:03,909 --> 00:29:06,343
if we allowed our UI to be on all these different queues, it
如果允许在不同的队列中操作 UI,实际上却是是
547
00:29:06,345 --> 00:29:08,912
could, things would be drawing at all different rates and
可以的,但是绘制速度就会不同,从而出现
548
00:29:08,914 --> 00:29:09,480
overlapping, it would be
重叠,预测不了
549
00:29:09,482 --> 00:29:11,882
unpredictable as to what happened on screen. Okay, so
界面效果. 所以
550
00:29:11,884 --> 00:29:15,285
we use the main queue as kind of a synchronization point
我们需要要利用主队列的同步性,
551
00:29:15,287 --> 00:29:18,522
where everybody comes back to draw on the main queue,
保证所有的绘制都在主队列中进行,
552
00:29:18,524 --> 00:29:22,259
all right? Now the main queue can only pull a closure or
明白了吗?主队列每次会出队一个闭包或
553
00:29:22,261 --> 00:29:24,294
a function off of the main queue and
一个方法,
554
00:29:24,296 --> 00:29:26,630
work on it when it's quiet. In other words,
在静默时,执行它. 换句话说,
555
00:29:26,632 --> 00:29:28,532
it's not off doing something else, like drawing or
它一直都是执行状态,比如在绘制,或
556
00:29:28,534 --> 00:29:31,835
something like, like that. Now the system is using the main
在执行和绘制差不多的任务. 主队列会一直在
557
00:29:31,837 --> 00:29:33,403
queue behind the scenes all the time.
系统后台运行着.
558
00:29:33,405 --> 00:29:35,572
For example, you know about draw req. Right.
比如,你知道绘制操作吧.
559
00:29:35,574 --> 00:29:38,342
I told you draw req gets called by the system and
我之前说过,绘制是系统在调用,
560
00:29:38,344 --> 00:29:40,277
it doesn't get called, you don't call it,
你并没有手动去调用,
561
00:29:40,279 --> 00:29:42,913
you don't tell it to, you just set needs display.
并没有让系统去调,只是告诉系统它需要显示.
562
00:29:42,915 --> 00:29:44,348
Well, you set needs display and
设置在主线程空闲的时候
563
00:29:44,350 --> 00:29:48,585
as soon as the main queue gets quiet, it goes and runs some
进行显示,系统会运行或调用某些
564
00:29:48,587 --> 00:29:51,555
function that causes that draw req to be called. So
方法来进行绘制. 这便是
565
00:29:51,557 --> 00:29:54,525
you see how that works, okay? So that's what's going on in
绘制的工作原理. 这便是主队列
566
00:29:54,527 --> 00:29:57,294
the main queue. What about other queues? Okay, so
在处理的东西. 那么其他队列呢?看
567
00:29:57,296 --> 00:30:00,030
there's, I'm, in the next slide I'm gonna talk about
这里,下个话题便会讲讲
568
00:30:00,032 --> 00:30:02,800
two different kinds of other queues that you can use to run
其他两种不同的队列,能用来处理
569
00:30:02,802 --> 00:30:06,236
all your non-UI stuff. All right? So the first,
所有和 UI 无关的队列. 目前,
570
00:30:06,238 --> 00:30:09,039
not gonna talk about this now, that'll be on the next slide.
我们暂时不谈这个,这是下个话题.
571
00:30:09,041 --> 00:30:12,142
So on this slide I'm gonna talk a little bit about how we
这张幻灯片还有一点东西没讲完,关于如何
572
00:30:12,144 --> 00:30:14,845
put something on a queue and the main way we do it is
将任务放到队列中,最常见的做法是
573
00:30:14,847 --> 00:30:17,981
with this function right here called dispatch_async.
调用一个叫 dispatch_async 的方法.
574
00:30:17,983 --> 00:30:22,986
Dispatch_async takes two arguments. One is the queue to
Dispatch_async 有两个参数. 一个是要加入的
575
00:30:22,988 --> 00:30:25,455
put it on, like the main queue or one of these other queues,
队列,可以是主队列,或者其他队列,
576
00:30:25,457 --> 00:30:29,760
and the second argument is the function to put on the queue,
第二个参数是,要加入队列的方法,
577
00:30:29,762 --> 00:30:32,963
again, usually it's a closure. You see how I'm using trailing
当然,通常是闭包. 我这里简单写了一个
578
00:30:32,965 --> 00:30:35,933
closure syntax right there? Okay. This is two arguments.
闭包的语法. 这有两个参数,
579
00:30:35,935 --> 00:30:39,002
One argument. Two arguments. So this is the closure you're
一个参数. 两个参数. 这就是加入到队列中的
580
00:30:39,004 --> 00:30:41,605
gonna put on there. What's cool about these closures,
闭包. 看起来很不错吧,
581
00:30:41,607 --> 00:30:43,607
they take no arguments and then return no arguments.
闭包没有参数,也没有返回值.
582
00:30:43,609 --> 00:30:46,910
So they're really easy to, to [LAUGH] put into your code.
所以,非常容易嵌到你的代码中(笑~
583
00:30:46,912 --> 00:30:50,180
Okay? And you can put anything you want in here. Okay?
你可以将任何想执行的代码扔到里面.
584
00:30:50,182 --> 00:30:53,884
But if this gonna do UI, you better not put it on anything
当然,最好是将 UI 操作扔进去,而不是别的操作,
585
00:30:53,886 --> 00:30:58,388
but the main queue. All right, so how do you get the queue?
毕竟这是主线程. 所以,怎样获得一个队列呢?
586
00:30:58,390 --> 00:30:59,823
How do you get this little queue argument?
怎样传入这个队列参数?
587
00:30:59,825 --> 00:31:02,993
Well, for the main queue, you call this function,
对于主队列来说,可以通过调用
588
00:31:02,995 --> 00:31:05,963
dispatch_get_main_queue. Now, you might ask,
dispatch_get_main_queue 来获得. 你或许会问,
589
00:31:05,965 --> 00:31:10,100
why is all these underbars and why does this look like this?
为什么这么多下划线,为什么这个方法长这样?
590
00:31:10,102 --> 00:31:15,339
This is basically a CAPI, okay, from iOS before Swift,
因为这是基于 CAPI 的,属于 iOS,而不是 Swift 的语法,
591
00:31:15,341 --> 00:31:18,008
and so it comes into swift looking like a CAPI.
所以这使得 Swift 看起来像 CAPI 一样.
592
00:31:18,010 --> 00:31:21,011
These are Swift, these are just Swift global functions.
在 Swift 中,这是个全局方法.
593
00:31:21,013 --> 00:31:23,413
But the reason this is not object oriented, for
它不是面向对象的,因为
594
00:31:23,415 --> 00:31:26,083
example is because this is a CAPI.
它是 CAPI.
595
00:31:26,085 --> 00:31:28,852
This, this is called grand central dispatch actually,
实际上,它被称为 GCD(大中枢派发)
596
00:31:28,854 --> 00:31:30,454
that's why they all start with dispatch,
所以这个方法以 dispatch 开头,
597
00:31:30,456 --> 00:31:33,290
all this multithreading stuff, and that's why it looks like
这就是为什么,所有这类多线程接口,都和这个类似.
598
00:31:33,292 --> 00:31:37,094
this. I'm gonna show you the object-oriented way to do
之后我会介绍怎么用面向对象的方式使用
599
00:31:37,096 --> 00:31:40,764
this in a minute, but actually we're gonna end up using these
这些接口,在此之前,你还会继续
600
00:31:40,766 --> 00:31:44,768
C-like ones more often, all right? So, again, all UI stuff
使用 C 语言的风格的接口. 再次强调,所有 UI 的操作
601
00:31:44,770 --> 00:31:49,039
have to be on this main queue. It's a serial queue, okay, so
都必须在主队列中进行. 这是个串行队列,
602
00:31:49,041 --> 00:31:51,608
nothing happens concurrently on this queue,
所以这个队列中不可能有并行的操作出现,
603
00:31:51,610 --> 00:31:55,112
things happen in order in the order they go in, okay?
入队是什么顺序,出队就是什么顺序,有问题吗?
604
00:31:55,114 --> 00:31:57,381
And, but all time-consuming stuff,
所有耗时的操作,
605
00:31:57,383 --> 00:31:59,750
okay, or even worse stuff that might block,
或其他耗资源的操作,都可能造成阻塞,
606
00:31:59,752 --> 00:32:02,786
like you're going out to the network to get an image,
就像网络请求图片这类的操作,
607
00:32:02,788 --> 00:32:05,555
like we are doing where that block's waiting for
或者其他因等待网络响应,
608
00:32:05,557 --> 00:32:07,958
the Web server on the other side to respond,
而导致阻塞的操作,
609
00:32:07,960 --> 00:32:10,794
all that wants to happen off the main queue.
这些操作,都不应该出现在主队列中.
610
00:32:10,796 --> 00:32:15,032
Now you still dispatch to that queue using dispatch_async.
目前我们在使用 dispatch_async 来派发队列.
611
00:32:15,034 --> 00:32:17,367
You go down here and you say look at the bottom,
看到讲义的底部,
612
00:32:17,369 --> 00:32:20,170
it says dispatc_async, not the main queue in here.
参数这里写到,不一定非要传入主队列.
613
00:32:20,172 --> 00:32:22,973
And I'll talk about where you get that queue in a second.
之后会讲到如何获得其他队列.
614
00:32:22,975 --> 00:32:24,007
And then open curly brace,
接着,代码包含在大括号中,
615
00:32:24,009 --> 00:32:27,644
so this is a closure, inside that closure you do the non-UI
所以这是一个闭包,在闭包中,执行的是和 UI 无关的
616
00:32:27,646 --> 00:32:31,081
thing that takes a long time or blocks. And then still
操作,它可能会花很长时间,或者发生阻塞. 接着又是一个
617
00:32:31,083 --> 00:32:35,485
inside this closure, you dispatch_async again. This
闭包,再次 dispatch_async. 这次
618
00:32:35,487 --> 00:32:39,990
time back to the main queue to do the UI stuff that you need,
回到了主队列中,处理和 UI 相关的操作,
619
00:32:39,992 --> 00:32:43,160
maybe that the image you got from doing this, you now put
可能是你请求完了图片,需要
620
00:32:43,162 --> 00:32:47,064
this in the UI here. So look how I'm dispatching within
扔到 UI 上进行显示. 所以来看看如何通过闭包
621
00:32:47,066 --> 00:32:50,567
a closure. So can you imagine this outer closure
来派发它们. 你能想象外部闭包
622
00:32:50,569 --> 00:32:53,770
is off running on some other thread in some other queue and
在其他队列的其他线程上运行,
623
00:32:53,772 --> 00:32:57,908
in the middle of it, it puts a block of code,
然后在其中,又有一个代码块,
624
00:32:57,910 --> 00:32:58,508
this block right here,
就是这个代码块,
625
00:32:58,510 --> 00:33:02,279
this closure. It puts it back on the main queue, this one
这个闭包. 又回到了主队列,
626
00:33:02,281 --> 00:33:05,315
continues to run after that in fact dispatc_async,
继续通过 dispatch_async 方法执行操作,
627
00:33:05,317 --> 00:33:08,418
that function always returns immediately. Because all it
这个方法会立即返回. 因为它是将任务
628
00:33:08,420 --> 00:33:11,655
does is put things on queues, it doesn't actually execute
扔到队列中,而没有真正执行
629
00:33:11,657 --> 00:33:13,490
any of the code inside the block.
代码块中的代码.
630
00:33:13,492 --> 00:33:16,827
It just puts that block of code onto the queue. Okay, and
只是将代码块扔到了队列中.
631
00:33:16,829 --> 00:33:20,397
then this outer closure just happily runs to completion and
接着,这个外部闭包就
632
00:33:20,399 --> 00:33:23,500
it's done. Meanwhile, that inner block has been posted
执行完了. 与此同时,内部闭包也扔到了
633
00:33:23,502 --> 00:33:24,868
on the main queue and it's just waiting for
主队列中,等待
634
00:33:24,870 --> 00:33:27,637
the main queue, for it to be first in line and
主队列执行. 排到主队列中,
635
00:33:27,639 --> 00:33:28,405
for the main queue to be quiet, and
当主队列空闲下来,
636
00:33:28,407 --> 00:33:33,143
then it'll pick it up and run it. You got it? So this is why
便会出队并执行. 这就是为什么
637
00:33:33,145 --> 00:33:36,680
we use the C function notation because it reads really cool,
我们会用 C 函数,因为看起来真的挺酷的.
638
00:33:36,682 --> 00:33:39,816
okay? Dispatch off the queue this, then dispatch the main
先派发这个队列,然后派发
639
00:33:39,818 --> 00:33:44,087
queue this, okay? It reads very clearly to the person
主队列. 很容易让阅读代码的人
640
00:33:44,089 --> 00:33:46,790
who's reading your code what you intend. Okay, but
明白你的意图. 但是
641
00:33:46,792 --> 00:33:49,192
you do have to remember that this is async,
注意这些都是异步的,
642
00:33:49,194 --> 00:33:53,163
it's out of sync. Just because you say to execute this code,
不是同步的. 因为我们并不需要显性调用这些代码,
643
00:33:53,165 --> 00:33:53,997
it's not gonna happen right away.
这些代码不会立刻执行.
644
00:33:53,999 --> 00:33:56,099
It's just being put on that queue, it'll happen sometime
只是将它们加入到队列中,它们会在
645
00:33:56,101 --> 00:34:00,037
in the future when that queue is ready, okay? When it rises
队列空闲下来的时候自己执行. 当它们是队列
646
00:34:00,039 --> 00:34:04,975
to the top of that queue and the main queue is quiet, okay?
的队首元素,并且主队列闲下来的时候,就会执行.
647
00:34:04,977 --> 00:34:08,111
So how do we get these not the main queue queues?
那么,怎么获得除了主队列以外的队列呢?
648
00:34:08,113 --> 00:34:10,313
Okay, that we want to run something else on.
比如我们想要执行其他的一些操作.
649
00:34:10,315 --> 00:34:11,415
Well there's really two ways to do it.
这有两种方式可用.
650
00:34:11,417 --> 00:34:15,552
The most common way is to use one of the concurrent queues
最常见的直接使用系统提供的
651
00:34:15,554 --> 00:34:18,121
provided by the system, okay? Now these
并行队列. 这四种队列
652
00:34:18,123 --> 00:34:21,758
queues provided by the system there's four of them, okay?
就是系统提供的.
653
00:34:21,760 --> 00:34:25,295
They each have what's called a quality of service and
它们有着不同的优先级的服务,
654
00:34:25,297 --> 00:34:28,165
the quality of service is really how
服务的优先级其实就是
655
00:34:28,167 --> 00:34:31,668
much attention the system gives to them.
系统会给予多少的关注度.
656
00:34:31,670 --> 00:34:34,304
You can think of it as their priority. High-priority queues
你可以顺着优先级来认识它们. 高优先级队列
657
00:34:34,306 --> 00:34:37,174
get a high quality of service. They get serviced a lot, okay?
拥有最高优先级的服务. 它们能获得较多的服务.
658
00:34:37,176 --> 00:34:41,111
But it's not purely that, but it's generally that. So
但也不是绝对的,只是大致上来说而已.
659
00:34:41,113 --> 00:34:43,513
what are the four qualities of service here?
来看看这四种优先级的服务吧.
660
00:34:43,515 --> 00:34:46,817
One is USER_INTERACTIVE. So, you can get a queue, you
一种是 USER_INTERACTIVE. 所以,你可以通过调用
661
00:34:46,819 --> 00:34:49,753
can call this dispatch, get global queue right here. And
get_global_queue 方法来获得一个队列.
662
00:34:49,755 --> 00:34:52,589
you can say give me the user interactive queue. Again, it's
然后给它 USER_INTERACTIVE 优先级. 这是个
663
00:34:52,591 --> 00:34:54,925
a concurrent queue, so it's doing things concurrently,
并行队列,所以会并行处理操作,
664
00:34:54,927 --> 00:34:58,495
it's not serial. And user interactive means
这就不像串行了. USER_INTERACTIVE 意味着
665
00:34:58,497 --> 00:35:01,398
the user just asked to do something.
这是用户要求处理的事情.
666
00:35:01,400 --> 00:35:03,066
It's gonna take a little bit of time,
可能会花一些时间,
667
00:35:03,068 --> 00:35:04,801
so I don't want to do it on the main queue,
虽然我不想再主队列中进行处理,
668
00:35:04,803 --> 00:35:07,604
but I need this done as soon as possible because the user
但是我仍然希望能进行执行,毕竟这是用户
669
00:35:07,606 --> 00:35:09,673
is interacting with me right now, okay?
希望立马响应的操作.
670
00:35:09,675 --> 00:35:11,641
So, this is very high priority, but
所以它拥有最高优先级,
671
00:35:11,643 --> 00:35:15,345
lower priority than the main queue, okay? So, this might be
但这仍然要比主队列的优先级低. 这可能是
672
00:35:15,347 --> 00:35:18,081
something where the user is dragging with their finger,
用户在拖拽手指时,
673
00:35:18,083 --> 00:35:21,218
and you're having to calculate something to make the image
你需要同时计算一些数据并
674
00:35:21,220 --> 00:35:23,220
that's pretty intensive, time wise, and so
立即给出反馈,这时就应该将任务扔到
675
00:35:23,222 --> 00:35:26,089
you put it off on the thread. The user continues to drag,
这种队列中. 用户继续手指继续拖拽,
676
00:35:26,091 --> 00:35:27,390
they're not seeing the result and
但是没有看到数据显示,
677
00:35:27,392 --> 00:35:28,558
then the result comes back in and
等数据计算完成时,
678
00:35:28,560 --> 00:35:30,393
then the user gets to see the result. So
用户才能看到数据. 所以,
679
00:35:30,395 --> 00:35:32,362
the results a little bit behind their finger,
数据会比用户的手势慢一步,
680
00:35:32,364 --> 00:35:33,196
because it takes so much time, but
因为计算会花一些时间,
681
00:35:33,198 --> 00:35:35,799
at least as their finger tracks the main queue is still
但至少主线程一直在监听
682
00:35:35,801 --> 00:35:39,870
listening to their finger, okay, and responding, okay?
手指的轨迹,并且响应还是有的,
683
00:35:39,872 --> 00:35:42,005
So that's what USE_INTERACTIVE is for.
这就是 USER_INTERACTIVE 优先级.
684
00:35:42,007 --> 00:35:45,675
USE_INITIATED means the user just asked me to do this, but
USE_INITIATED 意思是,用户让我处理这个,但是不需要在
685
00:35:45,677 --> 00:35:48,812
it's not in the middle of an interactive event so, you can
用户交互的时候去处理,所以允许事件
686
00:35:48,814 --> 00:35:51,181
take a little bit of time but get back to me right away,
等待一些事件,但一旦处理完一定要有反馈
687
00:35:51,183 --> 00:35:53,950
cuz the user just asked for this, right now, okay?
因为这是用户要求处理的
688
00:35:53,952 --> 00:35:58,255
So, that's lower priority, than an interactive thing.
所以,它的优先级要比交互事件低一等
689
00:35:58,257 --> 00:36:02,626
UTILITY is something that usually runs in the background
UTILITY 一般用于在后台处理
690
00:36:02,628 --> 00:36:06,796
for a long time, okay? Maybe it's fetching data.
一些耗时操作,比如获取数据什么的.
691
00:36:06,798 --> 00:36:09,633
It's cleaning up some database somewhere. It's doing
也可以用于清理数据库,或者
692
00:36:09,635 --> 00:36:12,569
something that the user didn't ask to do, but that needs to
处理一些程序必须要处理,
693
00:36:12,571 --> 00:36:15,305
be done for this program. It's that kind of thing. And
而用户又没有要求处理的事件. 大致就是这一类事件.
694
00:36:15,307 --> 00:36:17,941
then BACKGROUND is even lower priority. This is the kind of
BACKGROUND 的优先级最低. 一般将一些可
695
00:36:17,943 --> 00:36:22,078
thing well it could run today or tomorrow. But you know,
今天,可明天处理的事件加到里面. 也就是,
696
00:36:22,080 --> 00:36:24,247
it's really not something that I need done right away,
没必要立马处理,
697
00:36:24,249 --> 00:36:26,516
I don't really care. So, it's kind of just a,
不需要过于关注的事件. 就好比是,
698
00:36:26,518 --> 00:36:28,318
something that runs along in the background.
在后台默默执行的事件.
699
00:36:28,320 --> 00:36:30,253
It's only gonna happen when things are really quiet,
在没有其他事件需要占用资源的时候,
700
00:36:30,255 --> 00:36:34,991
when nobody wants any other service, okay? So, you get one
它才执行. 到此,你就已经学习了
701
00:36:34,993 --> 00:36:37,594
of these four queues by saying dispatch_get_global_queue,
dispatch_get_global_queue 包含的四种优先级.
702
00:36:37,596 --> 00:36:40,430
you specify which one you want. Then this has this
可根据需要选择. 除此之外,
703
00:36:40,432 --> 00:36:44,768
extra argument ,0 which is reserved for future use, so
还有一个参数 0,这将在之后用到,
704
00:36:44,770 --> 00:36:45,602
you just always put ,0 there.
现在就简单的传 0 就可以了.
705
00:36:45,604 --> 00:36:48,505
And that's gonna give you back a queue, a dispatch_queue_t
这样函数就会返回 dispatch_queue_t 类型的队列,
706
00:36:48,507 --> 00:36:51,942
that you can then use as your first argument to dispatch
它可以作为 dispatch_async 的第一个参数.
707
00:36:51,944 --> 00:36:55,645
async, okay. So these are the, this is the cues,
那么,这就是,
708
00:36:55,647 --> 00:36:57,514
these are the cues you're most often going to
这就是在你要处理后台事件时,
709
00:36:57,516 --> 00:37:00,383
put this work that needs to be done in the background onto.
最为常用的队列了.
710
00:37:00,385 --> 00:37:03,720
Now, its also possible for you to create your own
当然,你也可以创建自己的
711
00:37:03,722 --> 00:37:07,023
serial queue. You call dispatch_queue_create.
串行队列. 通过调用 dispatch_queue_create 函数,
712
00:37:07,025 --> 00:37:09,626
You give it a name. This can be any name you want, okay?
并给队列一个名字,任何名字都可以,
713
00:37:09,628 --> 00:37:12,028
This is usually just so you can see it in the debugger,
这在调试的时候经常看到,
714
00:37:12,030 --> 00:37:14,297
okay, this name is gonna appear in the debugger.
队列名称会出现在调试界面中.
715
00:37:14,299 --> 00:37:16,366
And then you say DISPATC_QUEUE_SERIAL,
第二个参数是 DISPATC_QUEUE_SERIAL,
716
00:37:16,368 --> 00:37:17,500
saying you want a serial queue.
表示你需要创建串行队列.
717
00:37:17,502 --> 00:37:20,737
Now you will get a serial queue of your own.
这样就能获得一个自己的串行队列了.
718
00:37:20,739 --> 00:37:22,672
And it's gonna be a pretty high priority queue,
这是一个优先级相当高的队列,
719
00:37:22,674 --> 00:37:25,742
nothing like the main queue, but pretty high priority.
虽然不像主队列那么高,但也算是万人之上了.
720
00:37:25,744 --> 00:37:27,644
Why would you ever want a serial queue?
什么时候需要创建串行队列呢?
721
00:37:27,646 --> 00:37:31,581
Let's say you had a big table that you wanna download
假设你有一个长长的列表,想要下载
722
00:37:31,583 --> 00:37:34,351
a thousand images to put in that table.
一千张图片扔到列表中
723
00:37:34,353 --> 00:37:37,754
Small little images, okay? Well you could do it on
都是些小图标. 你可以
724
00:37:37,756 --> 00:37:40,290
one of these user initiated queues or something like that.
在 USER_INITIATED 队列中或者其他队列中下载.
725
00:37:40,292 --> 00:37:42,225
But it's gonna fork off a whole bunch of threads and
但是这样系统就会创建一大串线程,
726
00:37:42,227 --> 00:37:45,128
try to download as many as it can concurrently.
而且尽可能多的进行并发下载.
727
00:37:45,130 --> 00:37:46,663
Open a whole bunch of network connections.
同时发起一堆网络请求.
728
00:37:46,665 --> 00:37:50,800
Whereas if you do is serially it will do it one by one,
但如果你在串行队列中处理,就会一个一个按秩序下载,
729
00:37:50,802 --> 00:37:53,837
okay? So it kind of throttles, it's a throttling,
就像是限流的阀门,
730
00:37:53,839 --> 00:37:55,872
a way of throttling your access to the network,
有限制的去访问网络.
731
00:37:55,874 --> 00:37:59,109
all right? You usually don't need this.
不过通常不需要这个.
732
00:37:59,111 --> 00:38:02,212
These are usually what you're gonna use for that not main
这些通常会在其他队列中处理,而不是主队列.
733
00:38:02,214 --> 00:38:06,449
queue queue, okay? Now, we're only seeing the absolute
到此,你已学习了多线程必备的一些知识,
734
00:38:06,451 --> 00:38:09,219
tip of the iceberg on multithreading here,
当然这只是冰山一角.
735
00:38:09,221 --> 00:38:13,323
okay? There's a lot more to GCD, Grand Central Dispatch.
更多关于 GCD,Grand Central Dispatch 的内容,
736
00:38:13,325 --> 00:38:14,557
You can look in the documentation for
可以查看文档,
737
00:38:14,559 --> 00:38:16,660
all the functions that start with dispatch underbar and
所有方法都以 dispatch_ 开头,
738
00:38:16,662 --> 00:38:20,263
you'll get an idea, there's at least a couple dozen in there.
至少有一打这样的方法.
739
00:38:20,265 --> 00:38:24,034
I don't have time to cover it in this class.
我没办法在课堂上依次为大家介绍.
740
00:38:24,036 --> 00:38:24,100
You won't need it,
其他方法你也不需要,
741
00:38:24,102 --> 00:38:26,102
you won't need anything more than I just showed you for
除了我介绍给大家的这些方法外,你暂时还用不到别的
742
00:38:26,104 --> 00:38:28,371
the work that you're gonna be doing in your assignments.
包括课后作业也不会用到.
743
00:38:28,373 --> 00:38:31,007
In your final project, you probably also won't need
甚至是最终考核项目,你都可能用不上
744
00:38:31,009 --> 00:38:33,910
anymore than that, but at least you know where to look,
别的方法,但至少要知道用的时候,
745
00:38:33,912 --> 00:38:38,214
if you might need more. There is an object-oriented
能在哪里查到. 这是面向对象的
746
00:38:38,216 --> 00:38:40,850
way of doing all this called, there's two classes,
调用方式,提供了两个类,
747
00:38:40,852 --> 00:38:43,853
NSOperationQueue, which is kind of like dispatch queues.
NSOperationQueue,和 dispatch queue 很像.
748
00:38:43,855 --> 00:38:47,824
And NSOperation, which is kinda like those closures,
还有 NSOperation,和闭包类似,
749
00:38:47,826 --> 00:38:52,929
right, those little functions. Why would you ever want this?
包含要处理的事件. 为什么我们需要这个两个类呢?
750
00:38:52,931 --> 00:38:56,132
These do have a little bit of extra functionality,
因为它们在 GCD 基础上,
751
00:38:56,134 --> 00:38:59,069
that they wrap from GCD, things like, hey,
还提供了一些额外的功能,比如,
752
00:38:59,071 --> 00:39:00,704
I have these two operations and
两个事件(operation),
753
00:39:00,706 --> 00:39:04,441
this one depends on this one finishing, okay. And so
一个要在另一个结束之后发起.
754
00:39:04,443 --> 00:39:06,076
I'm gonna put them in a concurrent queue, but
把它们扔在并发队列中,
755
00:39:06,078 --> 00:39:08,845
don't let this other one go until the first one finishes,
也能保证一个在另一个结束之后发起.
756
00:39:08,847 --> 00:39:11,047
right. So dependencies, you can have dependencies and
这就是依赖,你能对事件添加依赖.
757
00:39:11,049 --> 00:39:14,417
things like that. We're not gonna talk about that.
课堂上不讲,
758
00:39:14,419 --> 00:39:16,553
You can look at the documentation if you want.
你可以在文档中看到.
759
00:39:16,555 --> 00:39:20,690
You can do, I think almost everything here with GCD.
我们需要的处理,GCD 已经足够了.
760
00:39:20,692 --> 00:39:23,259
This would be more if you actually are building
如果你是在创建一个
761
00:39:23,261 --> 00:39:26,029
something where there's queue are doing queues and
有很多队列,而且
762
00:39:26,031 --> 00:39:29,299
these operations are doing the actual calculation. Like as
operation 都在处理相关运算. 就像
763
00:39:29,301 --> 00:39:31,568
your model is doing, you're doing some scientific app and
建模一样,做一个科学相关的 app,
764
00:39:31,570 --> 00:39:33,803
it's actually doing all this complex calculation where
并且在处理一些非常复杂的计算,
765
00:39:33,805 --> 00:39:35,372
things depend on each other and all that stuff.
每个计算都相互依赖.
766
00:39:35,374 --> 00:39:38,441
And so it has a lot of queues and a lot of operations going.
这样,就需要很多队列和操作了.
767
00:39:38,443 --> 00:39:40,910
You wouldn't use it as much for things like, I'm fetching
也就这个时候,你需要使用到依赖,如果只是
768
00:39:40,912 --> 00:39:42,879
something from the network and I need to put it in the UI,
需要从网络上拉取数据,然后在 UI 上展示,
769
00:39:42,881 --> 00:39:45,181
and I want that fetched to happen in another queue.
即使想要在其他队列中发起请求,
770
00:39:45,183 --> 00:39:47,317
That kind of stuff you're just going to use the dispatch
dispatch_ 相关方法已经够用了.
771
00:39:47,319 --> 00:39:53,156
underbar, okay. Now, in addition to dispatch underbar,
作为 dispatch_ 的补充,
772
00:39:53,158 --> 00:39:55,625
you have to understand multithreading from
你还需要从 iOS 的 API 中
773
00:39:55,627 --> 00:39:57,827
the perspective of how iOS's API uses it.
理解多线程该如何使用.
774
00:39:57,829 --> 00:40:00,997
Because there are plenty of methods all throughout
因为关于异步线程的方法,
775
00:40:00,999 --> 00:40:05,168
iOS that do what they do asynchronously, using threads.
实在是太多了.
776
00:40:05,170 --> 00:40:08,438
Okay? And the way you see this in the API, is that one of
你在看 API 的时候,其中一个
777
00:40:08,440 --> 00:40:11,941
the arguments to these methods is a block, is a closure,
参数是 block,一个闭包,
778
00:40:11,943 --> 00:40:14,477
right? It's gonna be a closure you provide, and
闭包内容由你提供,
779
00:40:14,479 --> 00:40:16,980
the documentation for that function will say your
文档的解释是,你的闭包
780
00:40:16,982 --> 00:40:21,084
closure's executed off the main cue, asynchronously.
会异步的在主队中执行.
781
00:40:21,086 --> 00:40:23,753
So what kind of methods are we talking about here that do
这就是能解决我们之前问题的方法.
782
00:40:23,755 --> 00:40:27,690
that? Here's one that goes and fetches a URL. Okay,
可以用它来拉取网络数据.
783
00:40:27,692 --> 00:40:30,794
from the internet. Downloads it from the internet. And
进行网络数据下载.
784
00:40:30,796 --> 00:40:32,595
when it, and it does it asynchronously obviously.
它会在异步中处理.
785
00:40:32,597 --> 00:40:34,798
You wouldn't wanna block the main queue waiting for
你也不想要它因为加载 url,而阻塞主队列吧.
786
00:40:34,800 --> 00:40:37,233
this URL to load. And when it comes back,
在响应回来后,
787
00:40:37,235 --> 00:40:39,936
it calls this closure right here, okay? Now
它救护调用这个闭包.
788
00:40:39,938 --> 00:40:44,040
inside this closure, you might want to do some UI things.
在这个闭包中,你就可以做一些 UI 操作了.
789
00:40:44,042 --> 00:40:46,543
Like maybe you downloaded an image and you wanna put it.
比如显示刚下载完成的图片.
790
00:40:46,545 --> 00:40:49,078
But you can't put that code right inside here.
但是你不能直接将 UI 代码写在这,
791
00:40:49,080 --> 00:40:52,015
Because this closure is executed on a different
因为这个闭包会在
792
00:40:52,017 --> 00:40:54,818
thread and on a different queue than the main queue. So
另一个队列的线程中执行,而不是在主队列中执行. 所以
793
00:40:54,820 --> 00:40:56,886
what you would have to do in here is the same thing
这里你还需要
794
00:40:56,888 --> 00:40:57,720
you have to do with the dispatch_async,
像之前创建 dispatch_async 一样,
795
00:40:57,722 --> 00:41:03,560
which is you have to wrap dispatc_async ge_mai_queue
用一个主队列的异步线程去包裹它.
796
00:41:03,562 --> 00:41:07,464
around it, okay? So here's the method. This is an iOS method
就像这样写. 这是个 iOS 的
797
00:41:07,466 --> 00:41:10,333
downloadTaskWithRequest. It takes this last argument,
downloadTaskWithRequest 方法. 最后一个参数,
798
00:41:10,335 --> 00:41:12,836
which is a closure. And inside there you're gonna have to
是一个闭包. 在闭包中,
799
00:41:12,838 --> 00:41:15,572
do this dispatch to the main queue. Because if you wanna do
你需要回到主队列. 因为你要处理
800
00:41:15,574 --> 00:41:18,107
something to the UI, because this is not happening,
UI 相关操作,就不能直接写,
801
00:41:18,109 --> 00:41:21,911
this closure will not be executed on the main queue.
这个闭包不会在主队列中执行.
802
00:41:22,047 --> 00:41:25,982
Got it? So we will talk a little bit about, you're just
明白吗?所以建议大家
803
00:41:25,984 --> 00:41:29,919
gonna run into occasional iOS APIs that take these closures,
可以查查 iOS API 中,异步操作时,
804
00:41:29,921 --> 00:41:32,455
and they're asynchronous. And you just have to know that if
关于这个闭包的描述. 你需要知道,要处理任何
805
00:41:32,457 --> 00:41:33,957
you wanna do anything in the UI, you're gonna have
与 UI 相关的操作,都必须要
806
00:41:33,959 --> 00:41:38,628
to dispatch async back to the main queue. All right, so
先回到主队列. 行,接下来
807
00:41:38,630 --> 00:41:41,364
let's do a demo of this where let's fix Cassini.
我们来完善 Cassini demo 中的问题.
808
00:41:41,366 --> 00:41:44,934
Okay, let's make Cassini so that it's UI's always
优化一下 Cassini 的 UI,
809
00:41:44,936 --> 00:41:49,606
responsive even while it's off downloading these things.
使之在有下载任务的时候,也能及时响应事件.
810
00:41:49,608 --> 00:41:53,376
All right lets go over here.
那么从这里开始吧.
811
00:41:53,378 --> 00:41:55,445
Okay, all this is gonna be, all this code that we're going
先处理这些,这些在 ImageViewController
812
00:41:55,447 --> 00:41:57,514
to have to do is going to be in our ImageViewController.
中的代码.
813
00:41:57,516 --> 00:41:59,916
We're going to make our ImageViewController, so
先要将 ImageViewController 改为
814
00:41:59,918 --> 00:42:02,952
that it is asynchronous, right? That it can load things
异步处理,这样就能异步
815
00:42:02,954 --> 00:42:05,522
asynchronously. Before I do that actually though,
加载数据. 在实际操作之前,
816
00:42:05,524 --> 00:42:07,524
I wanna talk one other thing we should be doing here,
我还要讲到一件事,
817
00:42:07,526 --> 00:42:08,958
probably, in ImageViewController.
需要在 ImageViewController 中注意的事.
818
00:42:08,960 --> 00:42:12,495
Notice that if someone sets our model, we immediately go
注意这里有一段设置模型的代码,我们在里面
819
00:42:12,497 --> 00:42:17,133
off and fetch it, okay? Now what if we never appeared on
获取数据. 但如果我们从未将它展示在
820
00:42:17,135 --> 00:42:21,437
screen? Okay, what if this or something that's in a,
屏幕上呢?如果这是个,
821
00:42:21,439 --> 00:42:24,207
gonna be in a navigation controller, it got created,
这是个 navigation controller 之类的,虽然创建了,
822
00:42:24,209 --> 00:42:27,710
but no one ever clicked to cause it to happen on screen,
并且进行了模型设置,
823
00:42:27,712 --> 00:42:28,311
but someone set our model.
但是从来没有经过点击事件展示到屏幕上.
824
00:42:28,313 --> 00:42:30,980
Then this wouldn't happen in Cassini, this app, but imagine
虽然这问题不会出现在 Cassini 中,但是要小心在
825
00:42:30,982 --> 00:42:35,418
an app where you've got this image MVC and some incentive.
其他 app 中重用这个 image 的 MVC 结构或逻辑的情况.
826
00:42:35,420 --> 00:42:37,186
Really we really wouldn't want to do this
千万不要出现这种额外的资源消耗,
827
00:42:37,188 --> 00:42:39,789
fetch cuz it could be very expensive as we've seen,
这种资源占用非常昂贵.
828
00:42:39,791 --> 00:42:40,623
right? It goes on the network,
明白吗?这需要用到网络,
829
00:42:40,625 --> 00:42:42,292
it's pulling down this thing if I'm paying for
要从网络上拉去数据,而如果我允许
830
00:42:42,294 --> 00:42:44,928
cellular data, it's costing me money! Okay,
蜂窝数据,这会产生费用的!
831
00:42:44,930 --> 00:42:47,931
to get that Cassini image, so I probably don't want to do
所以,除非是在显示到屏幕上时,
832
00:42:47,933 --> 00:42:51,968
this fetch image unless I'm on screen. If I'm on screen,
我才会去获取 Cassini 的图片数据. 只有在屏幕上时,
833
00:42:51,970 --> 00:42:55,004
I definitely want to do this. So here I'm going to say how
我才需要这样做. 那么,如何判断是否在
834
00:42:55,006 --> 00:42:58,341
can I tell if I'm on screen? If my view, okay,
屏幕上呢?如果这个 view,
835
00:42:58,343 --> 00:43:03,913
this is my MVC's view the top level, if it's window
即 MVC 结构中最顶层的 view,它的 window
836
00:43:03,915 --> 00:43:08,618
is not nil. Okay, that's a pretty reliable way to tell,
不是 nil. 则说明视图在屏幕上,
837
00:43:08,620 --> 00:43:12,989
sorry, that you are on screen, right? Because your view,
这种验证方式很靠谱. 因为你的 view,
838
00:43:12,991 --> 00:43:15,692
your mvc's view will not be in a window unless it's on
你的 mvc view 不在屏幕上,才获取不到 window.
839
00:43:15,694 --> 00:43:19,529
screen. Make sense? We haven't talked about window much.
明白吗?我们目前还没讲到太多 window 的知识.
840
00:43:19,531 --> 00:43:22,765
It's that UI Window class I told you that you really never
它就是之前我告诉你们的,
841
00:43:22,767 --> 00:43:23,299
use it, and you don't.
永远不要用,并且你们也没有用的那个 UIWindow 类.
842
00:43:23,301 --> 00:43:26,436
You're not going to send any messages to it here. But
这里不需要调 window 的方法,
843
00:43:26,438 --> 00:43:26,569
whether it's nil or
直接判断它是否是 nil,
844
00:43:26,571 --> 00:43:29,105
not will tell you whether your view is on screen.
就能告诉你 view 是否在屏幕上.
845
00:43:29,107 --> 00:43:32,342
Now what happens though if someone sets my image URL like
那么,现在如果有人在 prepareForSegue 或者
846
00:43:32,344 --> 00:43:36,713
in a prepare for segue and then I do go on screen later?
视图展示在屏幕上时,设置了图片 URL,
847
00:43:36,982 --> 00:43:39,349
Now I have to fetch that image, okay? Cuz now I
我就回去获取图片数据. 因为下一步肯定能
848
00:43:39,351 --> 00:43:41,918
am going to go on screen. So how can I find out in my view
展示到屏幕上. 那么如何得知 view controller
849
00:43:41,920 --> 00:43:44,487
controller lifecycle that now I'm gonna go on screen,
生命周期中,何时展示到屏幕上呢?
850
00:43:44,489 --> 00:43:49,993
which method? Which method in my view controller lifecycle?
哪个方法?view controller 生命周期中的哪个方法?
851
00:43:49,995 --> 00:43:54,497
Do you remember? It called. I'll put it down here
还记得吗?它叫做...
852
00:43:54,499 --> 00:43:59,302
next to this viewDidLoad one. ViewWillAppear. Okay, so
和 viewDidLoad 写在一起吧. 叫做,viewWillAppear.
853
00:43:59,304 --> 00:44:02,071
viewWillAppear is sent to you when you're about to appear on
在即将展示到屏幕上时,会调用 viewWillAppear 方法.
854
00:44:02,073 --> 00:44:06,509
screen. Okay and we always do super.viewWillAppear in
对于 view controller 声明周期的方法,
855
00:44:06,511 --> 00:44:10,146
all these view controller life cycles, all right. So,
我们通常会先调用父类的方法,super.viewWillAppear.
856
00:44:10,148 --> 00:44:14,050
here I'm going to say if my image is nil,
在这里判断,如果 image 是 nil,
857
00:44:14,052 --> 00:44:19,255
then I better go fetch it, okay? So, this is one
那么就获取它,这样处理
858
00:44:19,257 --> 00:44:22,692
thing I've done to increase my performance a little bit. But
能提升一点点性能. 但是
859
00:44:22,694 --> 00:44:24,794
it's still not going to stop things from blocking,
依然没能解决阻塞问题,
860
00:44:24,796 --> 00:44:26,562
because as soon as viewWillAppear happens,
因为当 viewWillAppear 调用时,
861
00:44:26,564 --> 00:44:27,597
and I'm just about to go on screen,
并且视图在屏幕上,
862
00:44:27,599 --> 00:44:30,900
my whole app is going to freeze while I'm out fetching.
整个 app 都被阻塞了,需要去获取数据.
863
00:44:30,902 --> 00:44:32,268
Those big huge images, right?
去获取一张很大的图片.
864
00:44:32,270 --> 00:44:34,437
So I haven't really fixed the problem. I need multithreading
所以我们还没解决这个问题,我需要用到多线程.
865
00:44:34,439 --> 00:44:36,506
for that. But I just wanted to show you to be a little bit
我将会演示如何小心翼翼的
866
00:44:36,508 --> 00:44:40,009
careful about when you fire off an expensive operation.
开启这一昂贵操作的技能.
867
00:44:40,011 --> 00:44:42,178
That's what viewWillAppare, Appear is for.
这也是 viewWillAppear 的作用.
868
00:44:42,180 --> 00:44:44,080
That's really where you wanna fire off things like going
你需要在这里发起所有的操作,
869
00:44:44,082 --> 00:44:48,084
out on the network and stuff, okay? All right, so now let's
例如网络请求什么的. 那么现在就
870
00:44:48,086 --> 00:44:50,620
do our multi guiding and fix this. We're gonna do this here
遵循着多线程规则,来解决阻塞问题吧. 我们需要对
871
00:44:50,622 --> 00:44:53,389
in fetchImage. We're gonna make it so that fetchImage
fetchImage 进行处理. 我们需要使 fetchImage
872
00:44:53,391 --> 00:44:57,126
does the actual fetching in another thread. All right, so
真正的获取事件发生在另一个线程中.
873
00:44:57,128 --> 00:45:00,997
how are we gonna do this? It's pretty simple actually.
那么怎么实现呢?其实很简单.
874
00:45:00,999 --> 00:45:05,968
We're just going to take this code right here which blocks.
只需要将这段代码,这个 block,
875
00:45:05,970 --> 00:45:07,937
This blocks the main queue while it goes out
这个 block 是在主线程进行
876
00:45:07,939 --> 00:45:10,640
on the network, to forget the contents of this URL.
网络请求来获取 url 内容的.
877
00:45:10,642 --> 00:45:13,843
And we're going to use the GCD to
我们使用 GCD 来
878
00:45:13,845 --> 00:45:16,846
put it on a different thread, okay? So I'm just going to
将操作扔到不同的线程中. 所以,代码应该是,
879
00:45:16,848 --> 00:45:21,718
say, dispatch, async, okay? Remember, dispatch async
dispatch_async,记得吗,dispatch_async
880
00:45:21,720 --> 00:45:26,122
takes two arguments. A queue and a block here, a closure or
有两个参数,一个队列和一个 block,即一个闭包或
881
00:45:26,124 --> 00:45:31,194
a function do that. So I'm going to put on one of those,
一个方法. 我准备将处理扔到
882
00:45:31,196 --> 00:45:34,497
concurrent cues. And I'm gonna put it on the one where
并行队列中. 优先级是
883
00:45:34,499 --> 00:45:36,733
the user initiated it but it's not interactive.
user initiated,而不是 interactive.
884
00:45:36,735 --> 00:45:38,634
The user's not in the middle of dragging or something.
因为处理中,用户不会进行拖拽等交互.
885
00:45:38,636 --> 00:45:40,536
They initiated it so it's pretty high priority and
initiated 的优先级已经很高了,
886
00:45:40,538 --> 00:45:45,208
so I get that by saying dispatch_get_global_queue and
调用 dispatch_get_global_queue 来获得队列,
887
00:45:45,210 --> 00:45:47,477
the one I want is
这个参数是
888
00:45:47,479 --> 00:45:54,083
QOS_CLASS_USE_INITIATED. This one right here, okay?
QOS_CLASS_USE_INITIATED. 第一个参数搞定,
889
00:45:54,085 --> 00:45:58,287
This flags is always zero, right? And now I have this
这个标识直接填 0. 接下来配置 block,
890
00:45:58,289 --> 00:46:00,423
block here, so I'm gonna double-click on that.
双击代码提示,
891
00:46:00,425 --> 00:46:00,957
It's really nice, by the way,
就自动补全了,另外,
892
00:46:00,959 --> 00:46:04,193
you can double-click on these things to expand the block.
除了能双击展开 block 以外,
893
00:46:04,195 --> 00:46:06,829
I'm gonna use the clay trailing,
我使用的的是尾闭包的形式,
894
00:46:06,831 --> 00:46:11,400
closure syntax here. Okay for my block, this block, you'll
完善闭包语法. 这里就是我的 block 了,
895
00:46:11,402 --> 00:46:13,836
notice it takes no arguments, returns no arguments.
你可以看到这个 block 没有参数,也没有返回值.
896
00:46:13,838 --> 00:46:18,941
I'm just going to put all this blocking junk inside here,
然后将这一大段 block 代码,扔到尾闭包中.
897
00:46:18,943 --> 00:46:23,012
okay. So this is going to fix my problem, okay.
这样就能解决我们的问题了.
898
00:46:23,014 --> 00:46:28,217
Except that it probably gonna make my app go all strange
不过这样写可能会导致 app 出现以下莫名其妙的问题,
899
00:46:28,219 --> 00:46:32,155
because I'm gonna be trying to do this inside
因为我在
900
00:46:32,157 --> 00:46:36,459
a non-main queue. All right, cuz this queue, this block
非主队列做了这个操作. 因为这个 block
901
00:46:36,461 --> 00:46:40,329
right here is gonna be put on this queue, and so I can't do
会在这个队列中执行,所以我们不能在 block 里面
902
00:46:40,331 --> 00:46:43,933
this inside of here. Question? >> Can you explain
做这个操作. 什么问题?能讲一下
903
00:46:43,935 --> 00:46:44,867
the trailing, the plot,
尾闭包吗?
904
00:46:44,869 --> 00:46:47,804
the trailing plot where you added the parenthesis and
应该在哪添加括弧,它是怎样
905
00:46:47,806 --> 00:46:49,438
what it does? >> Yeah, okay, so
执行的?行,好的,
906
00:46:49,440 --> 00:46:55,511
this used to look like this. Okay, so this is normal.
闭包以前是长这样的,这是一个普通的闭包.
907
00:46:55,513 --> 00:46:57,947
I'm calling this function right here, okay? Sorry,
我在调用这个方法时,哦,不好意思,
908
00:46:57,949 --> 00:47:00,750
this function right here. Here is its first arguments,
这个方法. 这是第一个参数,
909
00:47:00,752 --> 00:47:05,388
see that? Here its second argument, okay.
这是第二个参数.
910
00:47:05,390 --> 00:47:09,158
If the sec, if the last argument of any function is
如果第二个参数,如果最后一个参数接收的是
911
00:47:09,160 --> 00:47:14,697
a closure, then you can put it outside of the parenthesis.
闭包,那么就可以将其写在参数列表以外.
912
00:47:14,699 --> 00:47:17,466
See this dispatch_async parentheses starts here? So
这个 dispatch_async 方法括号从这里开始,
913
00:47:17,468 --> 00:47:20,603
I just took this parentheses off of here and
我只是将这里的反括弧,
914
00:47:20,605 --> 00:47:26,475
I put it over here. Okay, so now this is the first
移到了这里. 所以,现在这里是函数的
915
00:47:26,477 --> 00:47:29,812
argument to this function, inside these parenthesis. And
第一个参数,在参数列表的括号内.
916
00:47:29,814 --> 00:47:33,149
here's the closure which is outside the parenthesis, okay.
而这个闭包,在括号外面.
917
00:47:33,151 --> 00:47:36,185
You definitely wanna get used to that cuz we're almost
这种写法你一定会熟悉的,因为之后我们
918
00:47:36,187 --> 00:47:39,155
always gonna do it that way. And when people design APIs
都会这样写. 当开发者在设计 API 的时候,
919
00:47:39,157 --> 00:47:41,390
they almost always put the closure at the end so
他们尽量将闭包扔到最后,这样
920
00:47:41,392 --> 00:47:44,560
people can do this. All right, so
使用者就可以用尾闭包了.
921
00:47:44,562 --> 00:47:45,595
what am I gonna do about this,
所以我也这样去使用它.
922
00:47:45,597 --> 00:47:49,332
okay? I need this to be back on the main queue. Well,
现在我需要回到主队列.
923
00:47:49,334 --> 00:47:52,902
very simple here. I'm just going to dispatch it back to
非常简单,只需要将它派发回
924
00:47:52,904 --> 00:47:57,707
the main queue. Now, another thing I'm gonna do here,
主队列即可. 除此之外,还有一件事需要处理,
925
00:47:57,709 --> 00:48:02,278
while I'm kinda at it is, let's get this NSData right
先这样,先将这里获取的 NSData
926
00:48:02,280 --> 00:48:06,082
here. I'm gonna assign that to a local variable
使用临时变量接受一下,
927
00:48:06,084 --> 00:48:10,519
here called content of URL, okay? And then I'm gonna put,
叫 contentsOfURL,
928
00:48:10,521 --> 00:48:12,922
so that's gonna be happening in my other queue.
这一部分是发生在另一个队列中的.
929
00:48:12,924 --> 00:48:17,960
And then I'm gonna dispatch async back to the main queue,
然后,调用 dispatch_async 回到主队列,
930
00:48:17,962 --> 00:48:21,597
which I get by dispatch_get_main_queue.
传入 dispatch_get_main_queue.
931
00:48:21,599 --> 00:48:24,901
Okay, same exact thing here, okay, here's the block,
这个是一样的,这个 block,
932
00:48:24,903 --> 00:48:28,604
I'm gonna do the same thing. Click trailing, close your
和之前一样. 改为尾闭包,
933
00:48:28,606 --> 00:48:33,376
notation, and now I'm gonna put this inside here, okay.
然后将代码扔进去,
934
00:48:33,378 --> 00:48:37,413
But now the image data is this thing that I got right there,
现在 imageData 应该是这个,我将它放在这,
935
00:48:37,415 --> 00:48:41,317
so that's content of URL.
所以是 contentsOfURL.
936
00:48:41,319 --> 00:48:46,188
Okay, now this happening on the main queue, all is well.
现在,这就在主队列中执行了,全部搞定.
937
00:48:46,190 --> 00:48:50,459
This image=UIImage, the UI kit thing. This is a UI kit thing,
这个 image 是 UIImage,UIKit 中的那个. 这个 UIKit 元素,
938
00:48:50,461 --> 00:48:52,795
by the way, no, not actually because of the UIImage.
准确的说不是严格是,因为它是 UIImage,
939
00:48:52,797 --> 00:48:55,898
That is the one UI thing you can do on a different thread.
而 UIImage 可以在不同的线程中处理,
940
00:48:55,900 --> 00:48:58,367
But it's because when we say image= down here,
这只是进行了图片下载,
941
00:48:58,369 --> 00:49:01,904
it's gonna do something like imageview.image=new value.
因为我们之后要将 image 赋值给 imageView.image.
942
00:49:01,906 --> 00:49:03,439
Okay, well that's the UI kit right there, so
而 UIImageView 是 UIKit 元素,所以
943
00:49:03,441 --> 00:49:06,575
that has to be on the main thread. So that's why I have
必须要在主线程在中处理. 这才是我们要回到主线程的原因.
944
00:49:06,577 --> 00:49:08,878
to do that. Now notice we still have an error here.
注意我们这里还在报错.
945
00:49:08,880 --> 00:49:11,814
Anyone know what this error is? It's pointing right here.
有人知道这个错误原因吗?有人知道吗?
946
00:49:11,816 --> 00:49:17,987
What do you think it wants there? No idea?
知道编译器想要我们怎么做吗?没人知道?
947
00:49:21,059 --> 00:49:26,128
It wants self there, because this is a closure. Okay,
它想要加上 self,因为这是一个闭包,
948
00:49:26,130 --> 00:49:27,697
this is inside a closure. And remember,
这段代码在闭包中,记住,
949
00:49:27,699 --> 00:49:32,702
whenever we have a closure and we use a property on our
无论何时,在闭包中访问属性或成员变量,
950
00:49:32,704 --> 00:49:36,472
object, we need to put self in there to make it
都需要使用 self 调用,
951
00:49:36,474 --> 00:49:39,342
clear that we understand this is being captured and
以此指明变量值快照,
952
00:49:39,344 --> 00:49:44,480
kept in the heap. So as long as this closure right here,
并将它存到堆中. 所以,无论是这个闭包,
953
00:49:44,482 --> 00:49:46,749
which it's got this closure inside of it.
还是这个包含在闭包内的闭包,
954
00:49:46,751 --> 00:49:49,385
As long as this is out there fetching,
只要是在函数作用域外了,
955
00:49:49,387 --> 00:49:54,190
it's going to keep this view controller in the heap, okay?
都需要将 view controller 保存在堆上.
956
00:49:54,192 --> 00:49:58,194
Now if we wanted to here, we could do the week self thing
如果我们要在这里使用,还需要用 weak 修饰 self,
957
00:49:58,196 --> 00:50:02,398
and have week self question mark and that way if the image
防止在图片数据回来的时候,self 已经被释放,
958
00:50:02,400 --> 00:50:06,469
came back and our MVC had been thrown out of the heap,
导致 MVC 抛出的堆内存异常.
959
00:50:06,471 --> 00:50:09,405
it would be fine, we would just ignore it.
这样就能避免了,就能忽略掉异常了.
960
00:50:09,407 --> 00:50:11,841
Okay, but I'm pressed for time so I'm not gonna do that but
因为时间原因,我就不演示了,
961
00:50:11,843 --> 00:50:15,277
you could hopefully imagine doing that. Okay, so
不过你们应该能想象出来.
962
00:50:15,279 --> 00:50:17,179
everyone see what we've done here?
每个人都能理解了吗?
963
00:50:17,181 --> 00:50:20,883
So that's really all we need to do. One thing that it's
这就是我们需要的全部处理. 对了,
964
00:50:20,885 --> 00:50:24,053
a little, now, I told you that multithreading, it's pretty
还有一小点,现在我讲解了多线程,
965
00:50:24,055 --> 00:50:26,522
easy to read this code and understand what's going on.
非常简单,一看代码就能明白.
966
00:50:26,524 --> 00:50:29,725
But you've got to think of the bigger picture of what's
但是你想想,如果是在用多线程
967
00:50:29,727 --> 00:50:32,962
happening with multithreading here. For example,
加载一张很大的图片呢,会怎样. 比如,
968
00:50:32,964 --> 00:50:37,500
what happens in Cassini if I click on the Cassini,
在点击 Cassini 按钮时,Cassini 会怎样,
969
00:50:37,502 --> 00:50:40,236
which is a kind of a small image, and
这还是张小图,
970
00:50:40,238 --> 00:50:43,305
then I change my mind and click on Earth.
如果我临时改变主意,点击了 Earch.
971
00:50:43,307 --> 00:50:48,377
Okay, so Cassini, which is small, comes back, okay?
现在,Cassini 这张小图加载完成,
972
00:50:48,379 --> 00:50:51,280
This code is gonna put Cassini on screen. But then the Earth
那么 Cassini 就会渲染在界面上,这时候,Earth
973
00:50:51,282 --> 00:50:53,883
is gonna come back and this code's gonna put the Earth on
加载完了,Earth 就会覆盖在 Cassini 上,
974
00:50:53,885 --> 00:50:55,951
top of it, okay? So I'm gonna get this flashing thing,
所以就会导致 Cassini 图片闪一下,
975
00:50:55,953 --> 00:50:58,788
Cassini, Earth. Or worse, if I clicked Earth first and
接着显示 Earth. 更糟的是,如果我先点击的 Earth,
976
00:50:58,790 --> 00:51:01,057
then clicked Cassini, it would show Cassini and
之后再点击 Cassini,万一先展示 Cassini,
977
00:51:01,059 --> 00:51:03,359
then later Earth would come in and it would be showing Earth,
之后再展示 Earth,然后 Earth 就是最终渲染的图片,
978
00:51:03,361 --> 00:51:04,693
even though I clicked on Cassini.
即使我最后点击的是 Cassini.
979
00:51:04,695 --> 00:51:07,830
Okay, do you see the problem, is that I'm firing off these
能明白这个问题吗?我在不同的线程中,
980
00:51:07,832 --> 00:51:09,265
network things in other threads.
发起的网络请求,
981
00:51:09,267 --> 00:51:11,767
They're coming back at different times. So
他们的响应时间可能会不同.
982
00:51:11,769 --> 00:51:12,435
really I want to,
所以,
983
00:51:12,437 --> 00:51:14,870
If I'm gonna be a good programmer here,
如果我是一个优秀的开发者,
984
00:51:14,872 --> 00:51:16,172
multithreaded, I need to check and
在多线程中,我需要检查响应的
985
00:51:16,174 --> 00:51:19,208
make sure that the URL that I'm fetching,
URL,是否是我之前获取的那个,
986
00:51:19,210 --> 00:51:24,480
that's this URL right here, is still equal to the image URL
即这个 URL,是否匹配这个 imageURL.
987
00:51:24,482 --> 00:51:29,618
that I ask for. Okay, then I'll do this. If it's not,
如果匹配,才赋值. 如果不匹配,
988
00:51:29,620 --> 00:51:34,156
then maybe I could print something like ignored data
那么就输出 ignore data,
989
00:51:34,158 --> 00:51:39,061
returned from URL, and then we'll put the URL in here,
returned from URL,然后将 url 拼在后面.
990
00:51:39,063 --> 00:51:43,232
okay? And that needs self here, of course, cuz that's
这里也需要加上 self,毕竟
991
00:51:43,234 --> 00:51:47,002
inside this closure as well, all right? Everybody got that?
这也是在闭包中. 都懂了吗?
992
00:51:47,004 --> 00:51:50,439
So let's go ahead and run, see if this works. So
那我们来运行下,看看效果.
993
00:51:50,441 --> 00:51:54,110
what we should have now is a very responsive UI, okay,
所以,现在拿到的是一个丝滑的 UI,
994
00:51:54,112 --> 00:51:56,412
all the time, no matter what we click on, so let's try it.
整个程序都是,无论是点击哪个按钮,试试,
995
00:51:56,414 --> 00:51:59,482
Earth, Cassini, see I'm able to click on different things.
Earth,Cassini,看见了吗,都没有被阻塞.
996
00:51:59,484 --> 00:52:04,587
I can still rotate, okay. Whoops, wrong way around. And,
还可以进行旋转. 噢喔...旋转方式错了.
997
00:52:04,589 --> 00:52:09,125
the it's gonna pick whichever the last one that I asked for.
无论我最后请求的是哪个,
998
00:52:09,127 --> 00:52:11,060
It's still fetching right here. It's gonna eventually,
都会获取到争取的图片. 最终,
999
00:52:11,062 --> 00:52:17,633
hopefully, get one. And maybe it's even easier to see this,
都会获取到一张. 可能要等会才能请求完,
1000
00:52:17,635 --> 00:52:20,402
maybe. Let's stop this action, go look on our iPhone, cuz you
停止这个程序,重新跑在 iPhone 上看看,
1001
00:52:20,404 --> 00:52:23,172
can really see it happening on iPhone because when we click
因为这个过程在 iPhone 上更清晰,
1002
00:52:23,174 --> 00:52:26,142
on them, it actually segues and comes back.
它会有视图的转场效果.
1003
00:52:26,144 --> 00:52:31,881
So watch this. Are we running here?
来看看. 跑起来了吗?
1004
00:52:31,883 --> 00:52:37,887
Oops, no, we want iPhone.
哦,不对,我们需要的是 iPhone.
1005
00:52:40,491 --> 00:52:43,759
Okay, so we go back here. Now I click Cassini,
好了,回到这个界面,点击 Cassini,
1006
00:52:43,761 --> 00:52:46,228
it's loading it, but I can go back, okay.
程序正在加载,这时我点击返回,
1007
00:52:46,230 --> 00:52:48,197
While this is still loading, I can go back and
但其实加载还在继续,
1008
00:52:48,199 --> 00:52:51,634
change my mind because my UI is highly responsive,
我能临时改变主意,能点击返回,因为 UI 并未被阻塞,
1009
00:52:51,636 --> 00:52:53,636
even though it's loading those things in the background.
即使图片在后台加载.
1010
00:52:53,638 --> 00:52:56,705
When they eventually arrive, it's gonna put them in the UI,
当图片下载完成后,便会在 UI 上显示,
1011
00:52:56,707 --> 00:53:00,109
okay. But while they're out there fetching I can do things
但在下载过程中,
1012
00:53:00,111 --> 00:53:06,882
like go back in my navigation controller. Make sense,
我可以做其他的操作,比如点击返回按钮等。看吧,
1013
00:53:06,884 --> 00:53:13,522
see that? >> [INAUDIBLE]
没问题。[清不清楚]
1014
00:53:13,524 --> 00:53:14,056
>> What happens, what?
什么事?怎么?
1015
00:53:14,058 --> 00:53:14,857
>> [INAUDIBLE] Image each time
[听不清] 假设每次
1016
00:53:14,859 --> 00:53:17,560
you click out. >> So the question is, if I
都快速返回。那么,每次返回后,
1017
00:53:17,562 --> 00:53:20,596
click out, and then click back in, does it reload the image?
再点进去,会重新下载图片吗?
1018
00:53:20,598 --> 00:53:22,831
And the answer is absolutely, because when I click out,
答案显而易见,因为再返回后,
1019
00:53:22,833 --> 00:53:26,068
it throws that MVC out of the heap. Okay, it's gone.
MVC 的堆内存就被销毁了,也就不存在了。
1020
00:53:26,070 --> 00:53:28,103
Now actually, that MVC is not gonna immediately leave
但这次,MVC 却没有立即销毁,
1021
00:53:28,105 --> 00:53:30,773
the heap, cuz that closure that's doing the fetching is
因为闭包还在执行下载,
1022
00:53:30,775 --> 00:53:33,108
still holding it in the heap. But once that fetch comes
持有了该内存。当数据下载完成后,
1023
00:53:33,110 --> 00:53:36,779
back, then it'll leave the heap and the image with it. So
堆内存便销毁了,下载的图片也是。
1024
00:53:36,781 --> 00:53:40,583
yeah, every time we're re-fetching, okay?
所以,每次都是重新下载的,明白?
1025
00:53:41,552 --> 00:53:41,684
So see how we built this nice responsive UI right now, okay?
这就是我们讲到的快速响应 UI。
1026
00:53:41,686 --> 00:53:45,221
Everybody got that?
大家都明白吗?
1027
00:53:45,223 --> 00:53:48,090
Even though it's still taking a long time to fetch things,
即使数据下载依然需要很长时间,
1028
00:53:48,092 --> 00:53:51,493
we can still navigate. Now, what's a little weird about
但并不影响跳转。但是,UI 还有些
1029
00:53:51,495 --> 00:53:54,330
this UI, though, is that while it's fetching,
不足,在数据下载时,
1030
00:53:54,332 --> 00:53:57,633
like when I'm here, it's not clear what's going on.
比如这个界面,并不知道它在处理什么。
1031
00:53:57,635 --> 00:53:59,168
It looks like there's just no Earth image,
这里好像并没有 Earth 的图片,
1032
00:53:59,170 --> 00:54:02,171
I guess. I don't know, I guess not, I don't see it. Wouldn't
我也不知道,可能是吧,什么都看不见。如果
1033
00:54:02,173 --> 00:54:04,840
it be cool if we could give some feedback to the user?
能给用户一个标识不是更好吗?
1034
00:54:04,842 --> 00:54:08,544
Hey, I'm working on it, okay, this image is big.
Hey,我正在努力加载呢,图片太大了。
1035
00:54:08,546 --> 00:54:09,545
All right, and we can do that.
是的,我们可以处理。
1036
00:54:09,547 --> 00:54:12,414
And the cool way to do that is with a little spinner, okay,
可以在这里加个指示器,
1037
00:54:12,416 --> 00:54:15,651
a little animating. You see them all in apps little thing
一个小动画。在很多 app 中都能看到
1038
00:54:15,653 --> 00:54:17,886
that spins around basically telling him yeah, yeah,
指示器,告知系统收到了交互,
1039
00:54:17,888 --> 00:54:20,789
I know, I'm working on it. So let's put a spinner in there.
正在进行处理。那么,让我们在这里加上指示器吧。
1040
00:54:20,791 --> 00:54:24,059
Turns out to be really easy to put, do spinners in iOS.
在 iOS 中加指示器,真的非常简单。
1041
00:54:24,061 --> 00:54:26,395
We're just gonna go to the storyboard, okay.
打开 storyboard,
1042
00:54:26,397 --> 00:54:30,232
I'm gonna put the spinner here in this view. Now when I put
将指示器加到这个 view 中。但注意,
1043
00:54:30,234 --> 00:54:32,167
it in there, I'm gonna have to be very careful.
加进去要非常小心。
1044
00:54:32,169 --> 00:54:32,501
And this is a good chance for
正好给大家
1045
00:54:32,503 --> 00:54:35,004
me to show you more about the document outline. But
详细演示下 document outline。
1046
00:54:35,006 --> 00:54:38,140
watch this, so I'm gonna go down here. The spinner is just
看这里,滚到这里,有个可以直接
1047
00:54:38,142 --> 00:54:40,509
a thing you can drag out. You see it right here.
进行拖拽的指示器。就是这个。
1048
00:54:40,511 --> 00:54:42,544
Now watch what happens when I drag this out, okay, and
将它拖到界面中,
1049
00:54:42,546 --> 00:54:46,215
I try to use the blue lines to put it in the middle, drop it.
蓝色辅助线可帮助居中,就扔到这。
1050
00:54:46,217 --> 00:54:48,784
It looks like, okay, that worked, that's nice.
看起来不错,正是我们需要的。
1051
00:54:48,786 --> 00:54:50,786
I can even go here and inspect it up here.
还可以打开上面的 inspect,
1052
00:54:50,788 --> 00:54:54,123
You see, I can make a nice larger one. Maybe I'll even
这里,可以选择更大的样式。也可以
1053
00:54:54,125 --> 00:54:58,594
make it blue instead of white. Let's have it hide itself
将其改为蓝色。还可以设置
1054
00:54:58,596 --> 00:54:59,595
when it stops animating.
动画停止时,自动隐藏。
1055
00:54:59,597 --> 00:55:01,597
We can do all kinds of cool stuff here. But
这里可以设置很多属性。
1056
00:55:01,599 --> 00:55:04,400
actually something terrible has happened,
但同时引发了一个问题,
1057
00:55:04,535 --> 00:55:06,869
which is that when I dragged that thing out and
在我将控件拖出来,
1058
00:55:06,871 --> 00:55:08,037
dropped it in the middle there,
然后放置到中间时,
1059
00:55:08,039 --> 00:55:11,173
it made it a subview of the Scroll View, okay?
这个控件变成了 Scroll View 的子视图。
1060
00:55:11,175 --> 00:55:14,877
In the interface builder when you drag a view out on top of
在 interface builder 中,如果将一个控件拖到
1061
00:55:14,879 --> 00:55:16,578
another view, it makes it a subview of that.
另一个控件上,那该控件就成了另一个的子视图。
1062
00:55:16,580 --> 00:55:19,148
Now you can't really tell that here, but that's what this
直接从这里不大看的出来,
1063
00:55:19,150 --> 00:55:21,483
little guy over in the corner, the document outline,
要点击角落里的这个小按钮,打开 document outline,
1064
00:55:21,485 --> 00:55:24,353
is really great at. If you look at the document outline,
这非常棒,在 document outline 中,
1065
00:55:24,355 --> 00:55:27,189
do you see how the activity indicator is indented
能看到 activity indicator 在
1066
00:55:27,191 --> 00:55:30,893
from the Scroll View? Okay, that means it's a subview
Scroll View 中吗?这就意味着,它成为了
1067
00:55:30,895 --> 00:55:34,496
of the Scroll View. Now, if you don't want that, okay,
Scroll View 的子视图。如果你不想这样,
1068
00:55:34,498 --> 00:55:36,665
no problem. You can just drag it up above.
没问题,将它拖到上面。
1069
00:55:36,667 --> 00:55:39,568
Now, it's not a subview of the Scroll View. Now, of course,
现在,它就不再是 Scroll View 的子视图了。当然,
1070
00:55:39,570 --> 00:55:41,804
it's not in the right order right now, because I want this
这还不是正确的视图层级,因为我想要指示器
1071
00:55:41,806 --> 00:55:44,340
thing to be in front of the Scroll View. So, I'm gonna put
放在 Scroll View 前面。所以,要把
1072
00:55:44,342 --> 00:55:47,643
the Scroll View on top of it. So, now they're siblings, and
Scroll View 移到指示器上面。现在,它们就在一个层级了,并且
1073
00:55:47,645 --> 00:55:51,447
this one is in front, because remember, the subview is list,
这个再上面,记住,子视图是链表,
1074
00:55:51,449 --> 00:55:52,681
the top ones are in the back.
越在后面,层级越高。
1075
00:55:52,683 --> 00:55:54,650
The other ones go towards the front. So,
升序排列。所以,
1076
00:55:54,652 --> 00:55:58,020
now we have this thing in the front, okay? Now,
现在这个就在前面了。
1077
00:55:58,022 --> 00:56:00,522
another thing is, what if I wanna have,
还有个问题,如果我想,
1078
00:56:00,524 --> 00:56:03,359
constraints on this to keep this in the middle?
想要给该空间加约束,是它一直居中,该怎么办?
1079
00:56:03,361 --> 00:56:05,060
Well, I have to be very careful here too,
这一步同样需要仔细。
1080
00:56:05,062 --> 00:56:08,230
cuz those dashed lines. We're referring to the scroll view.
因为这里的约束,都是与 scroll view 关联的。
1081
00:56:08,232 --> 00:56:11,033
So if I tried to do reset to suggested constraints here,
所以,先要重置默认的约束,
1082
00:56:11,035 --> 00:56:13,869
I'm gonna get constraints that constrain the activity
会发现现在 activity indicator
1083
00:56:13,871 --> 00:56:16,205
indicator to the scroll view. I don't want that.
的约束都是基于 scroll view 的,我可不想这样。
1084
00:56:16,207 --> 00:56:19,575
I want the activity indicator to be in the middle of its
我希望 activity indicator 相对其父视图
1085
00:56:19,577 --> 00:56:19,842
superview.
居中。
1086
00:56:19,844 --> 00:56:23,612
And watch this, you can do control drag in the document
看仔细了。你可以在 document outline 中,
1087
00:56:23,614 --> 00:56:26,782
outline. So I'm gonna control drag from my activity
按住 control。从 activity indicator 这里,
1088
00:56:26,784 --> 00:56:30,919
indicator to its superview in the document outline,
按住 control,拖到 document outline 中的父视图上,
1089
00:56:30,921 --> 00:56:33,255
not over here, but in the document outline.
不是这,是在 document outline 中。
1090
00:56:33,257 --> 00:56:35,758
And when I let go, I can do things like, yeah,
松开鼠标,可以看到这个,是的,
1091
00:56:35,760 --> 00:56:41,764
please center it vertically and horizontally in yourself.
请在水平与竖直方向都居中。
1092
00:56:41,766 --> 00:56:46,568
Okay? So now this thing is centered and middle. Okay?
那么,现在就居中了。
1093
00:56:46,570 --> 00:56:50,939
So, now we've got the spinner, how do we do anything with it?
至此指示器就创建好了,那应该怎么处理逻辑呢?
1094
00:56:50,941 --> 00:56:51,440
Well, we need an outlet to it.
现在需要一个它的 outlet。
1095
00:56:51,442 --> 00:56:55,511
So, I'm going to bring out my code over here, okay?
把代码显示出来,
1096
00:56:55,513 --> 00:56:56,712
Do it automatic.
自动关联文件。
1097
00:56:56,714 --> 00:56:59,681
Got our image view controller here, let's give it to this,
这是 image view controller。关掉这个,
1098
00:56:59,683 --> 00:57:02,851
give it to that, give it some room. Okay so
关掉这个,腾点空间出来。好的,
1099
00:57:02,853 --> 00:57:05,721
I'm just going to create an outlet from this little
将指示器从这拖到这,
1100
00:57:05,723 --> 00:57:10,125
spinner right here, over here. See it make sure that it's
创建 outlet。注意这里的类型
1101
00:57:10,127 --> 00:57:13,796
type is activity indicator view not something else, and
是 activity indicator,而不是别的。
1102
00:57:13,798 --> 00:57:14,730
I'm just going to call it spinner,
名字就叫 spinner 吧,
1103
00:57:14,732 --> 00:57:17,499
I like spinner as my name. And it's just an outlet,
我喜欢 spinner 这个名字,这是个 outlet,
1104
00:57:17,501 --> 00:57:20,769
it's a normal outlet just like our scroll view or anything
非常普通的 outlet,和 scroll view 或其他 outlet
1105
00:57:20,771 --> 00:57:23,272
else is an outlet. Okay just like your display was in your
没区别。就像计算器能显示一样,
1106
00:57:23,274 --> 00:57:27,643
calculator there's nothing particularly special about it.
没什么特别的。
1107
00:57:27,645 --> 00:57:31,413
So now that we have this nice spinner right here,
现在,我们拿到了指示器变量,
1108
00:57:31,415 --> 00:57:34,450
we just need to turn it on and turn it off. So where are we
只需要控制它的开始与结束。那什么时候开始呢?
1109
00:57:34,452 --> 00:57:37,352
gonna turn it on? Well, I'm gonna turn it on right before
我觉得它应该在其他线程开始
1110
00:57:37,354 --> 00:57:41,023
I start doing something on that other thread, okay. So
处理事情的时候开始。
1111
00:57:41,025 --> 00:57:44,493
right here when I dispatch onto this other thread to go
就是这里,在将处理 NSData 的 block 派发到
1112
00:57:44,495 --> 00:57:47,629
do this NSData, which is gonna block, I'm just gonna say
其他线程的时候,让
1113
00:57:47,631 --> 00:57:53,435
spinner startAnimating. And I'm gonna be even trickier and
spinner startAnimating。我准备在这里增加点难度,
1114
00:57:53,437 --> 00:57:56,972
go spinner?.startAnimating just in case
改为 spinner?startAnimating,防止
1115
00:57:56,974 --> 00:58:00,776
I'm fetching my image like as a result of prepare for
在 prepare for segue 的时候
1116
00:58:00,778 --> 00:58:03,846
segue, okay, because there my outlets aren't set.
调用该方法获取图片,而这个时候,outlet 还未创建。
1117
00:58:03,848 --> 00:58:05,747
So even if my app spinner's not set,
这样的话,在 spinner 还没创建时,
1118
00:58:05,749 --> 00:58:09,751
I'll just do nothing. Now where do I wanna turn it off?
没有任何响应就行。那应该在何时停止呢?
1119
00:58:09,753 --> 00:58:11,954
Its actually two places that you can turn this off.
应该有两处地方需要停止。
1120
00:58:11,956 --> 00:58:16,892
One good place might be down here when your image gets set.
一处是在图片加载完成后。
1121
00:58:16,894 --> 00:58:18,994
Okay? Cuz if someone sets your image,
在图片加载完成后,
1122
00:58:18,996 --> 00:58:23,198
then you don't need to be spending anymore.
spinner 就没必要显示了。
1123
00:58:23,367 --> 00:58:25,434
By definition, you're showing the image.
理论上,这里图片都已经展示了。
1124
00:58:25,436 --> 00:58:29,004
So, it will make no sense to be spinning such a good place.
所以,spinner 已经完成使命了。
1125
00:58:29,006 --> 00:58:31,106
Got to be a little careful though here because,
另外一个地方需要特别小心,
1126
00:58:31,108 --> 00:58:34,109
what happens when we get in here, okay?
这里会出现什么情况呢,
1127
00:58:34,111 --> 00:58:36,378
And you get to the URL here, and
这里是获得 URL,
1128
00:58:36,380 --> 00:58:38,647
the image data wasn't able to be found, so
如果 URL 未找到图片数据,
1129
00:58:38,649 --> 00:58:41,583
you never call set image, so it's never going to stop.
图片就无法创建,那指示器就一直没停止。
1130
00:58:41,585 --> 00:58:45,387
So you probably want to put in else spinner stop animating
所以这里也需要加上 spinner 停止的代码。
1131
00:58:45,389 --> 00:58:48,490
here as well. Okay so this is gonna stop the spinner in
所以, spinner 就会在
1132
00:58:48,492 --> 00:58:52,094
the case where it went off to the Internet to get something,
无网络,或者未找到数据的情况下,
1133
00:58:52,096 --> 00:58:55,030
and it couldn't find it. Okay, eh,
停止。哦,
1134
00:58:55,032 --> 00:59:00,969
little tricky. Okay, so let's see how that works.
小失误。好,运行看看效果。
1135
00:59:06,644 --> 00:59:08,810
Alright, here we go, let's try Earth, there it is,
跑起来了,打开 Earth,对,没错,
1136
00:59:08,812 --> 00:59:11,513
it's spinning, and we can go back, let's try Cassini, it's
有指示器了,点击返回,打开 Cassini,也有
1137
00:59:11,515 --> 00:59:15,484
spinning. Now when Cassini appears it stops spinning, and
指示器。在 Cassini 加载完后,指示器停止,
1138
00:59:15,486 --> 00:59:19,354
so it hides because we had, we set this switch over here
并且自动消失,因为我们之前字 inspect 中,
1139
00:59:19,356 --> 00:59:23,158
on this one we inspected it called hides when stopped,
将它设置为停止就自动隐藏,
1140
00:59:23,160 --> 00:59:28,196
right. And so since it hides when stopped, it's not here
没问题。所以,在停止后,指示器
1141
00:59:28,198 --> 00:59:32,734
anymore okay. [INAUDIBLE] Saturn,
就不见了。[听不清] Saturn,
1142
00:59:32,736 --> 00:59:38,974
spins okay then we can rotate. Big image, taking a long time.
旋转屏幕。是个大图,需要更长的时间。
1143
00:59:38,976 --> 00:59:43,312
There it is, okay? So see how we turn this into an app, it's
完成,没问题。这就是在 app 中的运用,
1144
00:59:43,314 --> 00:59:45,948
really responsive and really nice and it's being as fast as
非常友好,非常漂亮,说多快有多快。
1145
00:59:45,950 --> 00:59:47,983
it can. It's getting those images as fast as it can,
图片现在说多快有多快,
1146
00:59:47,985 --> 00:59:51,153
but from the user standpoint, it's very responsive.
从用户的角度来说,也非常友好。
1147
00:59:51,155 --> 00:59:53,288
You really wanna go with that way.
大家以后也应该这样做。
1148
00:59:53,290 --> 00:59:57,326
Okay, so now I'm gonna show two unrelated to this, okay?
接下来,我将提到两个不相关的东西。
1149
00:59:57,328 --> 01:00:01,697
But did you've asked about in Piazza. One is notice that
大家对 Piazza 有以为吗。在运行的时候,
1150
01:00:01,699 --> 01:00:07,235
when we run this, let's run again, watch what happens.
重新运行下,看看出了什么问题。
1151
01:00:07,972 --> 01:00:14,009
When we run, we get an empty image view controller
在运行起来时,image view controller 是空的,
1152
01:00:14,011 --> 01:00:17,012
which really doesn't make any sense. What we'd
而这不应该出现才对。我们想要的是,
1153
01:00:17,014 --> 01:00:19,848
really want it to, when we first run and this is empty,
在第一次运行的时候,这是空的,
1154
01:00:19,850 --> 01:00:23,251
we want it to come up showing this view controller. So
而我们想要的是展示这个 controller。
1155
01:00:23,253 --> 01:00:24,152
this is like with your calculator,
这和之前的计算器项目类似,
1156
01:00:24,154 --> 01:00:25,921
you don't want it to come up showing empty graph.
我们不想展示空的界面。
1157
01:00:25,923 --> 01:00:28,857
You want it to come up showing the calculator because
空界面什么用都没有,应该展示
1158
01:00:28,859 --> 01:00:29,391
the empty graph is useless,
计算器界面才对。
1159
01:00:29,393 --> 01:00:32,394
it just forces them to do that back thing right here for
这个还必须要手动返回,而且
1160
01:00:32,396 --> 01:00:35,931
no good reason. Okay, so how do we do that, okay this is
还不知道为什么要这样。那么,该怎么处理呢,
1161
01:00:35,933 --> 01:00:38,367
another case where we need to use delegation.
这是另个需要用到 delegation 的原因。
1162
01:00:38,369 --> 01:00:43,105
In this case it's the split view delegate. Okay, split
这里要用到的是 split view delegate。split
1163
01:00:43,107 --> 01:00:45,407
view controller's delegate. So what does this code look like?
view controller 的 delegate。那代码该怎么写呢?
1164
01:00:45,409 --> 01:00:48,143
I'm gonna put this in Cassini, view controller right here.
我准备把代码放在 Cassiini view controller 的这个地方。
1165
01:00:48,145 --> 01:00:51,113
The first thing I need to do is I need to make the Cassini
首先,我准备,我准备将 Cassini view controller 设为
1166
01:00:51,115 --> 01:00:55,150
view controller be its own split view's delegate.
split view 的 delegate。
1167
01:00:55,152 --> 01:00:57,052
In other words, it needs to be the delegate of
也就是说,它需要成为
1168
01:00:57,054 --> 01:00:57,319
the split view it's in.
split view 的代理。
1169
01:00:57,321 --> 01:01:01,156
So I'm gonna do this in viewDidLoad. Okay sometimes
我准备在 viewDidLoad 里面处理。
1170
01:01:01,158 --> 01:01:03,525
viewDidLoad is not quite early enough to do things for
在 viewDidLoad 中,获取 split view 才
1171
01:01:03,527 --> 01:01:07,462
split views. We might have to do it in a wait for even. But
不算太早。别的方法可能还要判断时机,而
1172
01:01:07,464 --> 01:01:10,165
this will work in viewDidLoad. And I'm just going to set
viewDidLoad 就不用。我准备在这里设置
1173
01:01:10,167 --> 01:01:14,136
my split view controllers remember MVC knows the split
split view controller,每个 MVC 能拿到 split
1174
01:01:14,138 --> 01:01:16,872
view controller its in. Or the navigation controller its in,
view controller 变量。Navigation controller 也可以,
1175
01:01:16,874 --> 01:01:18,106
or the tab bar controller its in. So
tab bar controller 也行。
1176
01:01:18,108 --> 01:01:22,611
SplitViewController says what it is, might be nil, okay? But
而 splitViewController 也可能为空,
1177
01:01:22,613 --> 01:01:25,514
if it's not I'm gonna set its delegate to be myself.
如果不为空,则设置 self 为 delegate。
1178
01:01:25,516 --> 01:01:29,284
Of course as soon as I do this I'm getting an error because
当然,这样处理会出现错误,
1179
01:01:29,286 --> 01:01:32,954
I didn't say that I was a UI SplitViewController delegate.
因为 self 还未遵循 UISplitViewControllerDelegate。
1180
01:01:32,956 --> 01:01:33,822
So I need to go up here and
滚动到顶部,
1181
01:01:33,824 --> 01:01:37,793
say that I'm a UI SplitViewController delegate.
声明 self 遵循 UISplitViewControllerDelegate。
1182
01:01:39,096 --> 01:01:43,365
All right that satisfies that error. All is good. Now which
好的,错误解决了。没问题,那么,
1183
01:01:43,367 --> 01:01:46,668
split view controller method am I going to implement?
应该实现 split view controller 的哪个方法呢?
1184
01:01:46,670 --> 01:01:49,538
Okay hold on your chairs for this one, okay
抓紧椅子,别被吓到了。
1185
01:01:49,540 --> 01:01:52,007
by the way again on the split view controller delegate so
因为已经遵循 split view controller delegate,所以
1186
01:01:52,009 --> 01:01:54,142
I can just start typing split view and you can see it
键入 split view 就可以索引出方法,看吧,
1187
01:01:54,144 --> 01:01:56,678
shows me all the split view controller delegate methods.
这些,全都是 split view controller delegate 的方法。
1188
01:01:56,680 --> 01:01:59,781
There's quite a few. Okay about a dozen of them.
有很多方法,大概十多个。
1189
01:01:59,783 --> 01:02:02,918
And the one I want is the one that controls
而我需要的那个,
1190
01:02:02,920 --> 01:02:07,222
the collapsing of the detail on top of the master.
是可以控制 detail 压缩的方法。
1191
01:02:07,224 --> 01:02:10,058
Because I don't want it to collapse that detail on top of
因为在 detail 没有数据时,不需要将
1192
01:02:10,060 --> 01:02:13,595
the master if the detail is empty, okay?
detail 显示出来。
1193
01:02:13,597 --> 01:02:14,429
If it doesn't have an image in there,
如果没有图片,
1194
01:02:14,431 --> 01:02:17,666
I don't want it to do that. So this thing that controls that
就不需要显示。这个方法叫做,
1195
01:02:17,668 --> 01:02:22,738
it's called, called secondary view controller,
叫做 secondary view controller,
1196
01:02:22,740 --> 01:02:26,708
on to primary view controller. Okay, called
on to primary view controller。
1197
01:02:26,710 --> 01:02:29,511
secondary view controller, on to primary view controller.
secondary view controller,on to primary view controller。
1198
01:02:29,513 --> 01:02:32,748
Now, notice it returns a Bool, this Bool
注意,返回值为 Bool,如果
1199
01:02:32,750 --> 01:02:35,650
you return true if you took care of this.
返回 true,就是自行处理。
1200
01:02:35,652 --> 01:02:40,589
Return false if you want the system to do the collapse.
如果返回 false,表示交由系统处理。
1201
01:02:40,591 --> 01:02:43,492
Okay, so this is a way where the delegate can do this
那么,这个代理方法就是用来处理
1202
01:02:43,494 --> 01:02:44,726
collapse if it wants, or not, and
是否需要压缩的,
1203
01:02:44,728 --> 01:02:47,629
it just lets the split view know whether it did it or not.
而且,只是告知 split view 是否需要。
1204
01:02:47,631 --> 01:02:50,232
So that's what the bool is, let it know when it did it.
这就是这个 bool 值的作用,用于告知 split 的。
1205
01:02:50,234 --> 01:02:53,568
So, this is actually quite simple right here.
那这样就非常简单了。
1206
01:02:53,570 --> 01:02:56,738
We just want to get the image view controller,
我么只想获得 image view controller,
1207
01:02:56,740 --> 01:03:00,675
okay? Well first of all let's make sure that the primary
首先判断 primary view controller
1208
01:03:00,677 --> 01:03:05,881
view controller, content view controller, is ourself.
的 content view controller 是 self。
1209
01:03:05,883 --> 01:03:08,083
Now it should be, because we're setting ourselves
这个应该是没问题的,因为我们之前将 self
1210
01:03:08,085 --> 01:03:09,718
as our own split view delegate, so we should be
设为了 split view controller 的 delegate,所以
1211
01:03:09,720 --> 01:03:13,188
the primary view controllers, the masters content Okay?
self 应该是 primary view controller 的 master content。
1212
01:03:13,190 --> 01:03:16,658
So we showed you that, that should not be a problem. But
所以写的这个判断,是能成功的。
1213
01:03:16,660 --> 01:03:20,896
now I'm gonna say, on top of that, if I can let ivc =
接下来,在括号中写,if let ivc =
1214
01:03:20,898 --> 01:03:24,633
the secondaryViewController, that's the detail.
secondaryViewController,即 detail 页面,
1215
01:03:24,635 --> 01:03:30,238
It contentViewController as an ImageViewController. Okay? So,
的 contentViewController,并强转为 ImageViewController。也就是说,
1216
01:03:30,240 --> 01:03:36,011
if have a ImageViewController as my detail, and
如果 detail 是 ImageViewController,然后
1217
01:03:36,013 --> 01:03:39,281
where, everyone know about where, right? You did your
where,where 大家应该都知道吧?如果做了
1218
01:03:39,283 --> 01:03:41,783
reading assignments, you know about where. Where is
预习,那应该是没问题的。 Where 是
1219
01:03:41,785 --> 01:03:44,686
an additional clause that you can add onto an if, okay?
可以加载 if 之后的从句。
1220
01:03:44,688 --> 01:03:47,856
So I'm gonna say where the image view controllers
继续写,wherer image view controller
1221
01:03:47,858 --> 01:03:52,828
image URL is nil. So if I have an image view controller as
的 image URL 是 nil。也就是说,如果 image view controller 是
1222
01:03:52,830 --> 01:03:58,166
my detail and it's, image url is nil, then it's empty.
detail,并且图片的 url 为 nil,那界面就是空的。
1223
01:03:58,168 --> 01:03:59,968
There's nothing in there. There's no Saturn or
什么内容都没有,没有 Satrun 图片或
1224
01:03:59,970 --> 01:04:03,638
anything else. So in this case I'm going to return true,
别的内容。这种情况下,就返回 true,
1225
01:04:03,640 --> 01:04:05,874
which is me telling the system, yeah,
即告知系统,
1226
01:04:05,876 --> 01:04:08,710
I took care of that. Okay, I put that detail, I collapsed
我自己处理。我自己设置 detail,自己对
1227
01:04:08,712 --> 01:04:11,079
that detail on top of the master, don't worry about it.
master 上的 detail 进行处理,你不用管。
1228
01:04:11,081 --> 01:04:13,982
Now I didn't, didn't actually do anything, that's good,
而在这里,我什么都没做,这很好,
1229
01:04:13,984 --> 01:04:16,017
cuz that's what I want. I don't want that
我就想这样。因为我并不想
1230
01:04:16,019 --> 01:04:18,753
detail to be collapsed on top of the master if the thing is
在内容是空时,master 顶部的 detail 被压缩。
1231
01:04:18,755 --> 01:04:22,357
nil. All right, so I just kind of lied to the system and told
所以,这里是在骗系统,说
1232
01:04:22,359 --> 01:04:26,328
them I handled it when in fact I didn't. Okay, otherwise,
我要自行处理,但实际上,我什么都没做。好的,除此之外,
1233
01:04:26,330 --> 01:04:29,431
we're just gonna let the system do whatever it does,
别的情况就需要系统处理了。
1234
01:04:29,433 --> 01:04:33,902
okay. Return false, we did not handle this so you handle it.
返回 false,表示我自己不处理,你来处理吧。
1235
01:04:33,904 --> 01:04:41,543
Okay, so let's see if that works. Okay, so here it is,
接下来,看看效果吧。好的,
1236
01:04:41,545 --> 01:04:46,615
look at that. It came up, didn't show me a blank thing,
你看,没有再展示空白的页面了,
1237
01:04:46,617 --> 01:04:51,319
so it worked. Now let's click on one of these. Woop, okay,
成功了。点击其中一个,
1238
01:04:51,321 --> 01:04:51,486
it worked. So
没问题。
1239
01:04:51,488 --> 01:04:54,789
it collapsed the secondary on top of the primary when I
能在我需要的时候,将 primary 顶部的 secondary
1240
01:04:54,791 --> 01:04:58,026
wanted it too, okay? But if it came up nil,
压缩。当回来为空的时候,
1241
01:04:58,028 --> 01:05:01,229
then it didn't show it. Everyone understand that? I
就不会再显示。都能听懂吗?
1242
01:05:01,231 --> 01:05:03,899
know it seems a little, a lot of long words in here [LAUGH]
我知道这个方法很长 「笑」
1243
01:05:03,901 --> 01:05:06,501
like primary view controller, secondary view controller, but
又是 primary view controller,又是 secondary view controller 的,
1244
01:05:06,503 --> 01:05:09,871
this is just master in detail, that's all this is. And
其实就是 master 和 detail,没什么奇怪的。
1245
01:05:09,873 --> 01:05:12,040
we're just checking where the image URL's nil and just
而这里是检查 image 的 URL 是否为空,然后
1246
01:05:12,042 --> 01:05:14,809
returning that, we handled it when we really didn't handle
返回这个,理论上需要我们处理,但实际没有,
1247
01:05:14,811 --> 01:05:18,013
it because we don't want it handled. Okay. One other
因为这本身就不需要处理什么。另外一个问题是,
1248
01:05:18,015 --> 01:05:24,252
thing I want to show you is reusing the detail in iPad.
在 iPad 上重用 detail 的情况。
1249
01:05:25,856 --> 01:05:31,326
Look at iPad. And we have the master detail right here.
看看 iPad。这是 master detail。
1250
01:05:31,328 --> 01:05:35,363
Every time we click on this thing, it's creating a new MVC
每次点击的时候,会创建新的 MVC。
1251
01:05:35,365 --> 01:05:39,334
here. Okay? Creating a brand new MVC. And if there,
创建一个全新的 MVC。但对此,
1252
01:05:39,336 --> 01:05:41,102
there's really no reason for that. Okay?
其实完全没必要。
1253
01:05:41,104 --> 01:05:45,373
We could absolutely just reuse this MVC right here.
完全可以对 MVC 进行重用。
1254
01:05:45,375 --> 01:05:47,842
Okay, we don't have to create a new one every time.
我们不需要每次都创建新的。
1255
01:05:47,844 --> 01:05:50,979
So, how would we do that? How would we reuse this?
那么,该怎么办呢?如何重用呢?
1256
01:05:50,981 --> 01:05:54,182
Well, if we're gonna reuse this, we can't use
如果想要重用这个,就不能用
1257
01:05:54,184 --> 01:05:57,385
segues because segues always create a new MVC.
segue,因为 segue 每次都会创建新的 MVC。
1258
01:05:57,387 --> 01:05:59,254
That's what segues do, they create a new MVC.
这就是 segue 在做的事情,创建新的 MVC。
1259
01:05:59,256 --> 01:06:02,390
So, we can't use segues, okay, to do that in that case. So,
所以,这种情况下,我们不能用 segue。那么,
1260
01:06:02,392 --> 01:06:06,761
lets go back to our Cassini right here. Our, storyboard.
回到 Cassini 项目。看到 storyboard。
1261
01:06:06,763 --> 01:06:11,800
And we're not going to use these segues over here. Okay,
我们不再使用这里的 segue。
1262
01:06:11,802 --> 01:06:14,069
see these three segues? I'm gonna get rid of them.
看到这三个 segue 了吗?我准备把它们去掉。
1263
01:06:14,071 --> 01:06:16,671
Get rid of, oops, not that. Click here,
都去掉,哦,点错了。点这个,
1264
01:06:16,673 --> 01:06:16,738
get rid of that one,
去掉它。
1265
01:06:16,740 --> 01:06:18,940
I'm gonna get rid of that one, I'm gonna get rid of that one.
去掉这个,去掉这个。
1266
01:06:18,942 --> 01:06:20,442
Cuz I cannot segue from these buttons or
我不打算将 button 与 segue 关联了,
1267
01:06:20,444 --> 01:06:24,446
it will constantly create a new one of these details.
因为它每次都会创建新的 detail。
1268
01:06:24,448 --> 01:06:26,781
Instead, what I'm gonna do is to use target action.
而我准备用 target-action 来实现。
1269
01:06:26,783 --> 01:06:30,952
So I'm just gonna bring up my code here, some space,
接着,打开 diamante,腾点空间,
1270
01:06:30,954 --> 01:06:34,289
all right, and I'm gonna do a target action message from
好的,我会给每个按钮都添加
1271
01:06:34,291 --> 01:06:38,626
each of these that reuses the detail to show the image.
target-action,用于重用显示 image 的 detial。
1272
01:06:38,628 --> 01:06:41,529
So let's do that. Let's just go Ctrl+drag here.
那么,开始吧。按住 Ctrl 并拖到这里,
1273
01:06:41,531 --> 01:06:44,332
I'm gonna call this show image, that's a good name for
名字就叫 showimage,作为事件的名字
1274
01:06:44,334 --> 01:06:47,602
this action right here, and I'm gonna have UI button
还挺合适, 事件的 sender 是
1275
01:06:47,604 --> 01:06:50,505
be the sender, cuz I'm gonna know which one to show because
UIButton,因为我需要通过按钮
1276
01:06:50,507 --> 01:06:52,640
I'm gonna look at the sender. Okay. So,
判断应该显示哪一个。Okay,
1277
01:06:52,642 --> 01:06:54,909
I got show image right here. So, let's go ahead and
showimage 方法创建好了。接下来,
1278
01:06:54,911 --> 01:06:56,978
put the code here. So, show image is a lot like,
就是填充代码了。showimage 的处理,
1279
01:06:56,980 --> 01:07:00,448
looks a lot like prepare for seg, segue. We can only do
和 prepareforsegue 中的代码类似。只能在
1280
01:07:00,450 --> 01:07:04,119
this if we're in a split view. Right, we can only do the show
split view 中这样做,这样来
1281
01:07:04,121 --> 01:07:07,555
image thing here, this trick, if we are in a split view,
显示 image,如果在 split view 中,
1282
01:07:07,557 --> 01:07:11,192
sorry, let me, just so we can see what's going on here.
啊,不好意思,这里可以看到。
1283
01:07:11,194 --> 01:07:14,129
All right. Okay, we can only do a split view so
因为只能在 split view 中使用,
1284
01:07:14,131 --> 01:07:17,098
I'm gonna first check to see that. I'm gonna say,
所以首先判断,
1285
01:07:17,100 --> 01:07:21,836
if I can let the ivc = splitViewControllers,
if let ivc = splitViewController,
1286
01:07:21,838 --> 01:07:24,773
if I have one, view Controller,
如果存在,viewcontrollers,
1287
01:07:24,775 --> 01:07:29,110
controller[1]. We know that's the detail, right.
controller[1],我们知道这个是 detail,
1288
01:07:29,112 --> 01:07:30,478
And actually I don't wanna use sub one
但这里我不想用下标取值,
1289
01:07:30,480 --> 01:07:33,248
because if for some reason my split view controller
如果某些情况下,split view controller
1290
01:07:33,250 --> 01:07:36,217
doesn't have a detail, then view controllers only have one
没有 detail,那么 viewcontrollers 就只有
1291
01:07:36,219 --> 01:07:39,721
thing so I'm actually gonna use ViewController.last.
一个值,所以我准备用 viewcontroller.last 取值。
1292
01:07:39,723 --> 01:07:43,258
Okay, because last returns nil if there's no such thing.
如果 last 返回空,则说明什么都没有。
1293
01:07:43,260 --> 01:07:46,227
Okay, and then I'm gonna grab the content view controller as
接下来,判断 content view controller
1294
01:07:46,229 --> 01:07:49,264
always and I'm gonna check to see if that's an image view
是否是 image view controller,这和之前差不多,
1295
01:07:49,266 --> 01:07:50,765
controller. So in other words,
也就是说,
1296
01:07:50,767 --> 01:07:53,134
if I can get my detail as an image view controller,
如果 detail 是 image view controller,
1297
01:07:53,136 --> 01:07:56,271
then I'm basically gonna do this right here,
就可以走这段逻辑,拷贝过来,
1298
01:07:56,273 --> 01:07:59,507
in here, but here I don't have to say sender as UI button
这里并不需要将 sender 转换为 UIButton,
1299
01:07:59,509 --> 01:08:00,008
because the sender is
因为 sender 本来就是
1300
01:08:00,010 --> 01:08:02,177
a UI button so I can just say sender and
UIButton,而且 sender 在这里
1301
01:08:02,179 --> 01:08:06,614
it's never gonna be nil there. Okay? And do that. So see,
也不可能为空。Okay?就这样。
1302
01:08:06,616 --> 01:08:09,384
see what's going on here? So no segueing,
这里在干嘛呢?没有了 segue,
1303
01:08:09,386 --> 01:08:16,825
I'm not doing any segueing. So when I go over here, Okay,
不再需要 segue 了。继续运行,
1304
01:08:16,827 --> 01:08:20,762
and I pick something, like Cassini, or Earth, okay,
点击按钮,比如 Cassini,或者 Earch,
1305
01:08:20,764 --> 01:08:25,767
we'll say. It loads it up. Actually,
就可以看到,正在加载。实际上,
1306
01:08:25,769 --> 01:08:28,236
we got to do one other thing, sorry. Back here, in our
我们忘了点东西,不好意思,回到程序,
1307
01:08:28,238 --> 01:08:31,673
storyboard, let's wire up all these buttons to do that.
在 storyboard 中,应该给所有 button 添加事件。
1308
01:08:31,675 --> 01:08:35,977
[LAUGH] Okay, we only wired up earth to do this show image so
[笑] Okay,现在只是给 earth 关联了 showimage,所以,
1309
01:08:35,979 --> 01:08:39,080
let's do that. Let's go to automatic.
来处理其余的,选择 automatic。
1310
01:08:39,583 --> 01:08:40,415
All right, so let's control,
关联这个,
1311
01:08:40,417 --> 01:08:42,750
so this one here and let's wire up this one here. Okay,
关联这个。Okay,
1312
01:08:42,752 --> 01:08:46,354
so now all three buttons are sending that same show image.
现在三个按钮都和同一个 showimage 关联了。
1313
01:08:46,356 --> 01:08:49,057
So now we can actually demonstrate this. So let's
这次演示应该没问题。点击
1314
01:08:49,059 --> 01:08:53,094
do Cassini. Here it is showing this and once this appears, or
Cassini,会在这里展示,在展示出来后,
1315
01:08:53,096 --> 01:08:55,930
even before it appeared, I could hit Earth and
或者还没展示的时候,就可以点击 Earth,
1316
01:08:55,932 --> 01:08:59,000
it just reusing this. No seuges, so there's no way I'm
而且这是重用的。没有 segue,所以不会
1317
01:08:59,002 --> 01:09:02,837
creating a new MVC here. It's just reloading it, okay,
创建新的 MVC。只是在进行刷新,
1318
01:09:02,839 --> 01:09:07,842
just by setting the image URL on the image there. Got it?
只是重新设置了 image 的 URL。明白?
1319
01:09:08,178 --> 01:09:11,813
Okay. Now, what about back on iPhone, though?
那在 iPhone 上会怎样呢?
1320
01:09:11,815 --> 01:09:13,815
Let's go back to iPhone.
让我们回到 iphone。
1321
01:09:13,817 --> 01:09:18,286
Is this going to work over here? No, it's not
依然会有效吗?不,不行,
1322
01:09:18,288 --> 01:09:21,422
because it only does that thing if it's in split view.
因为这只能在 split view 的情况下可以。
1323
01:09:21,424 --> 01:09:24,759
Remember I said if split view controller, so if I hit Earth,
我记得我说过要 split view controller,所以这里点击 Earth,
1324
01:09:24,761 --> 01:09:28,163
it's not working at all now, cuz it's not segueing here. So
没有任何响应,因为根本没有 segue。
1325
01:09:28,165 --> 01:09:31,699
really what I need to do back here in Cassini is I need to
所以,回到 Cassini 项目,需要加上
1326
01:09:31,701 --> 01:09:35,904
say [COUGH] if I'm in a split view controller, then do this.
[咳嗽],如果是 split view controller,处理这个。
1327
01:09:35,906 --> 01:09:39,107
Otherwise, I basically want to segue. Okay, so
否则,应该处理 segue 逻辑。
1328
01:09:39,109 --> 01:09:44,078
now I'm gonna show you how to segue from code. Okay,
所以我会演示如何用代码实现 segue。
1329
01:09:44,080 --> 01:09:46,147
you've only learned how to segue from,
因为之前只讲过从,
1330
01:09:46,149 --> 01:09:46,981
by dragging in Storyboard.
从 storyboard 中拖拽。
1331
01:09:46,983 --> 01:09:50,018
And now I'm gonna show you how to do it in code,
现在我会将如何使用代码,
1332
01:09:50,020 --> 01:09:53,788
which is you just say performSegueWithIdentifier.
只需要调用 performSegueWithIdentifier。
1333
01:09:53,790 --> 01:09:55,490
And you give it the identifier. So
给一个 identifier。
1334
01:09:55,492 --> 01:10:00,094
we'll use the Storyboard ShowImageSegue,
可以使用 StoryBoard.ShowImageSegue,
1335
01:10:00,096 --> 01:10:01,930
and the sender can be whatever you want,
sender 这个可以随便填,
1336
01:10:01,932 --> 01:10:05,200
I'm gonna have the sender be this sender button right here,
这里我希望 sender 就是按钮事件的 sender,
1337
01:10:05,202 --> 01:10:06,868
this button that's asking us to show image, so
就是这样按钮触发的 showimage,
1338
01:10:06,870 --> 01:10:09,137
it's gonna be one of those Earth things or whatever.
可能是 Earth,也可能是别的。
1339
01:10:09,139 --> 01:10:11,839
Now this, perform segue with identifier,
performSegueWithIdentifier 方法,
1340
01:10:11,841 --> 01:10:16,477
requires that such a segue exist in the storyboard. Okay,
要求传入的 segue 必须在 storyboard 中存在。
1341
01:10:16,479 --> 01:10:17,545
and we got rid of all those segues.
但我们之前删掉了所有 segue。
1342
01:10:17,547 --> 01:10:20,915
So that's not gonna work. We need to have a storyboard with
所以代码不会生效。我们需要 storyboard 中,
1343
01:10:20,917 --> 01:10:24,252
this identifier, and the way you do this is, you actually
有这些 identifier,实现方式是,
1344
01:10:24,254 --> 01:10:27,622
segue from the view controller itself. Instead of seguing
从 view controller 自己发起 segue,而不是
1345
01:10:27,624 --> 01:10:30,792
from one of the buttons, when you wanna segue in code,
从 button 发起 segue。如果需要用代码实现 segue,
1346
01:10:30,794 --> 01:10:32,527
you segue from the view controller itself,
那就需要 view controller 发起,
1347
01:10:32,529 --> 01:10:35,930
from this little icon right here down to wherever you want
从这个小图标拖到需要创建的
1348
01:10:35,932 --> 01:10:39,234
to segue to. So we want to segue to this, right here,
segue 上。我们需要 segue 这个,
1349
01:10:39,236 --> 01:10:42,537
put these close to each other. Okay, so I'm just gonna Ctrl +
把它们放到一起。Okay,按住 Ctrl
1350
01:10:42,539 --> 01:10:47,242
drag from here to here. It's gonna be, it can be show,
拖到,从这里到这里。选择 show,
1351
01:10:47,244 --> 01:10:50,178
because we're never doing this in a split view controller cuz
因为这个不会对 split view controller 中处理,
1352
01:10:50,180 --> 01:10:52,313
split view controller we're doing the other thing. So
split view controller 会有其他的处理逻辑。所以,
1353
01:10:52,315 --> 01:10:55,750
we do show. We've got this thing here now, this one
这里选择 show。这就完成了,这就是一个
1354
01:10:55,752 --> 01:10:59,387
segue, this segue sets up the whole view controller to here.
segue,这个 segue 包含了整个 view controller。
1355
01:10:59,389 --> 01:11:03,992
We still need to set its identifier to ShowImage. Okay?
我们还需要将 identifier 设为 ShowImage。Okay?
1356
01:11:03,994 --> 01:11:07,996
And now, back here in this code, when we do perform segue
现在,回到代码中,当 preform segue
1357
01:11:07,998 --> 01:11:11,399
with identifier, it's going to do that segue. And
with identifier 时,就会处理 segue。
1358
01:11:11,401 --> 01:11:13,368
we're seguing from the view controller to the other one,
segue 就会从一个 view controller 跳转另一个,
1359
01:11:13,370 --> 01:11:15,470
not with the buttons anymore, from the view controller, so
而不是从按钮,是从 view controller,
1360
01:11:15,472 --> 01:11:22,977
let's see if that works now on iPhone All right, Cassini.
来看看 Cassini 在 iPhone 上能成功吗。
1361
01:11:22,979 --> 01:11:24,279
Sure enough it's working. Okay, so
当然没问题。Okay,
1362
01:11:24,281 --> 01:11:26,481
it's doing that segue, it's back to doing the segues.
能跳转过去,也能回来。
1363
01:11:26,483 --> 01:11:29,417
So this is a new MVC. Go back. This is a new MVC.
这是一个新的 MVC,返回,这又是一个新的 MVC。
1364
01:11:29,419 --> 01:11:33,054
But it's only doing it on iPhone because if you look at
但这只在 iPhone 上是这样,如果在代码的
1365
01:11:33,056 --> 01:11:35,089
our code over here in the showImage,
showImage 方法中,
1366
01:11:35,091 --> 01:11:38,159
if it's in a split view controller, it just reuses
我们判断了如果是 split view controller,而且
1367
01:11:38,161 --> 01:11:41,863
the image view controller that's already in the split,
image view controller 是 split 的 detail 时,
1368
01:11:41,865 --> 01:11:46,234
in the detail. Okay? Got all that? All right,
才进行重用。明白吗?好的。
1369
01:11:46,236 --> 01:11:48,670
I think I have time to do the last bit of slides here,
应该还有时间讲最后一部分讲义,
1370
01:11:48,672 --> 01:11:54,242
oops, which is on text field, go through it pretty quickly.
oops,是关于 text field 的,很快过一遍。
1371
01:11:54,911 --> 01:11:56,878
All right, so UITextField. I haven't been able to show
好的,UITextField。我现在还没办法展示,
1372
01:11:56,880 --> 01:11:59,714
it to you right now because it requires delegation to work.
因为它需要 delegation。
1373
01:11:59,716 --> 01:12:01,683
Without delegation, text fields don't work. So
没有 delegation,text field 就没反应。
1374
01:12:01,685 --> 01:12:04,585
UITextField is like UILabel but it's editable.
UITextField 和 UILabel 差不多,但它可编辑。
1375
01:12:04,587 --> 01:12:08,089
Users can touch on it and then a keyboard comes up and
用户点击它,键盘就会弹出,
1376
01:12:08,091 --> 01:12:09,424
they can start typing in it.
然后可以进行输入。
1377
01:12:09,426 --> 01:12:11,926
UITextField is not really a very main stream
UITextField 的输入方式
1378
01:12:11,928 --> 01:12:15,530
input mechanism on the iPhone because the keyboard is very
在 iPhone 上并不主流,因为键盘实在
1379
01:12:15,532 --> 01:12:18,700
small, okay? So, you only want to use UITextField when you
太小了,所以除非必要要用户
1380
01:12:18,702 --> 01:12:21,836
absolutely can't get the text from the user in any other
用键盘,否则不要使用这种方式。
1381
01:12:21,838 --> 01:12:23,705
way. You can't offer them a choice
别让他们从清单
1382
01:12:23,707 --> 01:12:26,708
from a list of the things, they have to actually type it
中选择,这也是需要输入的。
1383
01:12:26,710 --> 01:12:29,477
in, okay? So, be careful, this is not a desktop app that you
所以,要时刻谨记,你开发的不是桌面应用,
1384
01:12:29,479 --> 01:12:33,214
are building. It's a mobile app. All right so,
而是手机应用。所以说,
1385
01:12:33,216 --> 01:12:35,416
TextField is not a primary input source. Now,
textfield 不是主要的输入源。
1386
01:12:35,418 --> 01:12:39,354
the keyboard appears whenever a TextField becomes
如果 textfild 是第一响应者,
1387
01:12:39,356 --> 01:12:41,989
the first responder, and you can make a TextField the first
键盘就会弹出,要让 textfield 成为第一响应者,可调用
1388
01:12:41,991 --> 01:12:44,792
responder by sending it the message, becomeFirstResponder.
becomeFirstResponder 方法。
1389
01:12:44,794 --> 01:12:46,761
And then it will become the first responder.
然后它就成为第一响应者了。
1390
01:12:46,763 --> 01:12:48,529
You can make it stop being the first responder,
可通过调用 resignFirstResponder,
1391
01:12:48,531 --> 01:12:51,299
in which case the keyboard will go away, by saying,
来取消是第一响应者,如此,
1392
01:12:51,301 --> 01:12:54,202
resignFirstResponder, okay?
键盘就会消失,okay?
1393
01:12:54,204 --> 01:12:54,669
So, the keyboard appears and
所以,键盘的弹出与
1394
01:12:54,671 --> 01:12:57,905
disappears purely if there's something on the screen
消失,是有屏幕上是否有第一响应者
1395
01:12:57,907 --> 01:13:00,641
that wants to be the first responder. Okay, simple as
而决定的。就这样简单。
1396
01:13:00,643 --> 01:13:06,981
that. Now, delegation is used here primarily with the return
delegation 在这里可以用于处理返回按钮的事件。
1397
01:13:06,983 --> 01:13:09,951
key. So, if you bring up that little software keyboard
如果你打开键盘,在它角落里的
1398
01:13:09,953 --> 01:13:11,919
in the corner, there's a button called Return.
那个按钮,就叫做返回按钮。
1399
01:13:11,921 --> 01:13:15,256
And if you click return the TextField's delegate will
如果点击返回按钮,textfield 的 delegate 方法
1400
01:13:15,258 --> 01:13:17,892
be sent this message textFieldShouldReturn. It's
textFieldShouldReturn 就会被调用。之所以
1401
01:13:17,894 --> 01:13:20,762
called ShouldReturn because it returns a Bool about whether
叫做 ShouldReturn,是因为需要返回一个 bool 值,
1402
01:13:20,764 --> 01:13:23,631
it should do target/action when that return happens.
用于告知是否应该响应按钮的 target/action。
1403
01:13:23,633 --> 01:13:26,534
Because a TextField is like a button, if someone types in
因为 textfield 就像是按钮,如果有键入值,
1404
01:13:26,536 --> 01:13:29,904
there and hits Return, it'll do target/action, okay?
并且点击了返回,就会触发 target/action。
1405
01:13:29,906 --> 01:13:30,405
You can control drag and
你可以通过按住 control 并拖拽
1406
01:13:30,407 --> 01:13:32,407
it'll target/action just like a button. But
来创建 target/action,这和 button 差不多。
1407
01:13:32,409 --> 01:13:35,476
this text field should return, either has to return true or
对于 textfield 的 should return 来说,返回 true,
1408
01:13:35,478 --> 01:13:37,278
it has to not be implemented by the delegate for
或者不实现这个代理方法,
1409
01:13:37,280 --> 01:13:41,182
that to work. But one thing, so usually do implement this,
都是一样的效果。不过通常这个方法都是实现了的,
1410
01:13:41,184 --> 01:13:43,818
because one thing you wanna do in here is resign first
因为需要在这个方法中,取消第一响应者。
1411
01:13:43,820 --> 01:13:47,155
responder. A lot of times when the user hits return,
绝大多数时候,在用户点击返回时,
1412
01:13:47,157 --> 01:13:48,656
you do the target/action, but
都会相应 target/action,
1413
01:13:48,658 --> 01:13:51,259
you want the keyboard to go away. So, a lot of times
需要隐藏键盘。所以,一般情况下,
1414
01:13:51,261 --> 01:13:52,660
you'll do that here and TextField should return. So,
都会在 should return 里面处理。
1415
01:13:52,662 --> 01:13:55,663
you just need to set yourself as the UITextField delegate,
只需要将自己设置 UITextField 的代理,
1416
01:13:55,665 --> 01:13:58,466
you've got to save it to the UITextField delegate, and
声明自己是 UITextField 的协议,并且
1417
01:13:58,468 --> 01:14:01,369
then you implement this method and inside just say sender,
实现该方法,然后方法中的 sender
1418
01:14:01,371 --> 01:14:03,805
which is the text field that's sending this to you,
就是 text field,
1419
01:14:03,807 --> 01:14:08,342
resign first responder. You can also find out when editing
resign first responder。除此之外,也有其他地方需要
1420
01:14:08,344 --> 01:14:11,446
has ended in your TextField, meaning that the user
结束编辑,比如用户在
1421
01:14:11,448 --> 01:14:14,682
clicked on another TextField usually and you've resigned
点击另外一个 textfield 时,通常也需要取消当前的
1422
01:14:14,684 --> 01:14:17,285
first responder. Okay, any time you resign first
第一响应者。在取消第一响应者时,
1423
01:14:17,287 --> 01:14:19,787
responder you'll get sent this and you can ask the TextField,
就会调用这个方法,可以在方法中获取 textfield
1424
01:14:19,789 --> 01:14:23,758
ok, what text is in you. Okay, so that's another interesting
的 text 是什么。这也是有趣的方法。
1425
01:14:23,760 --> 01:14:26,360
one. And as I said, TextField's UIControl,
正如我所说,textfield 是 UIControl,
1426
01:14:26,362 --> 01:14:28,529
you can do target/action, just control, drag.
你可以添加 target/action,只需要 control 并拖拽。
1427
01:14:28,531 --> 01:14:32,600
The keyboard, you configure the keyboard actually by
对于键盘来说,其实配置键盘是和
1428
01:14:32,602 --> 01:14:33,401
talking to the TextField.
textfield 打交道的。
1429
01:14:33,403 --> 01:14:37,905
There's no UI keyboard object that you talk to in iOS, okay?
iOS 中并没有 UIKeyboard 这样的对象,okay?
1430
01:14:37,907 --> 01:14:39,407
When you wanna configure your keyboard,
要配置键盘时,
1431
01:14:39,409 --> 01:14:42,343
you talk to the thing that's bringing the keyboard up.
就是在和唤起键盘的那个对象打交道。
1432
01:14:42,345 --> 01:14:43,411
In this case, the TextField. And
比如,textfield。
1433
01:14:43,413 --> 01:14:45,913
there's all kinds of methods in here you could look at.
大家可以看到,这里有一堆方法。
1434
01:14:45,915 --> 01:14:48,950
This is the UITextInputTraits protocol, so
这些都定义在 UITextInputTraits 协议中,
1435
01:14:48,952 --> 01:14:51,085
that's where you wanna look in the documentation, for
如果要在文档中查看,就要查
1436
01:14:51,087 --> 01:14:51,519
this protocol. Remember,
这个协议。记住,
1437
01:14:51,521 --> 01:14:54,088
a protocol is just a bunch of methods and bars, that's all
协议就是一堆方法的定义,
1438
01:14:54,090 --> 01:14:57,825
these are. And these will set what kind of keyboard it is,
这就是协议。而这些就决定了键盘的属性,
1439
01:14:57,827 --> 01:15:00,895
you're entering a URL? Is it a password thing,
如果在输入 URL?如果在输入密码,
1440
01:15:00,897 --> 01:15:04,999
where you can't see the text? All that stuff Is part of that
那就是不可见的文本。这些都是这个协议的
1441
01:15:05,001 --> 01:15:07,702
protocol, so you wanna look that up. One thing about
一部分,所以你应该好好看看。关于键盘,
1442
01:15:07,704 --> 01:15:11,806
the keyboard, it comes up over your UI so it might block.
还有一个问题,它弹起是,可能会挡住界面。
1443
01:15:11,808 --> 01:15:14,141
It might even block the TextField that you're
如果没小心处理,也有可能正在输入时,
1444
01:15:14,143 --> 01:15:16,744
editing in, if you're not careful about how you layer UI
界面就被挡住了。
1445
01:15:16,746 --> 01:15:19,680
out. And we are gonna talk later into the quarter about
这个会在之后的
1446
01:15:19,682 --> 01:15:23,017
this NSNotification center. It's a way to get notified
NSNotificationCenter 中谈到。这是得知这类事件的渠道。
1447
01:15:23,019 --> 01:15:25,920
when things happened and when a keyboard comes up and
当键盘弹起,可能挡住界面时,
1448
01:15:25,922 --> 01:15:28,789
blocks your UI, you will get notified, okay?
可以收到通知,
1449
01:15:28,791 --> 01:15:29,423
And when we talk about this,
在我们具体介绍的时候,
1450
01:15:29,425 --> 01:15:32,593
you'll understand what you can do. You could move your UI
你就知道该怎么做了。你可以把界面
1451
01:15:32,595 --> 01:15:35,129
up from out underneath it, or maybe it's in scroll view,
移上去,如果是 scroll view,
1452
01:15:35,131 --> 01:15:37,265
you could scroll up or something like that, but
可以滚上去之类的,
1453
01:15:37,267 --> 01:15:38,966
you have to be careful about this. They keyboard,
但处理的时候要小心。键盘真的是
1454
01:15:38,968 --> 01:15:40,835
just boom, comes up right on top of your UI and
一下子就弹起来了,盖在界面上,
1455
01:15:40,837 --> 01:15:42,803
it can really block things, okay? So,
还挡住界面。
1456
01:15:42,805 --> 01:15:45,940
you'll need to eventually learn how to respond to this
所以你必须要学会如何接收这个
1457
01:15:45,942 --> 01:15:49,443
notification. There's lots of other TextField properties
通知。这些是 textfield 的另外一些属性,
1458
01:15:49,445 --> 01:15:53,447
that are interesting here. You can actually put a little
也挺有趣的。这个可以给它
1459
01:15:53,449 --> 01:15:55,683
button on the side. You can decide where,
在边上加上小按钮,位置由你定,
1460
01:15:55,685 --> 01:15:58,920
when the user touches it, does it clear out what's in there?
当用户点击这个按钮时,可以清空输入。
1461
01:15:58,922 --> 01:16:00,855
You can have a place holder in there, so
你也可以加 place holder,
1462
01:16:00,857 --> 01:16:02,189
that when there's nothing in the field,
在 textfield 上什么都没有时,
1463
01:16:02,191 --> 01:16:03,457
there's a kind of a light gray text.
可以展示一行浅灰色的文本。
1464
01:16:03,459 --> 01:16:05,626
Kind of telling the user what's supposed to be there,
比如告诉用户这文本框支持什么,
1465
01:16:05,628 --> 01:16:06,994
that kind of thing.
之类的。
1466
01:16:06,996 --> 01:16:09,430
Anytime I show you anything in this class,
每次我给大家展示类的信息是,
1467
01:16:09,432 --> 01:16:12,066
like UITextField or UIButton or anything, of course I'm
比如 UITextField,UIButton 这类,都是
1468
01:16:12,068 --> 01:16:15,069
expecting you to go read the documentation, okay. Otherwise
希望大家能去查阅文档。否则,
1469
01:16:15,071 --> 01:16:17,271
you're just not gonna know how to use these things.
你无法知道这些该怎么使用。
1470
01:16:17,273 --> 01:16:18,306
I only have a minute or
课堂上,我只有
1471
01:16:18,308 --> 01:16:20,007
two in these classes, in the lectures to
几分钟时间,只够
1472
01:16:20,009 --> 01:16:22,610
kind of tell you these exist. If you need to be able to go,
告诉大家里面有什么。如果要弄清楚,
1473
01:16:22,612 --> 01:16:24,345
use the documentation to figure them out, okay?
还是需要从文档中学习。
1474
01:16:24,347 --> 01:16:27,148
And this is a classic example figuring out how to
这是学习 textfield 最好的
1475
01:16:27,150 --> 01:16:30,184
use TextField here, Okay? Yeah, they have these left and
方式了。这里的 left 和
1476
01:16:30,186 --> 01:16:33,120
right overlays are kind of fun. You control the layout
right 覆盖视图也挺有趣,你可以通过布局
1477
01:16:33,122 --> 01:16:35,890
all this all kinds of stuff. The keyboard has
来控制它们。键盘有
1478
01:16:35,892 --> 01:16:39,093
an interesting bar in it which is sorry the input
一个 bar,哦不,一个输入协议,
1479
01:16:39,095 --> 01:16:42,263
trait protocol. Called input accessory view you can
名为 input accessory view,你可以将这个视图
1480
01:16:42,265 --> 01:16:44,131
actually put a little view on top of your keyboard.
放在键盘的顶部。
1481
01:16:44,133 --> 01:16:45,833
You've probably seen that in some apps. Right,
你可能在其他的 app 中看见过。对吧,
1482
01:16:45,835 --> 01:16:48,436
has kind of like a little view has custom stuff,
一个很小的,有些自定义控件的视图,
1483
01:16:48,438 --> 01:16:50,871
you can put your own view just by setting this bar.
你可以通过配置这个 bar 来创建自己的视图。
1484
01:16:50,873 --> 01:16:54,308
It's kinda fun. All right, so that's it. On Friday,
这倒是挺有趣的。好的,这就是全部内容。本周五,
1485
01:16:54,310 --> 01:16:56,377
we do have a section. It's on UI Testing,
我们还有一个小节,关于 UITesting,
1486
01:16:56,379 --> 01:17:00,147
which is literally being able to record the UI being
它可以详尽的记录 UI 交互,
1487
01:17:00,149 --> 01:17:02,049
interacted with and then writing code that tests,
还可以写代码进行测试,
1488
01:17:02,051 --> 01:17:03,484
to make sure it's doing what it's supposed to be doing.
以确保操作都是正确的。
1489
01:17:03,486 --> 01:17:06,921
That's really cool. A feature in xcode. And then next week,
这是 xcode 中非常棒的特性。下一周,
1490
01:17:06,923 --> 01:17:08,823
we're going to talk about Table View and
我们会讲解 tableview,
1491
01:17:08,825 --> 01:17:09,190
Core Data, okay?
还有 core data。
1492
01:17:09,192 --> 01:17:11,892
Table View is a way of showing big huge amounts of data.
table view 用于展示一系列数据,
1493
01:17:11,894 --> 01:17:15,563
And Core Data is a database to store big huge amounts of
而 core data 则是用于存储
1494
01:17:15,565 --> 01:17:17,164
data in. Assignment four.
一系列数据。关于作业,
1495
01:17:17,166 --> 01:17:18,399
Since it's a table view based assignment,
既然都是关于 table view 的作业,
1496
01:17:18,401 --> 01:17:21,869
we'll go out at the end of the next lecture, and it'll be due
所以也会在下一讲结束,也就是
1497
01:17:21,871 --> 01:17:24,772
a week later. And there are no more reading assignments.
一周以后。今天没有额外的阅读任务。
1498
01:17:24,774 --> 01:17:27,408
I can tell some of you haven't done your reading assignment
我知道一部分同学没有完成阅读任务,
1499
01:17:27,410 --> 01:17:29,777
because you don't know some of the questions I'm asking.
因为今天我提的好几个问题都没回答上来。
1500
01:17:29,779 --> 01:17:30,778
Go back and read that stuff, really.
记得之后要去看,真的,
1501
01:17:30,780 --> 01:17:34,348
it's not that much stuff, you should really understand all
这又没多少东西,而且这些知识你必须要知道,
1502
01:17:34,350 --> 01:17:36,350
these things, like the where clause,
比如闭包,
1503
01:17:36,352 --> 01:17:37,718
or you're going to be writing weird code.
否则写出的代码就很奇怪。
1504
01:17:37,720 --> 01:17:40,154
Because people say, why didn't they just use where there,
因为大家会说,你为什么不用这个,
1505
01:17:40,156 --> 01:17:40,588
why did they put another if?.
为什么这里要加上判断。
1506
01:17:40,590 --> 01:17:44,525
Okay? So, you don't want to be that guy, okay? Allright,
Okay?你不会想被这样对待的,好吗?
1507
01:17:44,527 --> 01:17:47,228
I'll see you guys next week. >> For
同学们,下周见。
1508
01:17:47,230 --> 01:17:47,261
more, please visit us at Stanford.edu
更多信息,请访问 Stanford.edu
================================================
FILE: subtitles/9. Table View.srt
================================================
1
00:00:00,001 --> 00:00:04,436
[MUSIC]
2
00:00:04,438 --> 00:00:08,507
Stanford University. >> Okay well
3
00:00:08,509 --> 00:00:13,846
welcome to lecture number nine of CS193P. This is spring of
4
00:00:13,848 --> 00:00:16,548
2016. Today, we're gonna talk all about Table View that's
5
00:00:16,550 --> 00:00:20,185
the only topic for today. It's basically a way to create,
6
00:00:20,187 --> 00:00:22,721
apps that are looking at large data sets.
7
00:00:22,723 --> 00:00:24,523
And our demo is gonna be a Twitter client, and
8
00:00:24,525 --> 00:00:26,859
of course Twitter is this humongous data set,
9
00:00:26,861 --> 00:00:28,794
bunch of tweets out there and more all the time.
10
00:00:28,796 --> 00:00:33,499
And so a Table View is a great way to, show tweets. So what
11
00:00:33,501 --> 00:00:38,270
is Table View? UITableView is a sub class of UIView, okay.
12
00:00:38,272 --> 00:00:42,374
It displays data in a big table, okay? And
13
00:00:42,376 --> 00:00:45,210
there's really two different looks to it, all right?
14
00:00:45,212 --> 00:00:48,547
There's the plain style which you see here on the left,
15
00:00:48,549 --> 00:00:51,717
which is basically just all of the things it's showing in
16
00:00:51,719 --> 00:00:55,254
a big, long straight list. And then there's group style
17
00:00:55,256 --> 00:00:58,991
on the right, which is also a bunch of rows of data, but
18
00:00:58,993 --> 00:01:04,630
grouped. A little more obviously into little groups
19
00:01:04,632 --> 00:01:08,467
and this ones in the right, okay, are usually more for
20
00:01:08,469 --> 00:01:12,571
static tables, okay, where the stuff that in there is fixed.
21
00:01:12,573 --> 00:01:16,075
It's kinda fixed in your storyboard usually even,
22
00:01:16,077 --> 00:01:18,510
whereas on the left is more for data that
23
00:01:18,512 --> 00:01:20,712
changing every changing coming like even here.
24
00:01:20,714 --> 00:01:24,450
If you know anything about NFL you'll know that this this
25
00:01:24,452 --> 00:01:28,921
list has changed dramatically since I made this little list.
26
00:01:28,923 --> 00:01:31,056
So dynamic data on the left basically and
27
00:01:31,058 --> 00:01:34,159
more static data on the right. Is not exclusively that but
28
00:01:34,161 --> 00:01:36,462
that's generally how we use these two styles,
29
00:01:36,464 --> 00:01:41,066
okay. Now let's talk about all the parts of a UITableView so
30
00:01:41,068 --> 00:01:42,201
we know the terminology, all right?
31
00:01:42,203 --> 00:01:44,837
I'm gonna show this to you in plain style, but then I'll
32
00:01:44,839 --> 00:01:47,573
show you the grouped style version of it afterwards.
33
00:01:47,575 --> 00:01:49,808
So there's a Table Header at the top.
34
00:01:49,810 --> 00:01:52,678
This is just a UIView. It can be any UIView you want.
35
00:01:52,680 --> 00:01:54,113
In our demo we're gonna have one of these,
36
00:01:54,115 --> 00:01:56,348
it's gonna be a UITextField, okay? But it can be,
37
00:01:56,350 --> 00:01:59,151
it can be your own custom view with subviews in there.
38
00:01:59,153 --> 00:02:00,552
It can be absolutely anything, and
39
00:02:00,554 --> 00:02:02,888
it always lives at the top of the header and as
40
00:02:02,890 --> 00:02:05,691
the table scrolls around it'll scroll off the top, right?
41
00:02:05,693 --> 00:02:08,727
It's at the very top of this thing you're scrolling on. And
42
00:02:08,729 --> 00:02:11,630
similarly there's a footer, also some UIView.
43
00:02:11,632 --> 00:02:12,664
Notice at the bottom I'm showing you
44
00:02:12,666 --> 00:02:16,001
a little bit of code like var tableViewFooter: UIView,
45
00:02:16,003 --> 00:02:18,337
that's basically telling you the function or
46
00:02:18,339 --> 00:02:20,572
the property where you set this thing. Okay,
47
00:02:20,574 --> 00:02:23,609
these would be functions or properties in UITableView.
48
00:02:23,611 --> 00:02:29,114
Okay? Next is a bunch of stuff that's, we grouped together,
49
00:02:29,116 --> 00:02:33,051
we call it a section, okay? So the data that's in the table
50
00:02:33,053 --> 00:02:36,388
can be sectioned off into little, pieces.
51
00:02:36,390 --> 00:02:37,489
And you'll see in the next slide
52
00:02:37,491 --> 00:02:41,260
a little more of an example of that. Each section, okay,
53
00:02:41,262 --> 00:02:45,197
has a header, this is usually a string but it could also
54
00:02:45,199 --> 00:02:48,600
be UIView if you wanted a complicated looking header,
55
00:02:48,602 --> 00:02:50,669
maybe with images or something like that. But,
56
00:02:50,671 --> 00:02:53,605
usually this is just a string. And also a section footer.
57
00:02:53,607 --> 00:02:55,607
Okay, so that's every section has a header and footer,
58
00:02:55,609 --> 00:02:59,077
header and footer, header and footer. Okay. And then inside
59
00:02:59,079 --> 00:03:02,281
the sections there's the rows. Now these roads like Row 0,
60
00:03:02,283 --> 00:03:05,417
Row 1, these two sections each have two rows,
61
00:03:05,419 --> 00:03:07,719
but there can be any number of rows and it doesn't have to be
62
00:03:07,721 --> 00:03:09,421
the same number of rows in every section.
63
00:03:09,423 --> 00:03:12,791
One section might have zero rows or five rows and
64
00:03:12,793 --> 00:03:16,595
another one might have 100 rows okay. This
65
00:03:16,597 --> 00:03:20,432
row is basically represented in the table by a UIView. It's
66
00:03:20,434 --> 00:03:25,437
actually a subclass of UIView called UITableViewCell. And
67
00:03:25,439 --> 00:03:29,875
this method right here it's gonna get called in your code,
68
00:03:29,877 --> 00:03:32,878
cellForRowAtIndexPath and it returns
69
00:03:32,880 --> 00:03:36,148
one of these UITableViewCells. Okay, this is probably
70
00:03:36,150 --> 00:03:39,184
the most important method in implementing Table View,
71
00:03:39,186 --> 00:03:40,586
is this cellForRowAtIndexPath.
72
00:03:40,588 --> 00:03:44,056
We'll be coming back to this again and again okay. So
73
00:03:44,058 --> 00:03:46,992
those are all the names of the parts of a Table View.
74
00:03:46,994 --> 00:03:49,394
So when I was talking about it you know what they are.
75
00:03:49,396 --> 00:03:50,829
So that's what it looks in plain style.
76
00:03:50,831 --> 00:03:55,234
Here's the exact same thing in group style. Okay, so
77
00:03:55,236 --> 00:03:57,736
it has the same thing, headers, cells, footers,
78
00:03:57,738 --> 00:04:01,106
it's just that it displays them a little bit differently.
79
00:04:01,208 --> 00:04:04,443
Okay. All right, sections or not? What does it look like to
80
00:04:04,445 --> 00:04:08,080
have sections or not? Now this is visible sections, so
81
00:04:08,082 --> 00:04:12,618
look here on the right you see I have some cities in Japan
82
00:04:12,620 --> 00:04:15,053
for example and this little gray bar right there.
83
00:04:15,055 --> 00:04:17,289
I don't know how well you can see that, actually,
84
00:04:17,291 --> 00:04:18,323
but there's a gray bar there,
85
00:04:18,325 --> 00:04:21,860
it says Japan, that's a section header, okay?
86
00:04:21,862 --> 00:04:23,662
So there's one section, the Japan section,
87
00:04:23,664 --> 00:04:25,998
it's got three rows. Here's a Mexico section,
88
00:04:26,000 --> 00:04:28,867
it's got three rows. Here's Italy section,
89
00:04:28,869 --> 00:04:30,869
it's got at least two rows there at the top.
90
00:04:30,871 --> 00:04:34,139
Okay, this one over here doesn't have any sections.
91
00:04:34,141 --> 00:04:36,208
Now, it actually might have sections, but
92
00:04:36,210 --> 00:04:39,278
no section headers. Okay, if you have sections but
93
00:04:39,280 --> 00:04:43,482
no section headers, it looks like you have no sections. But
94
00:04:43,484 --> 00:04:47,185
this is what we mean by sections, so just groups of
95
00:04:47,187 --> 00:04:53,925
things in the table. The type of cell, those little rows,
96
00:04:53,927 --> 00:04:56,128
there are five types of cells. Here are four of
97
00:04:56,130 --> 00:04:59,798
them that are built into the system. The subtitle has
98
00:04:59,800 --> 00:05:02,701
a piece of text and then a small little piece under it,
99
00:05:02,703 --> 00:05:05,570
okay? This is called the title and the subtitle, or
100
00:05:05,572 --> 00:05:08,740
the text and the detail text, sometimes we call it.
101
00:05:08,742 --> 00:05:12,444
The basic one, the second one has no detail text,
102
00:05:12,446 --> 00:05:15,480
only has the primary text. The right and
103
00:05:15,482 --> 00:05:18,383
left are similar to the subtitle but it puts the text
104
00:05:18,385 --> 00:05:21,219
in a little different place with a little different color.
105
00:05:21,221 --> 00:05:23,755
Okay? So you can see that the basic
106
00:05:23,757 --> 00:05:26,725
cell types that come with the system, pretty limited.
107
00:05:26,727 --> 00:05:28,727
Okay? It also can pl, display an image,
108
00:05:28,729 --> 00:05:32,831
turns out, but they're pretty limited. There's a fifth type,
109
00:05:32,833 --> 00:05:35,734
which is custom cell, which let's you build any UI
110
00:05:35,736 --> 00:05:38,470
you want inside the cell. You can drag buttons,
111
00:05:38,472 --> 00:05:40,572
text fields, images, anything you want in there. And
112
00:05:40,574 --> 00:05:44,743
we're going to talk about and demo that today. All right.
113
00:05:44,745 --> 00:05:50,082
So, how do I use a table view in my application?
114
00:05:50,084 --> 00:05:52,451
And the answer is, 99% of the time you're gonna
115
00:05:52,453 --> 00:05:56,755
use a table view controller. Okay? TableViewController,
116
00:05:56,757 --> 00:05:59,991
it's a UIViewController. A sub class of UIViewController.
117
00:05:59,993 --> 00:06:03,695
That sub class is called UITableViewController. And,
118
00:06:03,697 --> 00:06:05,364
just like when you use a UIViewController,
119
00:06:05,366 --> 00:06:07,933
you sub class it to make a class you can hook
120
00:06:07,935 --> 00:06:10,001
your outlets and actions up to. Same thing here.
121
00:06:10,003 --> 00:06:12,971
You're gonna sub class UITableViewController so
122
00:06:12,973 --> 00:06:16,675
you can wire up things if you want inside your controller.
123
00:06:16,677 --> 00:06:19,711
Okay? Now it is possible to use a UITableView without
124
00:06:19,713 --> 00:06:22,914
the UITableViewController but like I said we almost never
125
00:06:22,916 --> 00:06:26,952
do. 99% of the time we're going to use UIViewController.
126
00:06:26,954 --> 00:06:29,488
Okay, and so if you drag the UIViewController
127
00:06:29,490 --> 00:06:32,257
out into your storyboard, UITableViewController,
128
00:06:32,259 --> 00:06:34,693
sorry. If you drag UITableViewController out,
129
00:06:34,695 --> 00:06:36,628
you're gonna get this thing right here,
130
00:06:36,630 --> 00:06:38,663
that's gonna be a UITableViewController.
131
00:06:38,665 --> 00:06:41,466
And the view, remember the view property
132
00:06:41,468 --> 00:06:43,769
in a UIViewController is that top level view?
133
00:06:43,771 --> 00:06:47,072
It's going to be of type UITableView. Okay. The very
134
00:06:47,074 --> 00:06:51,309
top top level is a table view. So this, UITableViewController
135
00:06:51,311 --> 00:06:54,446
the entire view is just one big table view.
136
00:06:54,448 --> 00:06:57,783
Now it is possible to drag out your own UIViewController and
137
00:06:57,785 --> 00:07:00,085
make the table view only be a part of the view.
138
00:07:00,087 --> 00:07:03,321
Like be a subview of the main view, but again very rare
139
00:07:03,323 --> 00:07:05,557
to do that. Not even really gonna talk about doing that.
140
00:07:05,559 --> 00:07:08,527
But you could. Requires a little more work on your part.
141
00:07:08,529 --> 00:07:11,263
This is kind of a nice pre-packaged way to use
142
00:07:11,265 --> 00:07:15,801
TableView here. Okay? Of course when you sub class
143
00:07:15,803 --> 00:07:19,771
UITableViewController up here, you create your own class.
144
00:07:19,773 --> 00:07:21,840
We'll call it MyTableView Controller or
145
00:07:21,842 --> 00:07:22,307
something like that.
146
00:07:22,309 --> 00:07:24,976
You're going to want to remember to go to the identity
147
00:07:24,978 --> 00:07:28,180
inspector and set that as the type just like you would any
148
00:07:28,182 --> 00:07:31,149
other UIViewController. If you right click on
149
00:07:31,151 --> 00:07:33,885
it, okay, if you right click on the thing that represents
150
00:07:33,887 --> 00:07:36,254
the TableViewController, you'll see that, look,
151
00:07:36,256 --> 00:07:39,057
here's view, it's hooked up to a UITableview. And
152
00:07:39,059 --> 00:07:42,093
then down farther, you see dataSource and delegate.
153
00:07:42,095 --> 00:07:44,329
Those are both delegates, okay, for
154
00:07:44,331 --> 00:07:49,334
delegation. They are protocol based communication outlets,
155
00:07:49,336 --> 00:07:52,771
communication portals. From the Table View,
156
00:07:52,773 --> 00:07:55,607
to the Table View Controller. And you can see look,
157
00:07:55,609 --> 00:07:57,342
they're automatically hooked up for you. And
158
00:07:57,344 --> 00:08:00,779
what that means is that your UITableViewController subclass
159
00:08:00,781 --> 00:08:04,049
Just has to implement any and all of the data source and
160
00:08:04,051 --> 00:08:06,117
delegate methods that it wants. Okay?
161
00:08:06,119 --> 00:08:08,720
To make things work. Just like we do with scroll view, right?
162
00:08:08,722 --> 00:08:10,288
We set ourselves as a scroll view delegate.
163
00:08:10,290 --> 00:08:12,724
We implement that view for zooming in scroll view.
164
00:08:12,726 --> 00:08:15,026
Now we can zoom. Same thing with table view,
165
00:08:15,028 --> 00:08:17,996
except for we're automatically hooked up as the delegate and
166
00:08:17,998 --> 00:08:18,263
We'll talk about these, the difference between these two.
167
00:08:18,265 --> 00:08:19,865
data source.
168
00:08:19,867 --> 00:08:22,601
And there's just a bunch of methods in there like and
169
00:08:22,603 --> 00:08:25,637
index path for us to implement the table,
170
00:08:25,639 --> 00:08:29,674
all right? You can, of course, click on this table and
171
00:08:29,676 --> 00:08:33,144
inspect it in the inspector, just like anything else there.
172
00:08:33,146 --> 00:08:36,181
You can see things like, plain style and stuff. I'll show you
173
00:08:36,183 --> 00:08:39,651
some of that. Remember when you have a table view,
174
00:08:39,653 --> 00:08:43,188
you're gonna remember, wanna remember Ctrl+Shift click. If
175
00:08:43,190 --> 00:08:46,758
you remember ctrl-shift-click. It lets you, kind of, cli-,
176
00:08:46,760 --> 00:08:48,560
pick what's under the mouth,mouse,
177
00:08:48,562 --> 00:08:49,995
if there are multiple things under there.
178
00:08:49,997 --> 00:08:52,597
Mm-kay? That's really valuable here because you've got
179
00:08:52,599 --> 00:08:55,600
the cell, under that you've got the table view, under that
180
00:08:55,602 --> 00:08:57,903
you've got the controller. And if you had a custom cell,
181
00:08:57,905 --> 00:08:59,871
you might have buttons and text fields in here.
182
00:08:59,873 --> 00:09:02,607
So, kind of drilling down to which of those things you want
183
00:09:02,609 --> 00:09:08,013
to chose. Ctrl-shift-click. Okay, you want that. So
184
00:09:08,015 --> 00:09:10,849
here I'm gonna, switch the table view from being
185
00:09:10,851 --> 00:09:14,619
plain style to being grouped, okay? You can see that,
186
00:09:14,621 --> 00:09:16,555
when I switched a group, it changes a little bit and
187
00:09:16,557 --> 00:09:19,257
looks a little bit different, okay? Another thing I can
188
00:09:19,259 --> 00:09:23,628
switch is dynamic and static, okay? Now, grouped in plain,
189
00:09:23,630 --> 00:09:25,964
I told you the plain was usually dynamic data, and
190
00:09:25,966 --> 00:09:28,767
grouped is static. They don't have to be that way, okay?
191
00:09:28,769 --> 00:09:34,139
You choose the dynamic versus static with this other pop up,
192
00:09:34,141 --> 00:09:37,208
right here. But again, usually if it's grouped you're gonna
193
00:09:37,210 --> 00:09:38,143
pick static from this one, and
194
00:09:38,145 --> 00:09:40,745
if it's plain your gonna be picking dynamic. So
195
00:09:40,747 --> 00:09:43,748
I'm gonna switch to static right here. When I switch to
196
00:09:43,750 --> 00:09:48,420
static, all of this, these little rows in here,
197
00:09:48,422 --> 00:09:49,888
now they become fully editable.
198
00:09:49,890 --> 00:09:52,424
You can drag buttons out. You can drag text fields.
199
00:09:52,426 --> 00:09:55,360
Whatever. And whatever you put in your story board,
200
00:09:55,362 --> 00:09:58,196
that's what's gonna appear in your app, okay?
201
00:09:58,198 --> 00:09:59,731
However many rows there are here,
202
00:09:59,733 --> 00:10:01,700
that's how many rows are gonna be in the app.
203
00:10:01,702 --> 00:10:03,668
Whatever buttons and stuff you put in here,
204
00:10:03,670 --> 00:10:04,903
that's how many are gonna be in the app.
205
00:10:04,905 --> 00:10:08,173
You can wire up outlets in these static ones directly
206
00:10:08,175 --> 00:10:11,309
to your controller, just like if this were a view and
207
00:10:11,311 --> 00:10:13,244
you dragged a button down to it, okay? So
208
00:10:13,246 --> 00:10:16,448
think of static as really just normal view,
209
00:10:16,450 --> 00:10:19,584
but grouped, okay? Grouped in these little sections.
210
00:10:19,586 --> 00:10:21,419
Really great for things like settings like settings, like,
211
00:10:21,421 --> 00:10:25,690
you know, the settings app in iOS is just a big table view,
212
00:10:25,692 --> 00:10:28,393
a static grouped table view. So, it's really great for
213
00:10:28,395 --> 00:10:31,730
that. It's not good at all for dynamic data.
214
00:10:31,732 --> 00:10:34,199
It doesn't, grouped is not good for
215
00:10:34,201 --> 00:10:37,602
that. And if you have this set to static content,
216
00:10:37,604 --> 00:10:39,938
then you can't even do dynamic data, okay? So
217
00:10:39,940 --> 00:10:42,540
static, you edit it all in the story board. Very different
218
00:10:42,542 --> 00:10:45,944
from dynamic, okay? So let's go back to talk about dynamic,
219
00:10:45,946 --> 00:10:49,114
though, cuz that's really the more interesting one here,
220
00:10:49,116 --> 00:10:51,616
okay? We're also gonna switch back to plain style for
221
00:10:51,618 --> 00:10:56,421
dynamic. Now, these rows are prototypes, or
222
00:10:56,423 --> 00:11:00,225
templates, okay? Because I've got three rows here, but I can
223
00:11:00,227 --> 00:11:04,696
have hundreds of rows in my app when I load my data up. So
224
00:11:04,698 --> 00:11:06,698
these are just the different templates you can have.
225
00:11:06,700 --> 00:11:08,166
And you could have different kinds of templates.
226
00:11:08,168 --> 00:11:10,902
Like, in your homework, you're gonna have two different
227
00:11:10,904 --> 00:11:14,305
kinds of rows in one of your table views. One shows image,
228
00:11:14,307 --> 00:11:17,709
one's showing text. So you're gonna have one prototype for
229
00:11:17,711 --> 00:11:20,178
images, and you're gonna have another prototype for text,
230
00:11:20,180 --> 00:11:25,316
okay? And these prototypes are copied hundreds of times,
231
00:11:25,318 --> 00:11:27,986
or however many times is necessary for all the data.
232
00:11:27,988 --> 00:11:30,655
Now, it doesn't actually copy it hundreds of times, cuz
233
00:11:30,657 --> 00:11:33,958
it's really smart. It reuses them, so it only uses them for
234
00:11:33,960 --> 00:11:36,528
visible cells, okay? Table view's very efficient,
235
00:11:36,530 --> 00:11:39,798
as you can imagine. If you had 100,000 items in there,
236
00:11:39,800 --> 00:11:42,400
and you were making 100,000 UITableView cells,
237
00:11:42,402 --> 00:11:44,769
it would be pretty inefficient so it doesn't do that.
238
00:11:44,771 --> 00:11:47,772
It reuses them. But these prototypes are a template
239
00:11:47,774 --> 00:11:50,408
that get, you know, essentially copied.
240
00:11:51,645 --> 00:11:55,180
So, the cells can be inspected just like the table view.
241
00:11:55,182 --> 00:11:55,780
So, here I'm gonna change,
242
00:11:55,782 --> 00:11:58,216
I'm gonna pick this first cell, this first proto-type,
243
00:11:58,218 --> 00:11:59,451
and I'm gonna change it to be subtitle.
244
00:11:59,453 --> 00:12:01,786
And you can see it looks like this in the storyboard,
245
00:12:01,788 --> 00:12:04,122
just to remind you that it's subtitle. Of course,
246
00:12:04,124 --> 00:12:06,491
this is not gonna say subtitle and title when I launch.
247
00:12:06,493 --> 00:12:09,461
It's gonna be replaced by the data that I load into that
248
00:12:09,463 --> 00:12:12,731
cell, okay? Cuz I'm gonna have hundreds of these rows.
249
00:12:12,733 --> 00:12:16,601
All right. You can also set a little
250
00:12:16,603 --> 00:12:19,270
thing to appear on the right hand side of a row.
251
00:12:19,272 --> 00:12:22,107
For example, I picked a detailed disclosure which is
252
00:12:22,109 --> 00:12:26,811
this little greater than sign, and then this little round
253
00:12:26,813 --> 00:12:31,483
circled i. This little gray, greater than sign means that
254
00:12:31,485 --> 00:12:35,353
if I press on this row, it's gonna segue, okay? So, anytime
255
00:12:35,355 --> 00:12:37,989
you set up a segue from a row, which we're gonna talk about,
256
00:12:37,991 --> 00:12:40,925
you're always gonna wanna have your accessory be either
257
00:12:40,927 --> 00:12:42,994
detail disclosure or just disclosure.
258
00:12:42,996 --> 00:12:45,730
Detail disclosure means you have this little blue i, and
259
00:12:45,732 --> 00:12:48,366
I'll talk about what that means in a second, okay?
260
00:12:48,368 --> 00:12:51,669
So I'm gonna turn that off for now. All right.
261
00:12:51,671 --> 00:12:55,373
So, another style that you can choose is the custom style.
262
00:12:55,375 --> 00:12:59,711
So I'm gonna switch my style here to custom, okay? Now,
263
00:12:59,713 --> 00:13:02,347
this custom area, I can resize it, okay?
264
00:13:02,349 --> 00:13:07,085
Make it bigger. I can go over to my object pallet down here.
265
00:13:07,087 --> 00:13:09,654
I can pick up a label or a button or something.
266
00:13:09,656 --> 00:13:12,991
I can drag it out. I can use the blue lines.
267
00:13:12,993 --> 00:13:17,195
I can use stack views. I can control drag to the edges.
268
00:13:17,197 --> 00:13:20,732
I can build whatever UI I want in here. And remember, it took
269
00:13:20,734 --> 00:13:24,035
prototype. So every row that's gonna use this prototype
270
00:13:24,037 --> 00:13:27,739
is gonna have all those labels and buttons in each row, okay?
271
00:13:27,741 --> 00:13:33,311
So there's a, when you have this kind of mechanism,
272
00:13:33,313 --> 00:13:36,381
you might well ask yourself, well, how am I gonna hook up
273
00:13:36,383 --> 00:13:38,616
to that label? How am I gonna control drag?
274
00:13:38,618 --> 00:13:41,352
I can't really control drag an outlet to my table view
275
00:13:41,354 --> 00:13:44,656
controller because my table view controller's just one
276
00:13:44,658 --> 00:13:47,692
controller, and I've got hundreds of rows. So,
277
00:13:47,694 --> 00:13:49,227
which row is the outlet hooked to?
278
00:13:49,229 --> 00:13:53,331
So you can't hook your outlets up to dynamic prototype like,
279
00:13:53,333 --> 00:13:57,168
rows like this. You have to hook it up to outlets in
280
00:13:57,170 --> 00:14:02,807
a sub-classed UI table view cell, okay?
281
00:14:02,809 --> 00:14:05,643
So we're gonna create a custom subclass of UITableViewCell.
282
00:14:05,645 --> 00:14:08,313
And it's gonna have outlets in it. And we're gonna wire this
283
00:14:08,315 --> 00:14:11,749
to it. And we know that, for all the visible cells anyway,
284
00:14:11,751 --> 00:14:14,552
there's another TableUIViewCell that's hooked
285
00:14:14,554 --> 00:14:16,254
up. So, we're gonna have all these UITableViewCells,
286
00:14:16,256 --> 00:14:20,024
they're gonna all each have outlets to whatever is showing
287
00:14:20,026 --> 00:14:22,627
in their custom cell, okay? So
288
00:14:22,629 --> 00:14:25,063
you do this with the identity inspector in the same way.
289
00:14:25,065 --> 00:14:26,464
So you're gonna do new file, right?
290
00:14:26,466 --> 00:14:29,734
You're gonna go over to new file, and when you do new file
291
00:14:29,736 --> 00:14:33,204
you're gonna say Tableview Cell as the superview class.
292
00:14:33,206 --> 00:14:35,240
And then you're gonna go up here to the identity inspector
293
00:14:35,242 --> 00:14:38,009
with this selected, with this cell selected, and
294
00:14:38,011 --> 00:14:41,479
say that it's a my Tableview cell. And the reason you're
295
00:14:41,481 --> 00:14:43,648
gonna do that sub-class is so that you can have outlets and
296
00:14:43,650 --> 00:14:45,650
stuff. Just like the same reason you do it for
297
00:14:45,652 --> 00:14:47,418
a controller, but this is not a controller,
298
00:14:47,420 --> 00:14:49,087
this is a UI View subclass, actually.
299
00:14:49,089 --> 00:14:53,124
UI TableView cell is a UI view subclass. All right. So now
300
00:14:53,126 --> 00:14:56,294
once you have that, you can wire up all the outlets and
301
00:14:56,296 --> 00:14:57,095
actions you want, okay?
302
00:14:57,097 --> 00:14:58,263
And we're gonna do this in the demo and
303
00:14:58,265 --> 00:15:00,431
make maybe a lot more sense. So, here's an example.
304
00:15:00,433 --> 00:15:04,369
I'm creating an outlet to this label in my UI table view cell
305
00:15:04,371 --> 00:15:11,476
subclass, you see that? Okay? All right. And again,
306
00:15:11,478 --> 00:15:13,578
remember it's a prototype, it's gonna be duplicated.
307
00:15:13,580 --> 00:15:17,081
All your label, this label and all the stuff and, it's
308
00:15:17,083 --> 00:15:20,418
gonna be a different instance of this for every row, okay?
309
00:15:20,420 --> 00:15:24,122
All right. So, let's talk about those protocols that
310
00:15:24,124 --> 00:15:26,824
we're talking about dataSource and delegate, okay?
311
00:15:26,826 --> 00:15:30,295
There's two of them because the delegate controls how
312
00:15:30,297 --> 00:15:34,432
the table is displayed, okay? The dataSource controls what
313
00:15:34,434 --> 00:15:38,236
data is in the table, okay? What's in the rows, okay?
314
00:15:38,238 --> 00:15:40,705
So that's the difference between the two delegates.
315
00:15:40,707 --> 00:15:44,242
Now, the UITableViewController automatically sets itself as
316
00:15:44,244 --> 00:15:47,478
the delegate, and as the data source. So, it's all preset
317
00:15:47,480 --> 00:15:50,148
up. And as I told you, the view, the top level view,
318
00:15:50,150 --> 00:15:54,252
is a UITableView. There's also a var called TableView which,
319
00:15:54,254 --> 00:15:56,921
the computed var, that just returns the view.
320
00:15:56,923 --> 00:15:59,757
Okay, so that usually will access the table view,
321
00:15:59,759 --> 00:16:01,693
because it's typed to be UITableView,
322
00:16:01,695 --> 00:16:03,861
that's how we access our table view inside of
323
00:16:03,863 --> 00:16:07,899
our table view controller sub class code. All right.
324
00:16:07,901 --> 00:16:10,435
All right, so when do we need to implement the data source?
325
00:16:10,437 --> 00:16:14,072
Only if we're doing dynamic cells. If we're doing static,
326
00:16:14,074 --> 00:16:15,673
like, you know, the settings example, the group
327
00:16:15,675 --> 00:16:17,909
settings thing. Then you just do it in your storyboard,
328
00:16:17,911 --> 00:16:20,545
you don't need to do anything in the dataSource protocol to
329
00:16:20,547 --> 00:16:24,682
make that work. Okay? But if you're doing dynamic,
330
00:16:24,684 --> 00:16:26,751
you need to do it. And there are three very important
331
00:16:26,753 --> 00:16:28,920
methods in the dataSource protocol, there's a bunch of
332
00:16:28,922 --> 00:16:31,422
methods in there, but there's three really important ones.
333
00:16:31,424 --> 00:16:34,092
One is how many sections are in this table?
334
00:16:34,094 --> 00:16:36,461
How many rows are in each of the sections?
335
00:16:36,463 --> 00:16:37,462
It's gonna ask for every section,
336
00:16:37,464 --> 00:16:39,430
how many rows in this section? How many rows in this section?
337
00:16:39,432 --> 00:16:42,767
How many rows in this section? And then, give me a view,
338
00:16:42,769 --> 00:16:46,871
a UI table view cell subclass to draw this row, okay?
339
00:16:46,873 --> 00:16:49,207
And that's when you're gonna implement cell for
340
00:16:49,209 --> 00:16:51,476
row at index path and give it your sub class,
341
00:16:51,478 --> 00:16:52,577
an instance of your sub class. And
342
00:16:52,579 --> 00:16:56,014
I'm going to show exactly how you do that in a second here.
343
00:16:56,549 --> 00:16:56,714
to skip the two easy ones, the number of sections and rows,
344
00:16:56,716 --> 00:16:59,117
So we're going
345
00:16:59,119 --> 00:17:01,953
and go straight to cell for row at index path. Okay?
346
00:17:01,955 --> 00:17:05,156
So this method looks like this. Okay?
347
00:17:05,158 --> 00:17:09,193
I already talked about the fact that it's reusing here.
348
00:17:09,662 --> 00:17:11,796
And the method's called cell for row at index path,
349
00:17:11,798 --> 00:17:16,167
you can see that it returns a UITablViewCell, right here.
350
00:17:16,169 --> 00:17:19,837
This index path is really just the section and
351
00:17:19,839 --> 00:17:23,207
row, section and the row in that section, okay? They could
352
00:17:23,209 --> 00:17:27,478
have called this cell for row at section in row, two
353
00:17:27,480 --> 00:17:29,414
separate arguments, but they decided to call it cell for
354
00:17:29,416 --> 00:17:31,449
row and index path and they just made a thing called
355
00:17:31,451 --> 00:17:33,851
NSIndexPath that's got the section row in it, okay?
356
00:17:33,853 --> 00:17:37,789
And in fact, if you wanted to, like, get your data, you would
357
00:17:37,791 --> 00:17:41,192
just say let the data equal my internal data structure,
358
00:17:41,194 --> 00:17:44,262
whatever that is. IndexPath subsection, indexPath.row,
359
00:17:44,264 --> 00:17:46,831
that will tell your internal data structure where an array
360
00:17:46,833 --> 00:17:50,001
of arrays, where the sections were in there in the rows,
361
00:17:50,003 --> 00:17:52,036
you can just do this. So this is how you get the section,
362
00:17:52,038 --> 00:17:55,773
and this is how you get the row, okay? So
363
00:17:55,775 --> 00:17:57,075
then you have to take the data
364
00:17:57,077 --> 00:17:58,776
that corresponds to that section and row, and
365
00:17:58,778 --> 00:18:03,247
you have to load it up into a UITableViewCell. Okay, so
366
00:18:03,249 --> 00:18:05,183
let's talk about how that works. And
367
00:18:05,185 --> 00:18:08,352
while I'm doing this, I'm gonna move this code on top of
368
00:18:08,354 --> 00:18:11,622
the storyboard so that we can see the storyboard at the same
369
00:18:11,624 --> 00:18:14,959
time we're looking at the code, okay? So here's how you
370
00:18:14,961 --> 00:18:19,097
get the cell, okay? You call this table view method, okay?
371
00:18:19,099 --> 00:18:21,966
This is the table view here that's passed along, table
372
00:18:21,968 --> 00:18:26,170
view first argument. Dequeued reusable cell with identifier.
373
00:18:26,172 --> 00:18:30,208
That's the thing that's reusing these cells. Okay?
374
00:18:30,210 --> 00:18:33,144
As things go off screen, they get put back into this reuse
375
00:18:33,146 --> 00:18:35,947
queue, and when people call this they get pulled out.
376
00:18:35,949 --> 00:18:39,450
Okay? If there's not one available in the reuse queue,
377
00:18:39,452 --> 00:18:42,720
it makes one. It copies your prototype to make one.
378
00:18:42,722 --> 00:18:47,225
Okay? This says which prototype to use. Each of
379
00:18:47,227 --> 00:18:50,561
these prototypes can have a different identifier. You just
380
00:18:50,563 --> 00:18:54,599
select the row, and go into your storyboard, okay? And
381
00:18:54,601 --> 00:18:57,068
you set the identifier. Just like when you do with a segue,
382
00:18:57,070 --> 00:18:59,770
right? In a segue you set the identifier? Same thing here.
383
00:18:59,772 --> 00:19:01,839
You're just saying what the name of this is so
384
00:19:01,841 --> 00:19:04,942
that in your code, you can dequeue the kind of
385
00:19:04,944 --> 00:19:07,044
one you want. So if you have multiple prototypes
386
00:19:07,046 --> 00:19:10,448
you'll dequeue the right one here. And maybe some sections
387
00:19:10,450 --> 00:19:13,151
use one kind of thing, and some sections use a different.
388
00:19:13,153 --> 00:19:14,585
That's probably going to be in your homework, right?
389
00:19:14,587 --> 00:19:16,888
You're gonna have a section using one of the prototypes
390
00:19:16,890 --> 00:19:19,824
and a different section uses a different one, okay? Perfectly
391
00:19:19,826 --> 00:19:24,162
fine. So after you get the cell, which I call dequeued
392
00:19:24,164 --> 00:19:29,133
here by referencing that, you are going to load this cell
393
00:19:29,135 --> 00:19:33,938
up with the data from your internal data structure.
394
00:19:33,940 --> 00:19:37,141
See, data is this data right here, okay? And
395
00:19:37,143 --> 00:19:40,444
I'm just gonna put this in this title, in subtitle.
396
00:19:40,446 --> 00:19:42,647
Now you have to go look at the documentation for
397
00:19:42,649 --> 00:19:46,184
UITableVIewCell, this is not a custom cell, right?
398
00:19:46,186 --> 00:19:48,886
This is of type subtitle. So it's just a built
399
00:19:48,888 --> 00:19:51,956
in one, it only has these two things. If you'll look at
400
00:19:51,958 --> 00:19:53,424
the documentation for UITableViewCell,
401
00:19:53,426 --> 00:19:56,594
you'll see that it has a property called text label,
402
00:19:56,596 --> 00:19:59,664
which corresponds to that title thing, and detail text
403
00:19:59,666 --> 00:20:03,568
label, which corresponds to this little text label. Okay?
404
00:20:03,570 --> 00:20:07,438
So you can set the text of those things here.
405
00:20:07,440 --> 00:20:10,875
To whatever information, important information or
406
00:20:10,877 --> 00:20:13,477
less important information, from your data,
407
00:20:13,479 --> 00:20:16,847
internal data structure. Then you just return the cell and
408
00:20:16,849 --> 00:20:20,518
that's it. Okay so you dequeue the cell, load it up with
409
00:20:20,520 --> 00:20:21,452
the information at that section and
410
00:20:21,454 --> 00:20:25,189
row in your data structure, and return it. Any question
411
00:20:25,191 --> 00:20:28,626
about that? Okay, now let's talk about the custom case.
412
00:20:28,628 --> 00:20:30,661
Let's say you have a custom cell, okay? So
413
00:20:30,663 --> 00:20:33,698
now my type here is custom, I got this label, but I could
414
00:20:33,700 --> 00:20:36,601
have all kinds of buttons and stuff in here. In this
415
00:20:36,603 --> 00:20:39,971
case you do the same thing, dequeueReusableCell, okay?
416
00:20:39,973 --> 00:20:42,773
Probably has a different identifier here.
417
00:20:42,775 --> 00:20:47,445
Okay, then instead of doing that textLabel.text equals
418
00:20:47,447 --> 00:20:50,715
detailtextLabel.whatever equals, you just
419
00:20:50,717 --> 00:20:55,386
call some public API in your subclass of TableViewCell that
420
00:20:55,388 --> 00:20:58,656
passes the information to it. Then it's gonna take that and
421
00:20:58,658 --> 00:21:03,728
put it in all of its outlets. Okay? Make sense?
422
00:21:03,730 --> 00:21:06,564
So it's very very simple. Notice that I'm having to take
423
00:21:06,566 --> 00:21:11,435
the cell which gets dequeued as a UITableViewCell and
424
00:21:11,437 --> 00:21:15,506
I'm having to cast it here to be MyTableViewCell so
425
00:21:15,508 --> 00:21:19,010
I can call this public function in MyTableViewCell.
426
00:21:19,012 --> 00:21:21,779
See? Otherwise I wouldn't be able to call it because
427
00:21:21,781 --> 00:21:22,813
dequeued would be a UITableViewCell,
428
00:21:22,815 --> 00:21:30,054
not a subclass of it. Everybody got that? Okay.
429
00:21:30,056 --> 00:21:31,856
The UITableViewlDataSource that cell for
430
00:21:31,858 --> 00:21:34,558
row at index path is the most important thing in it.
431
00:21:34,560 --> 00:21:37,762
The other two here, number of sections in TableView and
432
00:21:37,764 --> 00:21:41,966
TableView's number of rows and section are the other two.
433
00:21:41,968 --> 00:21:44,835
The number of sections by default is one. So if
434
00:21:44,837 --> 00:21:47,972
you don't implement number of sections in table view at all,
435
00:21:47,974 --> 00:21:50,308
you just don't even implement it, it'll be one,
436
00:21:50,310 --> 00:21:54,478
okay? But the other one has to be implemented because
437
00:21:54,480 --> 00:21:57,148
the TableView has to know how many rows there are.
438
00:21:57,150 --> 00:21:59,317
TableView cannot survive unless it knows how many rows
439
00:21:59,319 --> 00:22:02,420
there are. So you have to implement this number of rows
440
00:22:02,422 --> 00:22:04,722
in section right here. And it's simple, it's just gives
441
00:22:04,724 --> 00:22:06,691
you the section. You return how many rows are in that
442
00:22:06,693 --> 00:22:09,627
section, okay? And it's gonna call that repeatedly, once for
443
00:22:09,629 --> 00:22:12,897
every section. Okay, what about in a static table?
444
00:22:12,899 --> 00:22:15,199
You don't have to do any of this, okay? Static table,
445
00:22:15,201 --> 00:22:19,103
it's fixed in your storyboard. All right? So that's how our
446
00:22:19,105 --> 00:22:22,940
table dataSource works. The section titles like you
447
00:22:22,942 --> 00:22:24,742
remember I showed you the section had Japan and
448
00:22:24,744 --> 00:22:27,478
the Japan things, and then Mexico and the Mexico cities?
449
00:22:27,480 --> 00:22:30,247
Well those titles like Japan, those are considered part of
450
00:22:30,249 --> 00:22:33,451
the data, as you might imagine. So there's another
451
00:22:33,453 --> 00:22:36,153
TableView dataSource method called TableView,
452
00:22:36,155 --> 00:22:39,090
titleForHeader or FooterInSection. Okay?
453
00:22:39,092 --> 00:22:40,991
You give it this section and you return to string.
454
00:22:40,993 --> 00:22:44,528
Or there's another one that you can return a view,
455
00:22:44,530 --> 00:22:45,796
a UI view, okay? So
456
00:22:45,798 --> 00:22:49,133
that's how you set the headers of your tables. There's
457
00:22:49,135 --> 00:22:51,602
a number of other methods in here that have to do with
458
00:22:51,604 --> 00:22:54,238
editing and moving rows because when you're deleting
459
00:22:54,240 --> 00:22:56,140
rows you're affecting your data source, right?
460
00:22:56,142 --> 00:22:57,508
Your deleting the data out of your data source.
461
00:22:57,510 --> 00:23:00,144
So those can get involved, I'm not going to talk about those.
462
00:23:00,146 --> 00:23:03,013
But this is the kind of thing you probably want to do either
463
00:23:03,015 --> 00:23:06,584
for the extra credit or in your final project.
464
00:23:06,586 --> 00:23:08,319
You're almost certainly going to want to have a table.
465
00:23:08,321 --> 00:23:10,087
At least one where you can delete the rows or
466
00:23:10,089 --> 00:23:12,423
move them around. Or something, or insert rows.
467
00:23:12,425 --> 00:23:17,628
Etc. Okay? Now, what about segueing from TableView cells?
468
00:23:17,630 --> 00:23:20,898
How do you do that? Exactly as you might imagine [LAUGH], you
469
00:23:20,900 --> 00:23:24,168
just control drag from them to the MVC you want to segue to,
470
00:23:24,170 --> 00:23:27,138
and when you do you're gonna get to choose what kind of
471
00:23:27,140 --> 00:23:28,906
segue you want, show or show detail,
472
00:23:28,908 --> 00:23:31,976
or eventually we'll talk about these other kinds of segues.
473
00:23:31,978 --> 00:23:36,414
Okay, if you have that little blue i, told you I'd get back
474
00:23:36,416 --> 00:23:40,584
to that, here it is, you can control+drag from it, over,
475
00:23:40,586 --> 00:23:44,321
and when you choose your segue, you choose it from this
476
00:23:44,323 --> 00:23:48,058
part that says Accessory Action down here. And when you
477
00:23:48,060 --> 00:23:51,328
have a little i here, if you click on the row it will do
478
00:23:51,330 --> 00:23:55,733
whatever segue is specified by the top selection segue thing.
479
00:23:55,735 --> 00:23:57,902
And if you Click just on the blue i,
480
00:23:57,904 --> 00:24:02,072
you'll get whatever segue is hooked up here, okay? So
481
00:24:02,074 --> 00:24:04,108
you can basically have a row that has two different segues,
482
00:24:04,110 --> 00:24:07,545
one if you click on the i and one if you click on the row.
483
00:24:08,414 --> 00:24:11,882
All right, you set your segue identifier as usual,
484
00:24:11,884 --> 00:24:12,850
nothing special there. But
485
00:24:12,852 --> 00:24:15,953
to prepareForSegue obviously is gonna be a little different
486
00:24:15,955 --> 00:24:17,087
when you're segueing from a row. So
487
00:24:17,089 --> 00:24:19,657
let's look at prepareForSegue. And the difference is all
488
00:24:19,659 --> 00:24:22,793
about who the sender is. Normally, the sender in
489
00:24:22,795 --> 00:24:24,528
prepareForSegue is like the UI button you clicked
490
00:24:24,530 --> 00:24:26,430
on that caused the segue to happen. Well, here,
491
00:24:26,432 --> 00:24:30,367
of course, the sender is the UITableViewCell you clicked
492
00:24:30,369 --> 00:24:34,138
on, okay. So you're gonna have to get that UITableViewCell,
493
00:24:34,140 --> 00:24:36,941
and since this is any object, you're gonna have to convert
494
00:24:36,943 --> 00:24:39,376
it to either MyTableViewCell if it's custom,
495
00:24:39,378 --> 00:24:42,680
or UITableViewCell if it's not custom. Then you're gonna call
496
00:24:42,682 --> 00:24:47,151
this very important method in tableView.indexPathforCell,
497
00:24:47,153 --> 00:24:49,620
okay. It's gonna give you an NSIndexPath that's
498
00:24:49,622 --> 00:24:52,990
basically gonna tell you which row in which section
499
00:24:52,992 --> 00:24:55,593
you're segueing from. Cuz if you're preparing,
500
00:24:55,595 --> 00:24:57,828
you gotta know which row it came from so you know
501
00:24:57,830 --> 00:25:00,531
how to prepare with the guy you're segueing to. Okay, so
502
00:25:00,533 --> 00:25:04,368
then you just take, you get your destinationViewController
503
00:25:04,370 --> 00:25:04,435
Okay, this is just like in any other, prepareForSegue.
504
00:25:04,437 --> 00:25:07,371
as usual.
505
00:25:07,373 --> 00:25:11,008
And then you get your data out of your model, okay,
506
00:25:11,010 --> 00:25:14,311
in your TableViewController, in that section and row. Okay,
507
00:25:14,313 --> 00:25:16,580
however, you might have to call a function to do that or
508
00:25:16,582 --> 00:25:19,416
however you do it, and then you just assign that
509
00:25:19,418 --> 00:25:22,219
to the public API of the thing you are segueing to just like
510
00:25:22,221 --> 00:25:25,422
you normally would in any prepareForSegue.
511
00:25:25,424 --> 00:25:27,658
Okay, so the magic is just this little thing,
512
00:25:27,660 --> 00:25:30,094
indexPathForCell, don't forget about that,
513
00:25:30,096 --> 00:25:32,830
okay. That's how you figure out which row you're segueing
514
00:25:32,832 --> 00:25:36,767
from. All right, how about the UITableViewDelegate,
515
00:25:36,769 --> 00:25:38,702
not the dataSource but the delegate?
516
00:25:38,704 --> 00:25:41,805
It has a lot of stuff for how the table view works.
517
00:25:41,807 --> 00:25:43,274
I'm not gonna talk a lot about it but
518
00:25:43,276 --> 00:25:46,277
I'll talk about a couple of interesting one.
519
00:25:46,279 --> 00:25:49,146
Usually the dataSource and the delegate are the same object,
520
00:25:49,148 --> 00:25:50,147
namely your controller,
521
00:25:50,149 --> 00:25:52,983
your TableViewController. The delegate lets you
522
00:25:52,985 --> 00:25:56,353
observe what's going on inside the table, okay?
523
00:25:56,355 --> 00:25:58,255
It has a lot of will do something,
524
00:25:58,257 --> 00:26:00,257
did do something kind of methods in it.
525
00:26:00,259 --> 00:26:03,093
It has a very interesting one which is to let you know when
526
00:26:03,095 --> 00:26:06,096
the user selects a row, when a user touches on a row. Now
527
00:26:06,098 --> 00:26:08,532
normally when they touch on a row, you're just gonna segue,
528
00:26:08,534 --> 00:26:10,601
but sometimes you might wanna touch on a row in a table and
529
00:26:10,603 --> 00:26:12,603
not segue. You wanna do something else, okay?
530
00:26:12,605 --> 00:26:14,538
And so this gives you, kind of, ta,
531
00:26:14,540 --> 00:26:18,008
TableView Target/Action, okay. The way you do that,
532
00:26:18,010 --> 00:26:20,578
is you implement this method in your controller,
533
00:26:20,580 --> 00:26:25,349
didSelectRowAtIndexPath, okay. And the table view will send
534
00:26:25,351 --> 00:26:28,485
you this whenever someone touches on a row, and
535
00:26:28,487 --> 00:26:33,724
this is just telling you which row they touched on, okay,
536
00:26:33,726 --> 00:26:37,227
simple. Another interesting one is if they
537
00:26:37,229 --> 00:26:40,564
press on that little i, the little circled blue i,
538
00:26:40,566 --> 00:26:43,300
you'll get this method, accessory button tapped for
539
00:26:43,302 --> 00:26:47,104
row with index path. Okay, so you can find out that the i
540
00:26:47,106 --> 00:26:50,307
button was pressed. Again, you might be segueing instead,
541
00:26:50,309 --> 00:26:53,877
but you can do, you can get notified if you want
542
00:26:53,879 --> 00:26:56,213
alternatively. Lots of other methods,
543
00:26:56,215 --> 00:27:00,184
I'm not really have time to talk about them all. So, you,
544
00:27:00,186 --> 00:27:03,020
providing the UIView for headers and
545
00:27:03,022 --> 00:27:08,926
footers is kind of a drawing thing so they do that.
546
00:27:08,928 --> 00:27:09,760
Editing and moving the rows,
547
00:27:09,762 --> 00:27:11,395
there's not just a data source element to it,
548
00:27:11,397 --> 00:27:14,331
there's actually the visual moving the rows.
549
00:27:14,333 --> 00:27:17,434
It gets involved in that, that kind of thing, okay?
550
00:27:17,436 --> 00:27:21,372
Now what if your model changes in your table view controller,
551
00:27:21,374 --> 00:27:22,640
right, the underlying data? Well,
552
00:27:22,642 --> 00:27:25,442
you need all those sections and rows to change, right?
553
00:27:25,444 --> 00:27:27,044
You might have a different number of sections and
554
00:27:27,046 --> 00:27:28,612
a different number of rows in each section.
555
00:27:28,614 --> 00:27:30,280
So you're gonna call this method and
556
00:27:30,282 --> 00:27:31,615
table you'll call it reloadData.
557
00:27:31,617 --> 00:27:33,817
And that causes the table view to go back and
558
00:27:33,819 --> 00:27:36,820
call all those methods, numberOfSectionsInTable,
559
00:27:36,822 --> 00:27:38,822
number of rows in each section, cell or
560
00:27:38,824 --> 00:27:40,824
row at indexPath for all the visible cells,
561
00:27:40,826 --> 00:27:44,294
call them all again, okay, to get your table view reset up.
562
00:27:44,296 --> 00:27:45,629
Now that might seem kinda heavy weight, and
563
00:27:45,631 --> 00:27:48,632
for a large table, maybe it would be. So if you know how
564
00:27:48,634 --> 00:27:52,302
your model changed such that you know which index paths,
565
00:27:52,304 --> 00:27:54,738
which sections and which sections and rows,
566
00:27:54,740 --> 00:27:58,575
like maybe only one section would have been changed. Then
567
00:27:58,577 --> 00:28:03,781
you can try this more fine tuned reloadRowsAtIndexPaths,
568
00:28:03,783 --> 00:28:09,520
which is just like reloadData but only certain index paths,
569
00:28:09,522 --> 00:28:11,789
okay? How about the height of rows,
570
00:28:11,791 --> 00:28:13,991
how tall are rows? Rows are a fixed width.
571
00:28:13,993 --> 00:28:15,659
They're the entire width of of the table, but
572
00:28:15,661 --> 00:28:18,696
what about the height? Normally the height is
573
00:28:18,698 --> 00:28:21,932
just set in the inspector in your storyboard and
574
00:28:21,934 --> 00:28:24,601
it's fixed. All of the rows are exactly the same height
575
00:28:24,603 --> 00:28:27,071
rows of a certain prototype, all the rows that ma, or
576
00:28:27,073 --> 00:28:28,439
of a certain prototype the same height.
577
00:28:28,441 --> 00:28:31,975
But it is possible to let the system calculate the proper
578
00:28:31,977 --> 00:28:36,113
height, and have the rows be different heights, okay.
579
00:28:36,115 --> 00:28:37,181
So it'll use autolayout,
580
00:28:37,183 --> 00:28:40,317
right, inside the area of custom cell to figure out how
581
00:28:40,319 --> 00:28:42,920
high it should be, and then make it that high, and
582
00:28:42,922 --> 00:28:45,322
it'll do that for every row. But if you're doing that and
583
00:28:45,324 --> 00:28:48,559
you had a thousand rows, think about is really,
584
00:28:48,561 --> 00:28:50,661
the system's gonna load up a cell for
585
00:28:50,663 --> 00:28:53,697
every single one of those 10,000 rows? Do the autolayout
586
00:28:53,699 --> 00:28:56,400
on each one to figure how big the table is? No way,
587
00:28:56,402 --> 00:28:58,402
that would be horrendously inefficient. So
588
00:28:58,404 --> 00:29:01,972
instead, it asks you to tell it an estimated height and
589
00:29:01,974 --> 00:29:05,175
uses the estimated height just to estimate the total size
590
00:29:05,177 --> 00:29:08,178
of the table view. But then as one comes on screen,
591
00:29:08,180 --> 00:29:10,681
it makes it to be the actual height based on using
592
00:29:10,683 --> 00:29:14,184
the autolayout. And the way you make it do the autolayout
593
00:29:14,186 --> 00:29:17,588
is with this height UITableViewAutomaticDimension.
594
00:29:17,590 --> 00:29:19,857
That's a special height which means calculate it for
595
00:29:19,859 --> 00:29:24,762
me using autolayout. Okay, you can also control the height.
596
00:29:24,764 --> 00:29:26,497
And you're probably gonna do this, hint hint,
597
00:29:26,499 --> 00:29:28,632
you probably gonna do this in your homework assignment.
598
00:29:28,634 --> 00:29:32,302
You control that height right here with a method that'll get
599
00:29:32,304 --> 00:29:33,203
sent to you, okay.
600
00:29:33,205 --> 00:29:34,671
The table view will send you this message,
601
00:29:34,673 --> 00:29:37,708
heightForRowAtIndexPath. It'll give you the indexPath,
602
00:29:37,710 --> 00:29:40,410
and it's asking you to tell it the height, okay.
603
00:29:40,412 --> 00:29:43,113
And in here, you could calculate the height. So maybe
604
00:29:43,115 --> 00:29:45,215
you don't want autolayout to make the decision about the
605
00:29:45,217 --> 00:29:49,620
height, you wanna calculate it based on something else, okay,
606
00:29:49,622 --> 00:29:51,855
some information that you might have about what's in
607
00:29:51,857 --> 00:29:55,292
that cell, okay? So you don't wanna use autolayout to it,
608
00:29:55,294 --> 00:29:58,395
that's fine, you can calculate it here. So here's a big hint,
609
00:29:58,397 --> 00:30:00,330
you're gonna wanna do this in your homework,
610
00:30:00,332 --> 00:30:04,968
okay? All right, so there's, in the TableView itself, we're
611
00:30:04,970 --> 00:30:07,404
talking about its delegate methods, in the TableView
612
00:30:07,406 --> 00:30:09,773
itself, there are tons and tons of methods, okay?
613
00:30:09,775 --> 00:30:11,942
Can't go over them all, you're gonna wanna look to them,
614
00:30:11,944 --> 00:30:15,712
look for them. But, you know, things like scrolling around
615
00:30:15,714 --> 00:30:17,681
in there. The UITableView actually turns out to be
616
00:30:17,683 --> 00:30:21,318
a subclass of UIScrollView, so it inherits all the ability
617
00:30:21,320 --> 00:30:24,588
UIScrollView can to scroll around and stuff like that. So
618
00:30:24,590 --> 00:30:27,191
you're gonna go take a look at UITableView and understand
619
00:30:27,193 --> 00:30:29,193
what it can do. But you're controlling the TableView
620
00:30:29,195 --> 00:30:32,262
mostly by these delegates, okay. But you can also send
621
00:30:32,264 --> 00:30:35,265
messages directly to TableView to make it do things.
622
00:30:35,267 --> 00:30:38,335
All right, so that's it on the slides for TableView.
623
00:30:38,337 --> 00:30:42,339
Assignment 4 out, okay, it's basically going to be to do
624
00:30:42,341 --> 00:30:45,309
a better version of the demo I'm doing today.
625
00:30:45,311 --> 00:30:47,344
So you're gonna start with what I have today and
626
00:30:47,346 --> 00:30:50,280
you're gonna improve it, okay? It's gonna be due in a week,
627
00:30:50,282 --> 00:30:54,151
next Monday. On Wednesday, we're gonna start a two day
628
00:30:54,153 --> 00:30:58,255
lecture on Core Data, which is object oriented database.
629
00:30:58,257 --> 00:31:01,258
And next week's assignment is going to be enhance what
630
00:31:01,260 --> 00:31:04,962
you're building this week to include an object oriented
631
00:31:04,964 --> 00:31:06,096
database in there, okay.
632
00:31:06,098 --> 00:31:08,232
Obviously, we're dealing with big data sets,
633
00:31:08,234 --> 00:31:10,267
it would make sense to put it in a database so
634
00:31:10,269 --> 00:31:11,668
we can query on it, things like that.
635
00:31:11,670 --> 00:31:14,705
On Friday, if there's enough interest, which is still to be
636
00:31:14,707 --> 00:31:18,008
determined, watch the bulletin boards in the class to find
637
00:31:18,010 --> 00:31:19,710
out, we might have UICollectionView.
638
00:31:19,712 --> 00:31:22,713
Now UICollectionView is just like TableView, except for
639
00:31:22,715 --> 00:31:25,382
it's much more flexible. It's not just rows,
640
00:31:25,384 --> 00:31:28,352
it can lay things out in almost any arrangement.
641
00:31:28,354 --> 00:31:31,021
But it's still the same kind of thing, with the number of
642
00:31:31,023 --> 00:31:33,824
sections, number of rows, all that. It's just that they're
643
00:31:33,826 --> 00:31:36,126
not called rows, they're just number of items, okay,
644
00:31:36,128 --> 00:31:38,929
because they can be laid out in different ways.
645
00:31:38,931 --> 00:31:42,132
So now I'm going to do a demo, okay, it's a TableView demo.
646
00:31:42,134 --> 00:31:46,336
We're going to build a Twitter client, okay. I'm calling
647
00:31:46,338 --> 00:31:50,507
it Smash Tag because hashtags are at the middle of it, and
648
00:31:50,509 --> 00:31:55,279
so Smash Tag is kind of a fun name for it. So let's go here.
649
00:31:55,281 --> 00:31:55,946
I'm just gonna go to Xcode,
650
00:31:55,948 --> 00:31:58,415
we're gonna start from scratch, as a brand new app,
651
00:31:58,417 --> 00:32:00,651
okay. This is an iOS application as usual,
652
00:32:00,653 --> 00:32:04,388
single view as always. We'll call it Smashtag here.
653
00:32:04,390 --> 00:32:07,858
This one's gonna be iPhone only, okay, this app's gonna
654
00:32:07,860 --> 00:32:09,826
be iPhone only. You will only be required
655
00:32:09,828 --> 00:32:11,428
to do iPhone as well. You don't have to do,
656
00:32:11,430 --> 00:32:14,898
you've already kind of learned your split view doing it on
657
00:32:14,900 --> 00:32:16,867
multiple, so you don't have to do that again. So
658
00:32:16,869 --> 00:32:18,201
we'll put it in the same place we put everything,
659
00:32:18,203 --> 00:32:22,472
home directory developer, okay. Here it is. I'm gonna
660
00:32:22,474 --> 00:32:25,809
clean up a little bit by going to my storyboard right here.
661
00:32:25,811 --> 00:32:28,745
Here's my storyboard, and I'll zoom out so you can see it. It
662
00:32:28,747 --> 00:32:31,615
comes with this pre-packaged freebie view controller.
663
00:32:31,617 --> 00:32:34,751
I don't even want that, so I'm just gonna delete that
664
00:32:34,753 --> 00:32:36,420
okay and ditto the code that came with
665
00:32:36,422 --> 00:32:39,289
it too right here. I'm just gonna delete this, get that
666
00:32:39,291 --> 00:32:42,426
out of there and move it to the trash. We'll go ahead and
667
00:32:42,428 --> 00:32:45,595
put all these other things into, not our storyboard, but
668
00:32:45,597 --> 00:32:48,799
these things into this little supporting files like we
669
00:32:48,801 --> 00:32:54,338
usually do. Supporting 14 files. Okay,
670
00:32:54,340 --> 00:32:57,407
so we're left with really a pretty much a blank app here.
671
00:32:57,409 --> 00:33:00,344
Okay, storyboard's got nothing in it, and we're gonna have
672
00:33:00,346 --> 00:33:03,013
one view controller in this app to start. You're gonna add
673
00:33:03,015 --> 00:33:05,482
more view controllers in yours, but we're gonna start
674
00:33:05,484 --> 00:33:07,884
one and, of course, it's gonna be a table view controller.
675
00:33:07,886 --> 00:33:08,752
So let's drag it out, okay.
676
00:33:08,754 --> 00:33:11,755
Well go down here. Go down to table view controller,
677
00:33:11,757 --> 00:33:15,625
it's like 4th of 5th one down. I'm just gonna drag it out.
678
00:33:15,627 --> 00:33:18,195
As promised, if you do the identity inspector, you can
679
00:33:18,197 --> 00:33:21,031
see this view controller is a UITableViewController.
680
00:33:21,033 --> 00:33:25,502
And if I click inside on the view that's inside this thing,
681
00:33:25,504 --> 00:33:30,073
okay, you'll see that it's class is UITableView, okay.
682
00:33:30,075 --> 00:33:34,144
If I click here, on a row, you'll see it's class is
683
00:33:34,146 --> 00:33:37,848
a UITableViewCell. Does everyone got the parts here?
684
00:33:37,850 --> 00:33:40,751
Have the parts all put together? Well,
685
00:33:40,753 --> 00:33:43,887
we obviously need to have our own custom subclass of UI,
686
00:33:43,889 --> 00:33:47,257
TableViewController, okay, just like we do with anything.
687
00:33:47,259 --> 00:33:49,259
So let's go ahead an do that right off the bat.
688
00:33:49,261 --> 00:33:51,762
File, New File. Okay, it's gonna be a Cocoa Touch class,
689
00:33:51,764 --> 00:33:54,097
it's gonna be a subclass of UITableViewController.
690
00:33:54,099 --> 00:33:56,500
I'm gonna call it TweetTableViewController,
691
00:33:56,502 --> 00:34:00,170
because it's gonna be a table view full of tweets. Okay, so,
692
00:34:00,172 --> 00:34:01,972
it's a tweet table view controller, or
693
00:34:01,974 --> 00:34:04,674
a TweetTableViewController, whichever, you want to look at
694
00:34:04,676 --> 00:34:06,543
it there, all right. So we got that, here it is,
695
00:34:06,545 --> 00:34:09,012
we'll put it in the same place as everything else.
696
00:34:09,014 --> 00:34:10,547
Okay, here's my TweetTableViewController,
697
00:34:10,549 --> 00:34:12,582
we'll look at that in a second. Let's go back to our
698
00:34:12,584 --> 00:34:15,886
story board and make sure we set our custom class, okay.
699
00:34:15,888 --> 00:34:16,987
This is a easy thing to forget,
700
00:34:16,989 --> 00:34:19,289
I'm sure a lot of you have. Let's go in here and
701
00:34:19,291 --> 00:34:20,857
make sure it's a TweetTableViewController.
702
00:34:20,859 --> 00:34:22,426
So, this is a TweetTableViewController right
703
00:34:22,428 --> 00:34:25,495
here. Let's go look at the code that it generated for us.
704
00:34:25,497 --> 00:34:28,065
This is a little more than we get when we normally
705
00:34:28,067 --> 00:34:30,734
Make a UIViewController, okay? Because, of course,
706
00:34:30,736 --> 00:34:33,570
we're getting table view specific stuff here. So,
707
00:34:33,572 --> 00:34:34,905
here's our viewDidLoad, it has a couple
708
00:34:34,907 --> 00:34:38,875
comments in here that you can look at when you go build your
709
00:34:38,877 --> 00:34:41,411
app. Has to do with selection being cleared and
710
00:34:41,413 --> 00:34:44,815
whether there's an Edit button for editing this thing. We're
711
00:34:44,817 --> 00:34:47,050
not gonna be doing that in this demo, so get rid of that.
712
00:34:47,052 --> 00:34:48,652
And we're not gonna have to worry about memory, so
713
00:34:48,654 --> 00:34:54,191
we get rid of that. Here is our UITableViewDataSource
714
00:34:54,193 --> 00:34:58,528
right here. Here's the number of sections in the table,
715
00:34:58,530 --> 00:35:00,464
here's the numberOfRowsInSection,
716
00:35:00,466 --> 00:35:01,865
here's cellForRowAtIndexPath.
717
00:35:01,867 --> 00:35:03,233
Okay, and we know we're gonna want that, so
718
00:35:03,235 --> 00:35:05,368
I'm even gonna uncomment that. Okay,
719
00:35:05,370 --> 00:35:08,038
this is the heart of a dynamic table.
720
00:35:08,040 --> 00:35:09,940
Okay? So, we'll implement that in a little bit.
721
00:35:09,942 --> 00:35:12,709
Here's a bunch of other stuff that has to do with editing
722
00:35:12,711 --> 00:35:15,479
the table, okay, deleting rows, things like that.
723
00:35:15,481 --> 00:35:17,114
You really want to look at this stuff if,
724
00:35:17,116 --> 00:35:19,850
again you're doing extra credit or the final project,
725
00:35:19,852 --> 00:35:21,485
you want to make the rows editable,
726
00:35:21,487 --> 00:35:24,321
but we'll clear it out of here to make it, clean. And
727
00:35:24,323 --> 00:35:27,824
then here's our navigation, okay? I'll leave it in here.
728
00:35:27,826 --> 00:35:30,560
Because you're gonna be doing a navigation,
729
00:35:30,562 --> 00:35:33,530
but I'm not doing navigation in my demo, okay.
730
00:35:33,532 --> 00:35:37,000
Tense, all right, that's our tweet controller.
731
00:35:37,002 --> 00:35:42,939
So we, any time we have a new view controller, we probably
732
00:35:42,941 --> 00:35:46,710
want to think about what our model is. Right? It's a really
733
00:35:46,712 --> 00:35:48,578
good start for any time you're building a view controller,
734
00:35:48,580 --> 00:35:53,350
what's it's model? And so, this is a table that contains
735
00:35:53,352 --> 00:35:57,487
tweets, right? So, could say our model probably looks
736
00:35:57,489 --> 00:36:02,859
something like an array of Tweet or something like that.
737
00:36:02,861 --> 00:36:04,127
Okay? Where this is a class yet
738
00:36:04,129 --> 00:36:06,329
to be determined. I'm actually going to make,
739
00:36:06,331 --> 00:36:09,032
and I could even say, you know, equals an array of Tweet
740
00:36:09,034 --> 00:36:11,301
to make an empty array of them to start off.
741
00:36:11,303 --> 00:36:13,036
But I'm actually going to do a little bit trickier.
742
00:36:13,038 --> 00:36:16,873
I'm actually going to have this be and array of and
743
00:36:16,875 --> 00:36:21,511
array of Tweets. Okay, and the reason I'm gonna do this
744
00:36:21,513 --> 00:36:24,381
is because I'm gonna go fetch a bunch of tweets and
745
00:36:24,383 --> 00:36:26,750
put it in one section in my table and then later
746
00:36:26,752 --> 00:36:29,486
I'm gonna let the use of fetch more tweets, and I'll put that
747
00:36:29,488 --> 00:36:32,088
in another section, okay. And I'm gonna add the other
748
00:36:32,090 --> 00:36:35,392
section by just adding another array of tweets. So this array
749
00:36:35,394 --> 00:36:40,063
of array of tweets each array inside is just a fetch. Okay?
750
00:36:40,065 --> 00:36:42,899
So that's why I'm doing an array of an array of tweets,
751
00:36:42,901 --> 00:36:46,136
so I can just adding sections, okay? So this is really
752
00:36:46,138 --> 00:36:48,538
nice, having an array of array of something is really
753
00:36:48,540 --> 00:36:51,708
nice for table views, because this really makes it easy to
754
00:36:51,710 --> 00:36:54,477
implement all the table view methods. Because it's really
755
00:36:54,479 --> 00:36:56,546
clear, the section is the outer part of the array, and
756
00:36:56,548 --> 00:37:00,584
then the row is in the inner part. Okay, everybody cool?
757
00:37:00,586 --> 00:37:02,419
Clear why I'm doing that? Now,
758
00:37:02,421 --> 00:37:06,823
of course, if anyone sets my model, I'm going to need to
759
00:37:06,825 --> 00:37:11,428
reload my table, which I do with TableView.reloadData().
760
00:37:11,430 --> 00:37:16,399
Okay, that's gonna call all these methods down here to get
761
00:37:16,401 --> 00:37:19,002
called again. Okay. Make sense? Now,
762
00:37:19,004 --> 00:37:21,972
my model is actually gonna be a little more than that.
763
00:37:21,974 --> 00:37:25,909
I'm gonna have another part of my model which is searchText,
764
00:37:25,911 --> 00:37:28,612
which is gonna be a string, okay? I'm gonna allow people
765
00:37:28,614 --> 00:37:32,115
to set this part of my model. And I'm gonna go search for
766
00:37:32,117 --> 00:37:35,585
those tweets, tweets that match that search text and
767
00:37:35,587 --> 00:37:38,755
load up the other part of my model with it. Okay, so
768
00:37:38,757 --> 00:37:40,257
its kinda part of my model as well,
769
00:37:40,259 --> 00:37:42,492
even though they're related in that way,
770
00:37:42,494 --> 00:37:44,661
they're both part of the model. Now in this one,
771
00:37:44,663 --> 00:37:48,765
if someone set this one, okay, I'm gonna remove all
772
00:37:48,767 --> 00:37:52,836
the tweets I got, okay, using the removeAll method in array,
773
00:37:52,838 --> 00:37:55,905
just wipe out all my tweets that I have. And then I'm
774
00:37:55,907 --> 00:37:58,475
gonna call searchForTweets or something like that,
775
00:37:58,477 --> 00:38:00,877
that's gonna actually have to go do a search. By the way,
776
00:38:00,879 --> 00:38:05,181
I'm also gonna set my title to whatever that searchText is.
777
00:38:05,183 --> 00:38:07,617
So if I'm in a navigation controller or something,
778
00:38:07,619 --> 00:38:11,288
the title up there will be whatever the search text was.
779
00:38:11,290 --> 00:38:12,822
So I need to search teets,
780
00:38:12,824 --> 00:38:17,694
Tweets here, so we'll private func searchForTweets, and
781
00:38:17,696 --> 00:38:22,165
we're gonna have to implement that. So that's my model,
782
00:38:22,167 --> 00:38:25,101
all right, and I can also, here in my viewDidLoad,
783
00:38:25,103 --> 00:38:28,305
maybe I would maybe even say something like searchText
784
00:38:28,307 --> 00:38:32,208
equals, I don't know, #stanford or something. Just,
785
00:38:32,210 --> 00:38:34,077
I'll, in viewDidLoad for testing purposes,
786
00:38:34,079 --> 00:38:37,113
I'll just have a initial thing that I'm gonna search for,
787
00:38:37,115 --> 00:38:39,549
okay? And when I set this it's gonna call this.
788
00:38:39,551 --> 00:38:42,118
It's gonna remove all the tweet, it's gonna search for
789
00:38:42,120 --> 00:38:45,021
new tweets, okay? Eventually this thing's gonna find
790
00:38:45,023 --> 00:38:48,058
the tweets and call this. That's gonna reload the data,
791
00:38:48,060 --> 00:38:51,394
that's gonna call these. Okay, these are gonna load up
792
00:38:51,396 --> 00:38:56,833
the table and start making cells to, put the tweets.
793
00:38:57,169 --> 00:38:59,669
Everybody understand that flow? All right,
794
00:38:59,671 --> 00:39:02,806
let's talk about tweet, this class tweet right here.
795
00:39:02,808 --> 00:39:05,008
I just typed that. What is that, okay?
796
00:39:05,010 --> 00:39:09,679
Well I could make you go and learn how to query Twitter and
797
00:39:09,681 --> 00:39:12,082
get tweets down and all that stuff.
798
00:39:12,084 --> 00:39:14,718
But I really want you to learn about table views here, so
799
00:39:14,720 --> 00:39:17,554
I'm gonna provide a framework for you that will do that.
800
00:39:17,556 --> 00:39:20,790
Will go out and you give it a search text, it'll,
801
00:39:20,792 --> 00:39:22,125
search text of some sort,
802
00:39:22,127 --> 00:39:23,426
it'll just go do the tweets and
803
00:39:23,428 --> 00:39:26,763
give it back to you as an array of these tweet objects,
804
00:39:26,765 --> 00:39:29,032
okay? Now I'm gonna give that to you
805
00:39:29,034 --> 00:39:32,202
as a framework. Now we haven't really talked about frameworks
806
00:39:32,204 --> 00:39:34,838
too much. The only time I ever mentioned frameworks actually
807
00:39:34,840 --> 00:39:37,774
was when I talked about public and private. Remember that? I
808
00:39:37,776 --> 00:39:42,879
said there's private, there's public, and there's nothing.
809
00:39:42,881 --> 00:39:47,217
And nothing means kind of public within your app,
810
00:39:47,219 --> 00:39:51,154
right? And the word public, which we never use, that means
811
00:39:51,156 --> 00:39:54,424
public outside of my app or in the case I'm gonna do it,
812
00:39:54,426 --> 00:39:57,994
outside of my framework. So I've made a framework for you,
813
00:39:57,996 --> 00:40:01,531
and it has public methods, methods marked public, okay.
814
00:40:01,533 --> 00:40:03,166
Actually public put on the line there, and
815
00:40:03,168 --> 00:40:05,568
that means that you can call those methods,
816
00:40:05,570 --> 00:40:09,239
use those classes outside of the framework, okay?
817
00:40:09,241 --> 00:40:13,810
So to make a framework work, though, okay, you have to
818
00:40:13,812 --> 00:40:16,813
create a higher level project than your project,
819
00:40:16,815 --> 00:40:19,849
called a workspace, that has both the framework and your
820
00:40:19,851 --> 00:40:24,788
project in it, okay? So let's do that right away, let's go
821
00:40:24,790 --> 00:40:28,057
over here to Xcode, and I'm gonna say New > Workspace.
822
00:40:28,059 --> 00:40:28,625
We've never done this, okay,
823
00:40:28,627 --> 00:40:31,428
this is a new kind of project-like thing, but
824
00:40:31,430 --> 00:40:33,897
it's really just a collection of other projects. So
825
00:40:33,899 --> 00:40:37,167
I hit New > Workspace. It says what do you wanna call it and
826
00:40:37,169 --> 00:40:37,734
where do you wanna put it?
827
00:40:37,736 --> 00:40:40,270
So I'm gonna put it in my home directory Developer here.
828
00:40:40,272 --> 00:40:43,173
I'm gonna call it Lecture9. I could call it anything.
829
00:40:43,175 --> 00:40:45,575
I could even call it Smashtag if I want, but
830
00:40:45,577 --> 00:40:46,042
it can be kind of confusing.
831
00:40:46,044 --> 00:40:49,279
So I'm gonna call it Lecture9, okay, and it creates this.
832
00:40:49,281 --> 00:40:51,915
And look, it looks kinda like an empty project.
833
00:40:51,917 --> 00:40:55,151
Okay, it has no files over here, and what you
834
00:40:55,153 --> 00:40:58,855
put over here are other projects. So in a workspace,
835
00:40:58,857 --> 00:41:01,124
this is gonna have other projects. And in fact,
836
00:41:01,126 --> 00:41:05,495
I'm just gonna go grab my Smashtag, here it is right
837
00:41:05,497 --> 00:41:10,433
here. You take the xcodeproj, okay, and you drag it in.
838
00:41:10,435 --> 00:41:14,904
Boom, I've added this to my workspace. Now, I can work on
839
00:41:14,906 --> 00:41:18,775
my project here just as easily as working at it directly,
840
00:41:18,777 --> 00:41:22,779
by double-clicking it to xcodeproj, okay? And, in fact,
841
00:41:22,781 --> 00:41:25,615
better, because when I put this other framework in there,
842
00:41:25,617 --> 00:41:27,984
that framework will work here. If I don't,
843
00:41:27,986 --> 00:41:30,019
work here then that framework's not gonna be
844
00:41:30,021 --> 00:41:33,256
visible, okay. So what about this Twitter framework?
845
00:41:33,258 --> 00:41:36,326
Where's that? Well, that turns out to be right here, okay?
846
00:41:36,328 --> 00:41:37,360
Here's my little Twitter project.
847
00:41:37,362 --> 00:41:39,195
This will be available to you obviously when you're doing
848
00:41:39,197 --> 00:41:42,165
your homework. And I'm just gonna drag it's Xcode project
849
00:41:42,167 --> 00:41:46,236
in here too. Now, when I do, be very careful not to put it
850
00:41:46,238 --> 00:41:49,272
inside the Smashtag project. You see how this is trying to
851
00:41:49,274 --> 00:41:52,575
put it inside? You want it to be a sibling of sash,
852
00:41:52,577 --> 00:41:54,611
SmashTags. So put it right up at the top so
853
00:41:54,613 --> 00:41:57,280
the two of them are siblings in this workspace.
854
00:41:57,282 --> 00:42:02,452
Oops, in this workspace right here, okay? See what I'm
855
00:42:02,454 --> 00:42:05,421
saying by that? Question. >> If you make
856
00:42:05,423 --> 00:42:08,391
changes to Smashtag in your workspace,
857
00:42:08,393 --> 00:42:10,627
do they stay? >> Yeah, so the questions is
858
00:42:10,629 --> 00:42:14,264
if I change my framework or my project here in the workspace,
859
00:42:14,266 --> 00:42:16,933
do they effect the project? Absolutely, here you
860
00:42:16,935 --> 00:42:19,869
are actually working on those projects in this workspace.
861
00:42:19,871 --> 00:42:22,105
But it's those projects you are working on, okay?
862
00:42:22,107 --> 00:42:24,541
Putting in the workspace, just relates them,
863
00:42:24,543 --> 00:42:27,443
it just gets them in the same grouping. So
864
00:42:27,445 --> 00:42:29,512
here's our Twitter project right here.
865
00:42:29,514 --> 00:42:31,748
And we still have to say that this
866
00:42:31,750 --> 00:42:35,184
Smashtag uses this framework, and we do that, right,
867
00:42:35,186 --> 00:42:39,122
by clicking on our Smashtag project. We need to drag
868
00:42:39,124 --> 00:42:42,358
our Twitter framework in here to let Smashtag know,
869
00:42:42,360 --> 00:42:45,061
yeah, I want you to use Twitter. So what do we,
870
00:42:45,063 --> 00:42:47,864
where do we do that? We do it right here where it says
871
00:42:47,866 --> 00:42:50,900
products Twitter framework, okay, this right here.
872
00:42:50,902 --> 00:42:55,238
We're just gonna take this guy, and pick it up and
873
00:42:55,240 --> 00:42:58,274
drag it in here. Now before I do that, when you
874
00:42:58,276 --> 00:43:01,811
download this you might find this to be red, okay.
875
00:43:01,813 --> 00:43:04,647
That means it's not built. So to build it, you're
876
00:43:04,649 --> 00:43:07,450
gonna wanna go up to the top here, you can see now that in
877
00:43:07,452 --> 00:43:10,119
the workspace I have two choices of things I can build.
878
00:43:10,121 --> 00:43:13,823
You're gonna go to Twitter. Very importantly, you want to
879
00:43:13,825 --> 00:43:17,427
pick generic iOS device, because this is a framework.
880
00:43:17,429 --> 00:43:21,164
And you wanna build it as a generic framework, okay, for
881
00:43:21,166 --> 00:43:24,300
all iOS devices, okay. So do that and then it'll probably
882
00:43:24,302 --> 00:43:26,336
automatically build when you do that. But if not,
883
00:43:26,338 --> 00:43:29,238
you can hit Play to build and it'll build Twitter, and
884
00:43:29,240 --> 00:43:30,807
then this will stop being red, okay.
885
00:43:30,809 --> 00:43:33,076
Very important, if this is red and you drag it in,
886
00:43:33,078 --> 00:43:35,845
it's not gonna work. This has to be black, okay?
887
00:43:35,847 --> 00:43:40,249
So I'm gonna drag it in, okay, okay, to Smashtags1.
888
00:43:40,251 --> 00:43:43,920
Okay, so here I picked Smashtag, drag this down here,
889
00:43:43,922 --> 00:43:48,224
it adds it. Notice it shows in Smashtag inside Smashtag's
890
00:43:48,226 --> 00:43:51,160
project, it shows it right there as well. If you're doing
891
00:43:51,162 --> 00:43:53,796
that objective C, by the way, there's some headers right
892
00:43:53,798 --> 00:43:56,532
here because even though I wrote this in Swift, you could
893
00:43:56,534 --> 00:44:00,303
use an objective C using these headers right here, okay.
894
00:44:00,305 --> 00:44:03,006
If you're in Swift, you don't care about those headers,
895
00:44:03,008 --> 00:44:07,443
all right? But now inside of our code of our Smashtag, we
896
00:44:07,445 --> 00:44:12,548
can reference that framework by saying import Twitter,
897
00:44:12,550 --> 00:44:16,252
which is the name of this framework, okay.
898
00:44:16,254 --> 00:44:18,921
And once we import Twitter, look, this, in fact,
899
00:44:18,923 --> 00:44:21,591
if we go back here, which is where it is. And then we're
900
00:44:21,593 --> 00:44:24,694
gonna build this one for, iPhone 6. If you go back here,
901
00:44:24,696 --> 00:44:27,597
you'll see that Tweet no longer generates an error, and
902
00:44:27,599 --> 00:44:29,966
it turns purple, cuz it's recognized.
903
00:44:29,968 --> 00:44:33,202
Now this is the only Tweet in our namespace, but the full
904
00:44:33,204 --> 00:44:36,873
name of this thing is actually Twitter.Tweet. Okay, and
905
00:44:36,875 --> 00:44:39,676
you can either specify full name or if there's no other
906
00:44:39,678 --> 00:44:41,611
Tweets around, you could leave this off.
907
00:44:41,613 --> 00:44:43,746
I'll put it in there just so you're reminded, okay,
908
00:44:43,748 --> 00:44:48,017
that that's coming from that framework. Got it? Now let's
909
00:44:48,019 --> 00:44:50,620
go look at this Twitter.Tweet and see what it is. Okay, so
910
00:44:50,622 --> 00:44:53,189
I'm gonna look at my Twitter framework right here, here's
911
00:44:53,191 --> 00:44:55,925
Tweet. You can see Tweet is really just a collection of
912
00:44:55,927 --> 00:44:59,462
information about a Tweet. The text in the Tweet, the user
913
00:44:59,464 --> 00:45:03,566
who Tweeted it, when it was created, a unique identifier,
914
00:45:03,568 --> 00:45:07,203
images that are attached to it, and hashtags, URLs and
915
00:45:07,205 --> 00:45:10,873
users that are mentioned in the Tweet. And you're homework
916
00:45:10,875 --> 00:45:13,910
is gonna be about showing me these Mentions. Okay,
917
00:45:13,912 --> 00:45:16,579
I'm not gonna do that in my demo, that's for you to do.
918
00:45:16,581 --> 00:45:18,915
Okay, so that's what a Tweet is. Now we have user,
919
00:45:18,917 --> 00:45:21,951
which is you know, the name, screenName of the user.
920
00:45:21,953 --> 00:45:24,287
MediaItem, this is for attached images.
921
00:45:24,289 --> 00:45:28,124
Notice it has the aspectRatio, that'll be very valuable to
922
00:45:28,126 --> 00:45:30,760
you as you try to build your table view, okay.
923
00:45:30,762 --> 00:45:32,729
This is just an image that's attached with the Tweet.
924
00:45:32,731 --> 00:45:34,664
When people tweet, sometimes they attach an image or
925
00:45:34,666 --> 00:45:37,867
whatever. And then there's this very important class
926
00:45:37,869 --> 00:45:40,937
called Request, okay, Twitter.Request. And
927
00:45:40,939 --> 00:45:45,975
I'm gonna look at the public API of this actually. Let's go
928
00:45:45,977 --> 00:45:49,512
over here, look at generated interface, here it is.
929
00:45:49,514 --> 00:45:51,781
You can see that I've marked a lot of these things public,
930
00:45:51,783 --> 00:45:55,551
you see public, public class. Here's all the public API.
931
00:45:55,553 --> 00:45:57,854
The things you're gonna, we're gonna use is this
932
00:45:57,856 --> 00:46:01,958
public initializer which gives the search String to search on
933
00:46:01,960 --> 00:46:05,962
Twitter and then how many results you want, okay.
934
00:46:05,964 --> 00:46:08,798
And then this guy fetchTweets, and what
935
00:46:08,800 --> 00:46:12,068
fetchTweets does is it uses that search String to go fetch
936
00:46:12,070 --> 00:46:16,439
Tweets. And it calls a little function here, this function,
937
00:46:16,441 --> 00:46:19,342
okay, a function of this type. It just hands you an array of
938
00:46:19,344 --> 00:46:21,778
the Tweets when they come back. Now this is
939
00:46:21,780 --> 00:46:24,947
asynchronous, okay, obviously. We're gonna go to the network
940
00:46:24,949 --> 00:46:28,351
here. So this is coming back and the thread that this comes
941
00:46:28,353 --> 00:46:31,821
back and calls you on is not necessarily the main thread.
942
00:46:31,823 --> 00:46:34,757
So we will wanna be sure to dispatch async outta here,
943
00:46:34,759 --> 00:46:37,727
back to the main thread if we're gonna be doing any UI.
944
00:46:37,729 --> 00:46:39,829
Everyone understand that?
945
00:46:39,831 --> 00:46:42,498
Okay, so that's it. This is a really easy class to use,
946
00:46:42,500 --> 00:46:45,401
right? We just initialize it with the search we want and
947
00:46:45,403 --> 00:46:48,171
then just fetch the Tweets, okay? It's also got this
948
00:46:48,173 --> 00:46:49,572
little thing down here, which is kinda fun,
949
00:46:49,574 --> 00:46:52,675
requestFromNewer, ForNewer. If you have a request that was
950
00:46:52,677 --> 00:46:55,878
successfully got you some Tweets, you can call this
951
00:46:55,880 --> 00:46:59,682
requestForNewer and it'll give you a new request that will
952
00:46:59,684 --> 00:47:03,052
request only more new Tweets. Tweets you didn't get in
953
00:47:03,054 --> 00:47:05,788
the last request. See what I'm saying? Cuz people are alway
954
00:47:05,790 --> 00:47:07,857
Tweeting all the time, so sometimes you just want
955
00:47:07,859 --> 00:47:09,458
the ones that were Tweeted since the last time
956
00:47:09,460 --> 00:47:14,063
you got it. So we'll use this as well, okay, in our demo.
957
00:47:14,065 --> 00:47:17,633
All right, everyone understand this Twitter thing? Pretty
958
00:47:17,635 --> 00:47:21,003
simple to use, all right? >> One more thing.
959
00:47:21,005 --> 00:47:21,204
>> Yeah.
960
00:47:21,206 --> 00:47:22,138
>> So it looked like it was
961
00:47:22,140 --> 00:47:24,407
returning a void. Do you have to type cast
962
00:47:24,409 --> 00:47:28,878
that to a Tweet array- >> On the previous one?
963
00:47:28,880 --> 00:47:29,145
>> Down here?
964
00:47:29,147 --> 00:47:33,216
It returns void, but remember, this is a function that takes
965
00:47:33,218 --> 00:47:35,952
an array of Tweets as the argument. So
966
00:47:35,954 --> 00:47:37,520
you're gonna pass a closure here,
967
00:47:37,522 --> 00:47:40,056
and that closure is going to take that array and
968
00:47:40,058 --> 00:47:41,624
do something with it. Okay, it does,
969
00:47:41,626 --> 00:47:43,893
it can't return the array because it's asynchronous.
970
00:47:43,895 --> 00:47:46,295
It has to go off and on that, it has to call you back.
971
00:47:46,297 --> 00:47:49,332
Okay, with this function, when it's got the tweets, so
972
00:47:49,334 --> 00:47:52,869
that's a good question. All right, back here,
973
00:47:52,871 --> 00:47:55,738
we're in our tweets, let's get to the heart of this,
974
00:47:55,740 --> 00:47:57,406
which is this guy, searchForTweets.
975
00:47:57,408 --> 00:47:59,675
That's the thing that's gonna use the Twitter,
976
00:47:59,677 --> 00:48:03,312
framework to search for tweets. So how's this thing,
977
00:48:03,314 --> 00:48:06,649
gonna do what it does basically, to do the tweets.
978
00:48:06,651 --> 00:48:08,918
Well I'm actually going to create a little private var
979
00:48:08,920 --> 00:48:12,355
which I'm going to call my twitterRequest. It's going to
980
00:48:12,357 --> 00:48:17,159
be of type Twitter.Request. Okay, and it's going to be
981
00:48:17,161 --> 00:48:20,763
computed, and this is just going to be a little way I can
982
00:48:20,765 --> 00:48:24,166
have a little var which is the request I want to make.
983
00:48:24,168 --> 00:48:29,105
And I'm going to say if, I can let query = searchText,
984
00:48:29,107 --> 00:48:34,110
okay that's this thing right here, okay. And
985
00:48:34,112 --> 00:48:36,212
I also don't want it to be empty so
986
00:48:36,214 --> 00:48:39,282
I'm gonna say where the query is not empty.
987
00:48:39,284 --> 00:48:40,983
Okay, there's that where clause again. So
988
00:48:40,985 --> 00:48:44,287
here I'm checking to see if it's not null with if let, and
989
00:48:44,289 --> 00:48:46,689
then I'm also making sure it's not empty, so,
990
00:48:46,691 --> 00:48:49,892
because I don't wanna do a Twitter request for empty,
991
00:48:49,894 --> 00:48:52,261
that like asking for all Tweets. That makes no sense,
992
00:48:52,263 --> 00:48:55,197
there's gotta be at least something to search for. So
993
00:48:55,199 --> 00:48:57,600
if that's okay, then I'm just gonna return your request by
994
00:48:57,602 --> 00:49:01,203
saying Twitter.Request. I'm gonna call that initializer
995
00:49:01,205 --> 00:49:04,040
that we were talking about. Search, we can search for
996
00:49:04,042 --> 00:49:07,376
the query as its own but maybe I wanna add some other stuff
997
00:49:07,378 --> 00:49:11,147
like I don't want retweets. So there if
998
00:49:11,149 --> 00:49:16,118
you can in the API for doing, for looking for
999
00:49:16,120 --> 00:49:18,754
tweets you can specify things like filter out retweets I
1000
00:49:18,756 --> 00:49:21,958
don't want them, so I'm gonna do that. And then count,
1001
00:49:21,960 --> 00:49:24,627
let's get a hundred at a time. So we'll get a hundred tweets
1002
00:49:24,629 --> 00:49:30,132
at a time, okay. Otherwise I'm just going to return nil here.
1003
00:49:30,134 --> 00:49:32,835
Okay, so if, if some, if I want to do
1004
00:49:32,837 --> 00:49:35,771
a ter, twitter request, but I don't have any search text
1005
00:49:35,773 --> 00:49:36,505
then I'm just going to return nil,
1006
00:49:36,507 --> 00:49:38,574
Ill know not actually do any kind of search.
1007
00:49:38,576 --> 00:49:41,344
Okay, do you understand what that var is?
1008
00:49:41,346 --> 00:49:43,980
It's just giving me the request that I'm gonna do. So
1009
00:49:43,982 --> 00:49:47,817
let's use that request. I'm just gonna say if I can let
1010
00:49:47,819 --> 00:49:52,722
the request = TwitterRequest, so it's gonna call that
1011
00:49:52,724 --> 00:49:57,560
little computed var up there, okay. Then I'm going to use
1012
00:49:57,562 --> 00:50:02,198
that request to fetch tweets, okay. So here's fetchTweets,
1013
00:50:02,200 --> 00:50:03,866
here's that argument which is the handler.
1014
00:50:03,868 --> 00:50:06,769
By the way a really fun way when you're doing closures
1015
00:50:06,771 --> 00:50:10,806
is to do the escape completion til it shows this in blue, and
1016
00:50:10,808 --> 00:50:11,674
then double click on it.
1017
00:50:11,676 --> 00:50:15,277
You see, cuz then it'll put the closure out there for
1018
00:50:15,279 --> 00:50:17,513
you with the right argument type here.
1019
00:50:17,515 --> 00:50:22,418
Now I like closing, close, trailing, closure syntax.
1020
00:50:22,420 --> 00:50:25,221
So I'm just gonna get rid of my parentheses and put this
1021
00:50:25,223 --> 00:50:29,125
closure at the end. Okay, so that's kinda cool right?
1022
00:50:29,127 --> 00:50:32,128
Fetch tweets, it's gonna call this closure.
1023
00:50:32,130 --> 00:50:34,296
Here's the tweets that it's gonna give back,
1024
00:50:34,298 --> 00:50:38,100
an array of tweets, I'll call this newTweets, okay. And
1025
00:50:38,102 --> 00:50:40,169
then in this code I can use the new tweets,
1026
00:50:40,171 --> 00:50:43,372
add them to my table basically is really easy. But
1027
00:50:43,374 --> 00:50:46,275
of course I have to dispatch async, okay,
1028
00:50:46,277 --> 00:50:50,913
because this closure is being executed off the main queue
1029
00:50:50,915 --> 00:50:53,883
when those tweets come back sometime in the future. So
1030
00:50:53,885 --> 00:50:57,486
I've got to dispatch async. So, for my queue right here
1031
00:50:57,488 --> 00:51:00,022
I'm going to dispatch back to the main queue, so
1032
00:51:00,024 --> 00:51:04,894
that's dispatc_ge_mai_queue and here's my block.
1033
00:51:04,896 --> 00:51:06,695
Again I'm going to double click it, okay.
1034
00:51:06,697 --> 00:51:11,700
Again I'm going closing, trailing closure syntax,
1035
00:51:11,702 --> 00:51:14,270
okay, and so this is now dispatched to the main queue.
1036
00:51:14,272 --> 00:51:17,606
So all I need to do here is just say if the new tweets
1037
00:51:17,608 --> 00:51:21,010
are not empty, in others words I got some back, then I'm just
1038
00:51:21,012 --> 00:51:25,147
going to insert to my to my tweets at the beginning,
1039
00:51:25,783 --> 00:51:28,350
these new tweets at index 0.
1040
00:51:28,352 --> 00:51:31,454
Okay, cuz I want the new tweet at the top of my table so
1041
00:51:31,456 --> 00:51:34,323
I'm putting it at the beginning of my array, okay.
1042
00:51:34,325 --> 00:51:38,861
Make sense? Now I've got a red error there,
1043
00:51:38,863 --> 00:51:43,766
anyone know what that is? It's pointing to right here.
1044
00:51:43,768 --> 00:51:49,939
Any ideas? Don't be shy. >> Self.
1045
00:51:49,941 --> 00:51:50,940
>> Self, absolutely.
1046
00:51:50,942 --> 00:51:54,844
We need self here. That's Swift trying to remind us,
1047
00:51:54,846 --> 00:51:59,715
hey, this closure, this closure right here is keeping,
1048
00:51:59,717 --> 00:52:02,952
and actually this one out here too, okay, is keeping this
1049
00:52:02,954 --> 00:52:05,254
self in the heap. Now I don't think we want
1050
00:52:05,256 --> 00:52:07,756
that here cuz what if we make some Twitter request and
1051
00:52:07,758 --> 00:52:11,026
it takes forever and meanwhile the user just navigates away
1052
00:52:11,028 --> 00:52:11,060
from this thing.
1053
00:52:11,062 --> 00:52:13,729
We want this thing to be able to leave the heap. So
1054
00:52:13,731 --> 00:52:17,733
we're going to fix this using weak.
1055
00:52:17,735 --> 00:52:21,303
weak weakSelf = self.
1056
00:52:21,305 --> 00:52:24,039
So I'm just creating a new variable here called weakSelf.
1057
00:52:24,041 --> 00:52:27,543
It's weak so it don't won't hold anything in a heap and
1058
00:52:27,545 --> 00:52:28,878
I'm gonna set it equal to self.
1059
00:52:28,880 --> 00:52:31,647
So it's a weak version of self. And then down here I'm
1060
00:52:31,649 --> 00:52:34,850
gonna use weak self, okay, but this is an optional because it
1061
00:52:34,852 --> 00:52:36,886
gets set to nil if this thing leaves the heap.
1062
00:52:36,888 --> 00:52:39,788
So I have to do question mark there so that this will just
1063
00:52:39,790 --> 00:52:42,591
not happen if it leaves the heap. And that's perfect,
1064
00:52:42,593 --> 00:52:45,528
that's exactly what I want. If those tweets come back and
1065
00:52:45,530 --> 00:52:49,798
this thing has left the heap, just ignore this. Okay,
1066
00:52:49,800 --> 00:52:52,568
perfect. Now there's another problem here,
1067
00:52:52,570 --> 00:52:55,738
multi-threading problem we gotta think about, okay,
1068
00:52:55,740 --> 00:52:58,374
which is what if this takes a long time and by the time it
1069
00:52:58,376 --> 00:53:02,378
comes back the user has issued a different Tweet request?
1070
00:53:02,380 --> 00:53:04,780
They type something else in to search for, okay.
1071
00:53:04,782 --> 00:53:06,282
You don't wanna show them these tweets,
1072
00:53:06,284 --> 00:53:07,516
they were from some old thing. So
1073
00:53:07,518 --> 00:53:10,119
just like we did in the image view controller here,
1074
00:53:10,121 --> 00:53:11,287
we're gonna have to have a var,
1075
00:53:11,289 --> 00:53:13,889
we'll have some private var,
1076
00:53:14,225 --> 00:53:18,627
which is our lastTwitterRequest, okay,
1077
00:53:18,629 --> 00:53:21,797
which is gonna be a Twitter.Request,
1078
00:53:21,799 --> 00:53:24,300
okay. And, we're gonna have to keep track of
1079
00:53:24,302 --> 00:53:26,869
this, okay. When we go off and do this fetch, right, before
1080
00:53:26,871 --> 00:53:30,105
we do it I'm gonna remember what that last Twitter request
1081
00:53:30,107 --> 00:53:33,108
was, okay, which is the request we're doing. And
1082
00:53:33,110 --> 00:53:38,614
then down here I'm gonna say, if the request that we did
1083
00:53:38,616 --> 00:53:41,450
is the last one that we requested,
1084
00:53:41,452 --> 00:53:42,685
then we can do this.
1085
00:53:42,687 --> 00:53:46,622
Otherwise just ignore these tweets coming back. Okay,
1086
00:53:46,624 --> 00:53:51,860
this needs weakSelf in front of it as well. Okay, everyone
1087
00:53:51,862 --> 00:53:54,463
see that? I really want you to start understanding,
1088
00:53:54,465 --> 00:53:56,465
this is really gonna be important in your homework.
1089
00:53:56,467 --> 00:53:58,934
Okay cuz in your homework you're going to be
1090
00:53:58,936 --> 00:54:01,670
doing multi-thread stuff. And stuff's gonna come back and
1091
00:54:01,672 --> 00:54:04,340
this is a table view that's gonna be scrolling up and
1092
00:54:04,342 --> 00:54:05,741
down, reusing the cells.
1093
00:54:05,743 --> 00:54:08,644
It might come back to a cell that's no longer displaying
1094
00:54:08,646 --> 00:54:10,112
the same row as it was before cuz
1095
00:54:10,114 --> 00:54:13,415
it got reused, okay. So you really got to understand this
1096
00:54:13,417 --> 00:54:16,385
thing about understanding the world when you come back from
1097
00:54:16,387 --> 00:54:19,588
all asynchronous requests, okay. The three things,
1098
00:54:19,590 --> 00:54:23,259
the two things really to understand about asynchrony.
1099
00:54:23,261 --> 00:54:24,360
One is memory cycles,
1100
00:54:24,362 --> 00:54:27,263
make sure you break them when appropriate. And number two is
1101
00:54:27,265 --> 00:54:29,164
understanding that these things take time and
1102
00:54:29,166 --> 00:54:32,201
when they come back things might not be the same. Okay,
1103
00:54:32,203 --> 00:54:33,902
those are the two things to make sure you understand when
1104
00:54:33,904 --> 00:54:38,607
you're doing asynchronous programming. All righty then,
1105
00:54:38,609 --> 00:54:42,778
we have our, this is all we need to do to build our model,
1106
00:54:42,780 --> 00:54:43,579
build our data structure up.
1107
00:54:43,581 --> 00:54:46,682
Okay, this thing is now built up. Okay, this tweets,
1108
00:54:46,684 --> 00:54:50,452
when we add, when we insert more tweets to it, that's
1109
00:54:50,454 --> 00:54:54,089
gonna cause didSet to happen because this is a value type.
1110
00:54:54,091 --> 00:54:55,457
Okay, so didSet is gonna happen and boom,
1111
00:54:55,459 --> 00:54:58,794
reload data's gonna happen. So now we gotta make sure that
1112
00:54:58,796 --> 00:55:02,197
when reload data happens down here, that these things all do
1113
00:55:02,199 --> 00:55:05,234
what they're supposed to, right. So let's talk about
1114
00:55:05,236 --> 00:55:10,239
number of sections. That one's real easy. Tweets.count,
1115
00:55:10,241 --> 00:55:16,545
okay, cuz our tweet is an array of arrays.
1116
00:55:16,547 --> 00:55:17,346
The number of arrays in the top
1117
00:55:17,348 --> 00:55:22,685
level is how many sections we have, okay. So that's good.
1118
00:55:22,687 --> 00:55:25,988
Got that warning out of there. How about this one? This one
1119
00:55:25,990 --> 00:55:32,061
is tweets(section).count. So now we're looking at our array
1120
00:55:32,063 --> 00:55:35,564
of arrays, finding the array that is, that is this section,
1121
00:55:35,566 --> 00:55:38,867
and looking at how many rows are in it. Everybody got that?
1122
00:55:38,869 --> 00:55:42,504
This is really cool to have really simple implementations
1123
00:55:42,506 --> 00:55:46,608
of these by designing our data structure to make this simple.
1124
00:55:46,610 --> 00:55:48,877
Hint, hint, hint, I strongly recommend that for
1125
00:55:48,879 --> 00:55:52,014
your homework as well. Try to design your data structure
1126
00:55:52,016 --> 00:55:53,882
using all you've learned about Swift,
1127
00:55:53,884 --> 00:55:56,418
Swift is really awesome at being able to design
1128
00:55:56,420 --> 00:55:58,954
sophisticated flexible data structures.
1129
00:55:58,956 --> 00:56:02,191
Design a data structure, you can have one liners like this,
1130
00:56:02,193 --> 00:56:06,061
okay. Now you have a different thing going on. Your table's
1131
00:56:06,063 --> 00:56:08,330
gonna have mixed things, different kinds of things,
1132
00:56:08,332 --> 00:56:09,331
I have all the same kind of thing,
1133
00:56:09,333 --> 00:56:11,266
all mine are all tweaked. You have mixed things so its
1134
00:56:11,268 --> 00:56:14,136
a little more complicated for you, but it still can be done.
1135
00:56:14,138 --> 00:56:18,941
All right so now we have to do this self wrote index pass we
1136
00:56:18,943 --> 00:56:23,112
have to make a cell. Well the first thing we want to
1137
00:56:23,114 --> 00:56:26,115
do is this reuse identifier right here. So I'm actually
1138
00:56:26,117 --> 00:56:29,184
going to make a private struct called storyboard, just like I
1139
00:56:29,186 --> 00:56:34,957
did in the last demo. And it's gonna have a static let called
1140
00:56:35,693 --> 00:56:39,495
TweetCellIdentifier and we'll call it- we'll just call it
1141
00:56:39,497 --> 00:56:42,364
Tweet. So this is gonna be the identifier,
1142
00:56:42,366 --> 00:56:44,133
of the cells in the storyboard.
1143
00:56:44,135 --> 00:56:48,237
So I'm gonna replace this reuse identifier with
1144
00:56:48,239 --> 00:56:51,807
Storyboard.TweetCellIdentif- ier. Okay?
1145
00:56:51,809 --> 00:56:54,743
So that's specifying it. Now I need to do that same thing in
1146
00:56:54,745 --> 00:56:58,414
my storyboard. Okay, here's my row right here,
1147
00:56:58,416 --> 00:57:02,451
my prototype row. I'm gonna start off by making it be
1148
00:57:02,453 --> 00:57:04,586
a subtitle row, we'll eventually make it custom, but
1149
00:57:04,588 --> 00:57:07,523
let's make it be a subtitle role. And I gotta make sure my
1150
00:57:07,525 --> 00:57:10,893
reuse identifier is the same, so I'll make it be Tweet.
1151
00:57:10,895 --> 00:57:14,163
See the two things I did there? So that makes it so
1152
00:57:14,165 --> 00:57:18,634
this code matches up with my storyboard. All right?
1153
00:57:18,636 --> 00:57:22,371
So now, here I've dequeued this reusable cell, might have
1154
00:57:22,373 --> 00:57:25,140
reused it, might have made one from my prototype in there
1155
00:57:25,142 --> 00:57:28,043
which is a subtitle cell. So now I'm just gonna get
1156
00:57:28,045 --> 00:57:32,981
the tweet that corresponds to this index path right here.
1157
00:57:32,983 --> 00:57:35,818
Again, super simple because of our data structure,
1158
00:57:35,820 --> 00:57:44,393
tweets[indexPath.section] [indexPath.row].
1159
00:57:44,395 --> 00:57:49,431
Got it? Okay, cuz this is an array of arrays, right?
1160
00:57:49,433 --> 00:57:52,067
So now I just need to load this cell up so I'll,
1161
00:57:52,069 --> 00:57:55,604
in its text label, which is that main label of the thing,
1162
00:57:55,606 --> 00:57:59,341
I'll put the text which is the tweets text. Okay,
1163
00:57:59,343 --> 00:58:05,447
that's the text of the tweet, and then in the detail,
1164
00:58:05,449 --> 00:58:10,552
TextLabel, we'll put the tweet user's name,
1165
00:58:10,554 --> 00:58:16,592
okay? All right, so that's it. Loaded our table up,
1166
00:58:16,594 --> 00:58:21,396
should just work fine. Here we go. This is where we cross our
1167
00:58:21,398 --> 00:58:24,566
fingers in the live demo we didn't forget anything.
1168
00:58:27,771 --> 00:58:31,707
All right, here we go. It's running, and black, and
1169
00:58:31,709 --> 00:58:33,876
what do we got down here in the console?
1170
00:58:33,878 --> 00:58:36,378
Failed to instantiate the default view controller Main,
1171
00:58:36,380 --> 00:58:40,182
perhaps the designated entry point not set? Yeah,
1172
00:58:40,184 --> 00:58:43,552
let's go look at that. Main storyboard,
1173
00:58:43,554 --> 00:58:48,290
Zoom out, there's no little arrow going in here, okay.
1174
00:58:48,292 --> 00:58:51,093
I'm sure your guys have experienced that as well. So
1175
00:58:51,095 --> 00:58:54,930
let's go here and just slack this, View Controller and
1176
00:58:54,932 --> 00:58:57,499
say that it is the initial View Controller and
1177
00:58:57,501 --> 00:59:02,838
now we have this arrow. Okay? Excellent.
1178
00:59:02,840 --> 00:59:07,843
Party on. All right. So look at that, it worked perfectly.
1179
00:59:07,845 --> 00:59:11,013
Okay, look at that, we can look at all these tweets,
1180
00:59:11,015 --> 00:59:13,916
who tweeted them, okay, we're looking at Stanford here,
1181
00:59:13,918 --> 00:59:16,385
fantastic, scrolling up and down. Okay,
1182
00:59:16,387 --> 00:59:19,555
this is about the ugliest Twitter client I've ever seen.
1183
00:59:19,557 --> 00:59:20,088
>> [LAUGH]
1184
00:59:20,090 --> 00:59:22,157
>> Okay? This is,
1185
00:59:22,159 --> 00:59:24,526
if you put this in the app store you've,
1186
00:59:24,528 --> 00:59:28,130
the ridicule would be, would be unending for this.
1187
00:59:28,132 --> 00:59:31,633
Now how we gonna fix this, though? Really there's no way,
1188
00:59:31,635 --> 00:59:34,670
none of these standard cells are gonna make it look good.
1189
00:59:34,672 --> 00:59:37,306
We need to build a custom cell, obviously,
1190
00:59:37,308 --> 00:59:37,873
to make this look good.
1191
00:59:37,875 --> 00:59:40,909
So it's doing the right thing, it just looks terrible. So
1192
00:59:40,911 --> 00:59:43,779
instead of using this subtitle cell right here,
1193
00:59:43,781 --> 00:59:46,582
we're gonna do a custom cell. All right, so
1194
00:59:46,584 --> 00:59:50,018
how do we do that? First thing we need to do is, let's change
1195
00:59:50,020 --> 00:59:54,489
this from subtitle to custom. Then we're gonna be a, need to
1196
00:59:54,491 --> 00:59:59,027
go here and change the, the, the identity inspector,
1197
00:59:59,029 --> 01:00:01,863
need to change this to be some subclass of UITableViewCell
1198
01:00:01,865 --> 01:00:03,865
because obviously we're gonna have some labels and
1199
01:00:03,867 --> 01:00:07,202
stuff in here that we need to have outlets to, okay? So
1200
01:00:07,204 --> 01:00:10,772
let's go up here, new, file, okay.
1201
01:00:10,774 --> 01:00:11,740
Cocoa Touch Class,
1202
01:00:11,742 --> 01:00:13,508
this time instead of UITableViewController,
1203
01:00:13,510 --> 01:00:17,245
it's gonna be UITableViewCell subclass. And we'll call
1204
01:00:17,247 --> 01:00:20,182
this a TweetTableViewCell cuz that's what it is, shows
1205
01:00:20,184 --> 01:00:23,719
a Tweet. Okay, click that, put it in the same place we put
1206
01:00:23,721 --> 01:00:27,255
everything. [SOUND] Here we go, there's our TableViewCell,
1207
01:00:27,257 --> 01:00:28,056
we'll look at that in a second.
1208
01:00:28,058 --> 01:00:31,727
In our storyboard, we're gonna change the custom class here
1209
01:00:31,729 --> 01:00:34,630
to be TweetTableViewCell, okay. And
1210
01:00:34,632 --> 01:00:37,499
here's our TweetTableViewCell. We don't need to wait for
1211
01:00:37,501 --> 01:00:38,767
a nib, you all remember what that is and
1212
01:00:38,769 --> 01:00:41,703
we don't need it. Here's an interesting one right here.
1213
01:00:41,705 --> 01:00:42,671
This one will get called
1214
01:00:42,673 --> 01:00:45,207
in the table view cell if the table view gets selected.
1215
01:00:45,209 --> 01:00:47,576
I showed you how you can find out in the controller,
1216
01:00:47,578 --> 01:00:50,145
here you can actually find out in the table view cell. But
1217
01:00:50,147 --> 01:00:51,913
we don't need either of those things, okay?
1218
01:00:51,915 --> 01:00:54,216
We're not gonna do either of those things.
1219
01:00:54,218 --> 01:00:57,019
What we really need is just to build our UI. So let's
1220
01:00:57,021 --> 01:01:00,288
go back to our storyboard, and build the UI that we want,
1221
01:01:00,290 --> 01:01:05,560
eh in here. So what kind of stuff do we need, for
1222
01:01:05,562 --> 01:01:09,498
a tweet? Well let's see, we probably need,
1223
01:01:09,500 --> 01:01:13,268
want something which is, the Tweets Text.
1224
01:01:13,270 --> 01:01:15,671
Okay. The Tweets Text, by the way, is long and
1225
01:01:15,673 --> 01:01:18,407
I want it to wrap into multiple lines. So
1226
01:01:18,409 --> 01:01:20,676
I'm actually gonna go to the inspector up here and
1227
01:01:20,678 --> 01:01:24,980
make the number of lines in this text label be zero.
1228
01:01:24,982 --> 01:01:28,550
Zero means keep wrapping. Okay,
1229
01:01:28,552 --> 01:01:30,952
don't try to fit it all on one line with dot dot dot.
1230
01:01:30,954 --> 01:01:33,588
Remember our display would always say dot dot dot. Zero
1231
01:01:33,590 --> 01:01:36,992
means keep wrapping around so we definitely want that.
1232
01:01:37,461 --> 01:01:40,295
We also want the tweeter. So lets have one for
1233
01:01:40,297 --> 01:01:45,167
the tweeter. Okay, and that we do want to be on one line.
1234
01:01:45,169 --> 01:01:50,706
Lets show when the tweet was created. Maybe? And how about,
1235
01:01:50,708 --> 01:01:54,109
let's put an image in there. Let's put where's our UI
1236
01:01:54,111 --> 01:01:58,447
image view? Here it is. Put an u, image view, out here.
1237
01:01:58,449 --> 01:02:03,785
We'll have this be the profile image basically of the user.
1238
01:02:03,787 --> 01:02:07,656
Now, some things here we gotta do to arrange this, and I'm
1239
01:02:07,658 --> 01:02:10,826
gonna try and keep this mostly in what you already know.
1240
01:02:10,828 --> 01:02:13,528
One thing is the font, okay. These things
1241
01:02:13,530 --> 01:02:17,132
are very different from like a button in terms of fonts.
1242
01:02:17,134 --> 01:02:20,802
These are user data. This is what the user is looking at.
1243
01:02:20,804 --> 01:02:24,639
So we need to not use system font here. We need to pick
1244
01:02:24,641 --> 01:02:28,810
a different font which is one of the textiles.
1245
01:02:28,812 --> 01:02:30,412
Remember I mentioned the textiles? So
1246
01:02:30,414 --> 01:02:33,949
tweeter is kind of like a headline. It goes at the top.
1247
01:02:33,951 --> 01:02:37,519
Tweet Text is probably like the body. That's the main,
1248
01:02:37,521 --> 01:02:42,657
oops, that's the main body of the, of what's showing here.
1249
01:02:42,659 --> 01:02:44,826
Created is kind of like a caption.
1250
01:02:44,828 --> 01:02:46,561
It's just a little thing on the side. So
1251
01:02:46,563 --> 01:02:49,931
we'll say, caption one. And we might play with these various
1252
01:02:49,933 --> 01:02:54,536
things to find out what, you know, is the best font that
1253
01:02:54,538 --> 01:02:57,205
we want in each circumstance. So, let's say one thing is
1254
01:02:57,207 --> 01:02:59,941
a font. Next thing is, we need to arrange them in stack
1255
01:02:59,943 --> 01:03:02,844
views. Obviously, we love stack view, so let's do that.
1256
01:03:02,846 --> 01:03:07,182
Let's go here and embed these two in a stack view how about,
1257
01:03:07,184 --> 01:03:11,219
we'll have them both be filled there.
1258
01:03:11,221 --> 01:03:15,991
Let's put these two in a stack view,
1259
01:03:15,993 --> 01:03:20,996
thank you. Also we'll fill both directions there so
1260
01:03:20,998 --> 01:03:23,498
we got those. Let's put these in a stack view.
1261
01:03:23,500 --> 01:03:26,668
You can see a stack view, very powerful.
1262
01:03:26,670 --> 01:03:31,540
Now let's put a little spacing between there. Like that.
1263
01:03:31,542 --> 01:03:34,209
One thing that's not really very nice about this.
1264
01:03:34,211 --> 01:03:36,845
Okay, we probably want this to be fill also. One thing that's
1265
01:03:36,847 --> 01:03:39,881
not very nice about this is, look at this space right here.
1266
01:03:39,883 --> 01:03:43,218
Look how it's giving so much space to the tweeter. And not
1267
01:03:43,220 --> 01:03:46,888
pretty much space to the Tweet text, okay. Well, it turns out
1268
01:03:46,890 --> 01:03:50,992
there's a way in auto layout to control that, which is you
1269
01:03:50,994 --> 01:03:55,063
can go to something that you want to hug to it's contents,
1270
01:03:55,065 --> 01:03:58,500
right. You want to can it, be the smallest possible content.
1271
01:03:58,502 --> 01:04:01,636
You can select it, go over to the size inspector over here,
1272
01:04:01,638 --> 01:04:04,039
which is where all the constraints are, right here.
1273
01:04:04,041 --> 01:04:07,209
And change its content hugging priority. See content hugging
1274
01:04:07,211 --> 01:04:09,845
priority? I'm gonna make its content hugging priority in
1275
01:04:09,847 --> 01:04:13,582
the vertical direction to be high, higher than the other
1276
01:04:13,584 --> 01:04:15,750
one, okay? And when I do that, look what happened,
1277
01:04:15,752 --> 01:04:19,788
it hugged, okay? And now it made this one be the big one.
1278
01:04:19,790 --> 01:04:21,790
Because this one's content hugging priority is 251,
1279
01:04:21,792 --> 01:04:25,827
this one's 300. So, it's more of a priority to hug,
1280
01:04:25,829 --> 01:04:28,930
you got it? That's the only kinda special trick I'm gonna
1281
01:04:28,932 --> 01:04:31,132
show you today. I am going to do a whole
1282
01:04:31,134 --> 01:04:34,102
lecture on autolayout next week. All right, so
1283
01:04:34,104 --> 01:04:37,339
we've got this. So now, let's do our thing when we hook it
1284
01:04:37,341 --> 01:04:41,376
up to the edges. Leading edge up to top
1285
01:04:41,378 --> 01:04:46,414
down here to the bottom and over here to the trailing.
1286
01:04:46,416 --> 01:04:48,149
Let's go ahead and click on these, and
1287
01:04:48,151 --> 01:04:51,219
see if we can make standard. No? So, let's make this zero,
1288
01:04:51,221 --> 01:04:56,157
see if we can make this one a standard. No, make this one
1289
01:04:56,159 --> 01:05:02,163
also zero. See if we can make this one a standard. No, make
1290
01:05:02,165 --> 01:05:06,034
this one zero. And the top one is probably already zero.
1291
01:05:06,036 --> 01:05:10,505
Let's go ahead and look at it in our Spectre. Here, yeah,
1292
01:05:10,507 --> 01:05:12,941
the top is already zero. So, it's zero. Now, this is also
1293
01:05:12,943 --> 01:05:16,645
not looking like I want. Okay, the image view is dominating.
1294
01:05:16,647 --> 01:05:19,514
This is just the image view of the guy who tweeted it.
1295
01:05:19,516 --> 01:05:22,350
So, it just wants to be small and in the corner. So,
1296
01:05:22,352 --> 01:05:25,687
I'm actually going to fix the size of this. So,
1297
01:05:25,689 --> 01:05:29,691
if you control drag to itself, you can set a constraint,
1298
01:05:29,693 --> 01:05:33,295
a constraint itself. So it's going to react to controlled
1299
01:05:33,297 --> 01:05:36,331
to itself, so I'm going to fix its height and width.
1300
01:05:36,333 --> 01:05:38,633
So, that has fixed it's height and width,
1301
01:05:38,635 --> 01:05:40,936
of course I don't want it to be this big.
1302
01:05:40,938 --> 01:05:42,704
I want it to be like, you know, 60 by 60, let's say.
1303
01:05:42,706 --> 01:05:47,242
So, I'm going over here to the inspector, size inspector.
1304
01:05:47,244 --> 01:05:50,779
On this thing. And I'm gonna change, this constraint, right
1305
01:05:50,781 --> 01:05:53,548
here, just by clicking edit. I'm gonna change it to 60. And
1306
01:05:53,550 --> 01:05:58,954
I'm gonna change the height to also be 60. Okay. Now,
1307
01:05:58,956 --> 01:06:02,590
we have a much more reasonable layout here, right. The text
1308
01:06:02,592 --> 01:06:05,894
is using most of the space. The tweeter's just at the top.
1309
01:06:05,896 --> 01:06:08,229
We got the little image for the tweeter up there, and
1310
01:06:08,231 --> 01:06:12,434
we've got this created in here. We're good? Okay.
1311
01:06:12,436 --> 01:06:14,903
Now let's go and create outlets to all these things.
1312
01:06:14,905 --> 01:06:17,839
So, let's make more space here. Now one thing to
1313
01:06:17,841 --> 01:06:22,577
be careful of, if I go to, man to automatic here, and
1314
01:06:22,579 --> 01:06:24,913
I try to make an outlet look at the class it puts here.
1315
01:06:24,915 --> 01:06:27,816
[NOISE] The tableview controller. We know we cannot
1316
01:06:27,818 --> 01:06:30,518
hook these outlets up to the tableview controller, because
1317
01:06:30,520 --> 01:06:33,355
they have to be different for every cell. So instead, we
1318
01:06:33,357 --> 01:06:38,293
have to go to Manual up here, okay, and go not in Twitter.
1319
01:06:38,295 --> 01:06:40,495
We need to go, actually we'll, let's go here.
1320
01:06:40,497 --> 01:06:43,531
[LAUGH] How do we get to there? Let's see, Manual,
1321
01:06:43,533 --> 01:06:48,069
TweetTableView, so here it is. Okay, so let's go here, so
1322
01:06:48,071 --> 01:06:50,405
here's our, our TweetTableViewCell right here,
1323
01:06:50,407 --> 01:06:55,810
but this is not quite right either. Hold on a second here.
1324
01:06:55,812 --> 01:07:00,215
Manual. Automatic.
1325
01:07:00,217 --> 01:07:03,485
Level object. Okay.
1326
01:07:03,487 --> 01:07:05,887
It's showing me the interface here for some reason.
1327
01:07:05,889 --> 01:07:09,090
Lets do something different here. Lets go over here.
1328
01:07:09,092 --> 01:07:15,463
[SOUND] Okay, I'm not sure why it's doing that.
1329
01:07:25,275 --> 01:07:33,581
Okay, strange let's do TweetTableViewCell.
1330
01:07:41,658 --> 01:07:42,257
Very sorry about that.
1331
01:07:42,259 --> 01:07:43,792
For some reason you see it's fixed.
1332
01:07:43,794 --> 01:07:44,926
It's showing me the interface for
1333
01:07:44,928 --> 01:07:49,330
this. I'm not sure exactly why. Okay. Let's try this.
1334
01:07:49,332 --> 01:07:53,368
Let's go here, and then over here.
1335
01:07:53,370 --> 01:08:01,576
[INAUDIBLE] Okay,
1336
01:08:01,578 --> 01:08:04,679
well you know what I'm gonna do, I'm gonna quit this thing
1337
01:08:04,681 --> 01:08:09,584
and re-launch. By the way when you go back in,
1338
01:08:09,586 --> 01:08:11,820
be careful not to go back in with your Xcode project,
1339
01:08:11,822 --> 01:08:16,391
you gotta go back in with your workspace, alright? Alright,
1340
01:08:16,393 --> 01:08:20,228
now let's see. All right, we got our storyboard,
1341
01:08:20,363 --> 01:08:23,298
manual swift module, that's no good either. Mm.
1342
01:08:23,300 --> 01:08:28,570
This, all right, no.
1343
01:08:33,176 --> 01:08:37,445
Okay, this. This.
1344
01:08:39,516 --> 01:08:44,519
Okay, hm. I better back out there.
1345
01:08:53,697 --> 01:08:58,633
Well, weird. Hm,
1346
01:08:58,635 --> 01:09:03,538
I guess maybe we can do this, there we go. Okay, so
1347
01:09:03,540 --> 01:09:07,041
we are going to, I'll make this wider too,
1348
01:09:07,043 --> 01:09:10,311
so we are going to hook up our outlets, okay?
1349
01:09:10,313 --> 01:09:13,748
From here to here. So we do that with control drag,
1350
01:09:13,750 --> 01:09:17,452
just as we normally would, so here we'll control drag this.
1351
01:09:17,454 --> 01:09:23,324
I'm gonna call this one the tweetScreenNameLabel,
1352
01:09:23,326 --> 01:09:27,328
okay, because it's showing the screen name of the user.
1353
01:09:27,330 --> 01:09:34,068
Well do this one here. We'll call the tweetTextLabel.
1354
01:09:34,070 --> 01:09:38,840
We'll do here, this is the TweetCreatedLabel,
1355
01:09:38,842 --> 01:09:43,211
and we'll do this one. This is the,
1356
01:09:43,213 --> 01:09:48,183
TweetProfileImageView, okay.
1357
01:09:48,185 --> 01:09:51,319
And that's a UI image view. Okay, so
1358
01:09:51,321 --> 01:09:54,022
I've got these nice outlets right here. And,
1359
01:09:54,024 --> 01:09:57,325
let's go back to here, get our Text here all right, so
1360
01:09:57,327 --> 01:10:00,028
we got these nice outlets. So, now we need to be able to
1361
01:10:00,030 --> 01:10:02,964
load these outlets up. When this cell, okay,
1362
01:10:02,966 --> 01:10:05,967
comes on the screen, and we do self-reload index path we need
1363
01:10:05,969 --> 01:10:09,571
to be able to tell this cell here is the tweet okay, so
1364
01:10:09,573 --> 01:10:12,340
that you can load up all this information about the tweet.
1365
01:10:12,342 --> 01:10:15,476
So, I'm gonna have a public var here called tweet,
1366
01:10:15,478 --> 01:10:19,214
which is the tweet. And it's a twitter.tweet.
1367
01:10:19,216 --> 01:10:21,950
Okay, and when you set that,
1368
01:10:21,952 --> 01:10:26,955
I'm going to update my UI to set all of these things. So,
1369
01:10:26,957 --> 01:10:33,094
I'm gonna have some private func updateUI here.
1370
01:10:34,297 --> 01:10:36,598
Okay, makes sense? Now this is complaining
1371
01:10:36,600 --> 01:10:40,468
about twitter.tweet, because I have to import Twitter.
1372
01:10:40,470 --> 01:10:45,106
Don't forget, import Twitter, otherwise you're not gonna see
1373
01:10:45,108 --> 01:10:50,078
the classes from that in this swift file. Okay, so
1374
01:10:50,080 --> 01:10:53,481
we have this update UI right here. In the interest of time,
1375
01:10:53,483 --> 01:10:57,785
I'm gonna type this really fast okay, there it is.
1376
01:10:57,787 --> 01:11:01,889
okay, you can go look at this at you later offline, but
1377
01:11:01,891 --> 01:11:02,423
basically what's it's doing it
1378
01:11:02,425 --> 01:11:04,726
firstly just clearing out all the outlets,
1379
01:11:04,728 --> 01:11:07,996
okay, then it's one by one going through and
1380
01:11:07,998 --> 01:11:12,700
setting each of them based on the tweet, okay.
1381
01:11:12,702 --> 01:11:16,271
Tweet that we have. Notice by the way here,
1382
01:11:16,273 --> 01:11:20,208
kind of fun thing, in the tweet's text, I add cameras,
1383
01:11:20,210 --> 01:11:23,878
a bunch of cameras at the end if the tweet has an image
1384
01:11:23,880 --> 01:11:26,748
attached. Okay, that's what this little thing is here. I'm
1385
01:11:26,750 --> 01:11:29,517
just going looking at all the images that this tweet has.
1386
01:11:29,519 --> 01:11:31,886
That's just media array and for each one, I'm adding
1387
01:11:31,888 --> 01:11:34,789
a little camera there just to let us know there's an image.
1388
01:11:34,791 --> 01:11:37,425
Okay, tha's being added to the text of the end of
1389
01:11:37,427 --> 01:11:40,328
the text, all right? Then her's the created date in
1390
01:11:40,330 --> 01:11:43,931
the nice little format. Here's the image view, if it exists.
1391
01:11:43,933 --> 01:11:47,035
All right. UI image. Notice this blocks the main
1392
01:11:47,037 --> 01:11:50,138
thread. Fix this in your homework because one of your
1393
01:11:50,140 --> 01:11:52,173
required tasks is you cannot block the main thread. So
1394
01:11:52,175 --> 01:11:56,177
even though I'm not doing the dispatch here, you need to.
1395
01:11:56,446 --> 01:12:00,381
Got it? All right so, that's our TweetLabel.
1396
01:12:00,383 --> 01:12:04,385
So now to make this work, we just need to set this tweet
1397
01:12:04,387 --> 01:12:07,789
var in our cellForRowAtIndexPath.
1398
01:12:07,791 --> 01:12:10,024
Okay, so here's our cellForRowAtIndexPath.
1399
01:12:10,026 --> 01:12:12,694
Right now, it's doing all this business right here.
1400
01:12:12,696 --> 01:12:16,664
We don't want that. Okay, instead we're just gonna say
1401
01:12:16,666 --> 01:12:20,368
if we can let a tweetCell equal the cell as one of our
1402
01:12:20,370 --> 01:12:25,006
TweetTableViewCells, okay, which this should be, because
1403
01:12:25,008 --> 01:12:29,477
all these the tweet prototype in our storyboard over here,
1404
01:12:29,479 --> 01:12:33,681
okay, that this is this is the tweet prototype cell right
1405
01:12:33,683 --> 01:12:38,586
here if we look at it Type, it is, if I can select it here.
1406
01:12:38,588 --> 01:12:40,288
Here's where I wanna use Ctrl+Shift, okay,
1407
01:12:40,290 --> 01:12:42,690
Ctrl+Shift lets me pick which, what I want,
1408
01:12:42,692 --> 01:12:43,858
which is the TableView cell.
1409
01:12:43,860 --> 01:12:47,362
You can see that it's a custom type, right? So this should be
1410
01:12:47,364 --> 01:12:50,732
good right here. So if I can get this cell as that,
1411
01:12:50,734 --> 01:12:53,434
then I'm just gonna set the cell's tweet equal to
1412
01:12:53,436 --> 01:12:58,606
the tweet at that row and index path. Okay, all right,
1413
01:12:58,608 --> 01:13:03,111
tweetCell. Okay? Got it? So
1414
01:13:03,113 --> 01:13:08,015
that's it. Not that hard to just custom UI.
1415
01:13:08,017 --> 01:13:13,688
Let's go take a look, make sure it's working. All right,
1416
01:13:13,690 --> 01:13:17,959
here it is! So, well, this looks somewhat better,
1417
01:13:17,961 --> 01:13:22,630
I guess. It's really not much better because, one thing is,
1418
01:13:22,632 --> 01:13:23,931
look at the heights of the rows.
1419
01:13:23,933 --> 01:13:26,734
They're all the same. Even if it's a short little one like
1420
01:13:26,736 --> 01:13:29,670
this, it gets all this yucky white space, this extra white
1421
01:13:29,672 --> 01:13:33,408
space. Compared to a long one, right? So that's bad. And, and
1422
01:13:33,410 --> 01:13:37,178
big ones are not even fitting, okay? Also, where's my profile
1423
01:13:37,180 --> 01:13:40,548
image view? Got a couple problems here. So let's talk
1424
01:13:40,550 --> 01:13:43,317
about the height first. What's going on with the height?
1425
01:13:43,319 --> 01:13:46,220
And remember I said you can have the height be dynamic and
1426
01:13:46,222 --> 01:13:48,790
automatically calculated from the tweet by just
1427
01:13:48,792 --> 01:13:51,592
setting the height to automatic dimension. And
1428
01:13:51,594 --> 01:13:54,662
the place to do that is in your viewDidLoad.
1429
01:13:54,664 --> 01:13:56,431
Okay, so here's my viewDidLoad and
1430
01:13:56,433 --> 01:14:02,236
I'm just gonna set in here my tableView's row height
1431
01:14:02,238 --> 01:14:06,507
equal to this UITableView automatic dimension thing.
1432
01:14:06,509 --> 01:14:09,043
Okay, but now what about the estimated height?
1433
01:14:09,045 --> 01:14:10,678
I need to give it an estimate height so I'm actually going
1434
01:14:10,680 --> 01:14:14,882
to set the tableView's estimated row height equal to
1435
01:14:14,884 --> 01:14:19,554
the row height that came out of the storyboard. Okay?
1436
01:14:19,556 --> 01:14:22,857
So this is the row height I'm getting now. Same on every
1437
01:14:22,859 --> 01:14:25,159
one. I'm gonna make that one just be the estimated one,
1438
01:14:25,161 --> 01:14:28,463
and then I'm gonna set the row height to be this automatic.
1439
01:14:28,465 --> 01:14:29,997
And that's gonna recalculate it all the time.
1440
01:14:29,999 --> 01:14:32,300
How about the fact that I'm not getting those profile
1441
01:14:32,302 --> 01:14:34,402
image views? Well look, I have something in my console right
1442
01:14:34,404 --> 01:14:38,439
here. You recognize this? App Transport Security, okay?
1443
01:14:38,441 --> 01:14:40,842
You all remember that from last time? So let's fix that.
1444
01:14:40,844 --> 01:14:45,146
We go into our Info.plist. We're going to add a row here.
1445
01:14:45,148 --> 01:14:48,115
It's going to be the App Transport Security row right
1446
01:14:48,117 --> 01:14:53,754
there. We're going to go down here and add arbitrary loads,
1447
01:14:53,756 --> 01:14:56,591
okay. And we're going to set it to yes. So
1448
01:14:56,593 --> 01:14:59,594
we're going to allow arbitrary URLs. Okay?
1449
01:14:59,596 --> 01:15:03,998
Makes sense? Get this down out of there.
1450
01:15:04,267 --> 01:15:07,502
Okay, let's run again. Hopefully, things will look
1451
01:15:07,504 --> 01:15:13,674
really nice. Okay, that does look quite a bit better,
1452
01:15:13,676 --> 01:15:16,878
okay. It starts to look like some, I don't know if we could
1453
01:15:16,880 --> 01:15:20,481
post it on the abstract quite yet, okay but getting there,
1454
01:15:20,483 --> 01:15:23,851
okay. So I want to ask one more thing, which is I wanna
1455
01:15:23,853 --> 01:15:27,388
put a search thing at the top here, so we can search. Okay,
1456
01:15:27,390 --> 01:15:30,424
for not just always being #Stanford, as much as
1457
01:15:30,426 --> 01:15:34,095
we love #Stanford. So the way we're gonna do that is we're
1458
01:15:34,097 --> 01:15:36,130
gonna go to our storyboard. One thing is I'm gonna put
1459
01:15:36,132 --> 01:15:38,599
it inside of a navigation controller here and
1460
01:15:38,601 --> 01:15:42,169
embed this whole thing inside a navigation controller.
1461
01:15:42,171 --> 01:15:45,439
That means I can have a nice title on it, right?
1462
01:15:45,441 --> 01:15:47,241
Remember I set the title to be what I search for.
1463
01:15:47,243 --> 01:15:51,746
So I'm gonna have that there. Then I'm also going to go and
1464
01:15:51,748 --> 01:15:53,915
drag out a text field to be my search. So
1465
01:15:53,917 --> 01:15:56,284
this is gonna be the editable kind of text field. Which
1466
01:15:56,286 --> 01:15:59,820
is right here. And if you drag right up at the top. You have
1467
01:15:59,822 --> 01:16:01,989
to be a little bit careful. It has to look like this,
1468
01:16:01,991 --> 01:16:03,491
you see where it stretches out.
1469
01:16:03,493 --> 01:16:05,426
You can set the tableView's header.
1470
01:16:05,428 --> 01:16:09,363
So I've set this text field as the tableView's header. Okay?
1471
01:16:09,365 --> 01:16:11,532
And I probably wanna changed some things about it.
1472
01:16:11,534 --> 01:16:14,936
Maybe a little bit bigger font. Maybe I wanna use
1473
01:16:14,938 --> 01:16:19,173
the place holder here like, Twitter, search,
1474
01:16:19,175 --> 01:16:21,809
text, or something like that. That's a little something
1475
01:16:21,811 --> 01:16:24,612
that tells what this field is. Okay. That's just always gonna
1476
01:16:24,614 --> 01:16:27,782
be in the background til they start typing or whatever.
1477
01:16:27,784 --> 01:16:30,351
And I'm gonna need to have an outlet to it. So let's
1478
01:16:30,353 --> 01:16:33,354
do that. Okay, this time, I do want automatic, okay,
1479
01:16:33,356 --> 01:16:35,590
because this is not in a cell, this is at the top. So
1480
01:16:35,592 --> 01:16:40,895
let's Ctrl+Drag down here, let's put this, I don't know,
1481
01:16:40,897 --> 01:16:44,498
down at the bottom here. Let's Ctrl-Drag out here, and
1482
01:16:44,500 --> 01:16:49,503
call this searchTextField. All right. When,
1483
01:16:49,505 --> 01:16:55,176
searchTextField is set, we're gonna set its delegate,
1484
01:16:55,178 --> 01:16:57,812
to be ourself. We know that with text fields, we want our
1485
01:16:57,814 --> 01:17:02,350
delegate. We're also gonna set the searchTextField's text,
1486
01:17:02,352 --> 01:17:05,052
equal to our own search text. Okay.
1487
01:17:05,054 --> 01:17:09,724
Everybody understand that? Let's go back here and, so we
1488
01:17:09,726 --> 01:17:14,095
can see a little better. Okay it's complaining about self.
1489
01:17:14,097 --> 01:17:16,931
It doesn't like self because we are not
1490
01:17:16,933 --> 01:17:19,600
a UI text field delegate,
1491
01:17:19,602 --> 01:17:22,403
so we better set ourselves to be that. All right,
1492
01:17:22,405 --> 01:17:26,974
what else do we got down here? >> The didSet.
1493
01:17:27,777 --> 01:17:29,310
>> The didSet, yeah thanks,
1494
01:17:29,312 --> 01:17:34,382
didSet. There we go, okay,
1495
01:17:34,384 --> 01:17:37,785
so when the searchTextField is set we're gonna do this and
1496
01:17:37,787 --> 01:17:40,755
then of course we're gonna implement that method,
1497
01:17:40,757 --> 01:17:43,124
the textField method which is the textField,
1498
01:17:43,126 --> 01:17:46,661
the textFieldShouldReturn. Okay, and in here,
1499
01:17:46,663 --> 01:17:50,131
we're just going to have the textField.resignFirstRespon-
1500
01:17:50,133 --> 01:17:55,603
der. Okay, and then we're gonna return true.
1501
01:17:55,605 --> 01:18:00,775
And let's, also have the text, let's grab
1502
01:18:00,777 --> 01:18:06,180
our search text out of there. searchText = textField.text.
1503
01:18:06,182 --> 01:18:08,916
Okay, so when someone hits the return key, we're going to
1504
01:18:08,918 --> 01:18:12,653
grab the text out of there and hide the keyboard, okay makes
1505
01:18:12,655 --> 01:18:16,123
sense? All right, I believe that's all we need to do,
1506
01:18:16,125 --> 01:18:18,626
let's get our Stanford out of there, where's Stanford?
1507
01:18:18,628 --> 01:18:24,298
There it is. No more Stanford. Run.
1508
01:18:28,504 --> 01:18:31,172
Okay, so we've got our search text up there. So let's try,
1509
01:18:31,174 --> 01:18:33,974
let's put Stanford back in there, see if that works.
1510
01:18:33,976 --> 01:18:36,977
There it is, that's good. Let's try something else like
1511
01:18:36,979 --> 01:18:42,650
NBA. Okay, there's the NBA. Okay, looking good.
1512
01:18:43,352 --> 01:18:49,857
Go back to Stanford again, and see that again, okay?
1513
01:18:49,859 --> 01:18:51,759
There you go. All right, CS193P mentioned right there.
1514
01:18:51,761 --> 01:18:54,361
How about that? Okay, so that's it. Now,
1515
01:18:54,363 --> 01:18:57,131
I have a couple more things I wanted to show, but we ran
1516
01:18:57,133 --> 01:19:00,401
outta time, so I'm going to post them in this code.
1517
01:19:00,403 --> 01:19:02,703
I'll be posting this code, I'll add a couple of things.
1518
01:19:02,705 --> 01:19:05,106
The two things I wanna do is. One is
1519
01:19:05,108 --> 01:19:07,842
I want to be able to pull down on this tableView and
1520
01:19:07,844 --> 01:19:10,745
show more tweets. Okay, so the tableView,
1521
01:19:10,747 --> 01:19:13,681
the way to kind of update it, if you pull down on it and
1522
01:19:13,683 --> 01:19:14,715
a little spinner comes up on it,
1523
01:19:14,717 --> 01:19:17,952
and it's gonna show more, okay. So I'll put that,
1524
01:19:17,954 --> 01:19:23,390
I'll basically add that code into what I'm going to post.
1525
01:19:23,392 --> 01:19:27,995
That's all I'll show. Okay? That's it, sorry to run long.
1526
01:19:27,997 --> 01:19:29,764
See you next time. >> For
1527
01:19:29,766 --> 01:19:29,797
more, please visit us at Stanford.edu.