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. Creative Commons 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.